var $propertypublic $property;function fun(){...}public function fun(){...}
PHP5.2版本:魔术方法推举利用公有属性public, 实例化工具也无法访问魔术方法, 如果利用private私有属性, 只管程序运行正常, 但phpDesigner8软件会报错;
PHP5.4版本:改变了旧版本的规则, 魔术方法必须是公有属性, 否则将禁用对成员属性操作的方法及干系的功能(如isset(), unset(), clone等)
Warning: The magic method __set() must have public visibility and cannot be static in E:\wamp\apache\htdocs\20160524\ceshi.php on line 15
子类中重载父类的方法:
<?phpclass person{ var $name; var $sex; var $age; function __construct($name,$sex,$age){ $this->name=$name; $this->sex=$sex; $this->age=$age; } function say(){ echo "我的名字叫:".$this->name."<br>性别:".$this->sex."<br>年事:".$this->age; }}class student extends person{ private $school; function __construct($school){ //布局函数, function __construct($name,$sex,$age,$school),须要通报4个值 parent::__construct("张三","男",26); //调用父类布局函数 $this->school=$school; } function say(){ //重载了父类的say()方法 echo parent::say()."<br>学校:".$this->school; //调用了父类的方法,这样可以不毁坏父类的变量封装,如果不这样的话,父类中的变量不能为private }}$student1=new student("湖南大学"); //要与student类的布局参数数目同等$student1->say();?>
魔术方法:__get __set __isset __unset
在给不可访问属性赋值时, __set()会被调用。
读取不可访问属性的值时, __get()会被调用。
当对不可访问属性调用isset()或empty()时, __isset()会被调用。
当对不可访问属性调用unset()时, __unset()会被调用。
__get()魔术方法
<?phpclass Person { //声明一个人类Person,个中包含的三个成员属性被封装起来 //下面是声明人的成员属性,全都利用了private关键字封装 private $name; //第一个成员属性$name定义人的名子,此属性被封装 private $sex; //第二个成员属性$sex定义人的性别,此属性被封装 private $age; //第三个成员属性$age定义人的年事,此属性被封装 //声明一个布局方法,将来创建工具时,为工具的成员属性授予初值 function __construct($name="", $sex="男", $age=1) { //利用了默认参数 $this->name = $name; //利用传入的参数$name为成员属性$this->name赋初值 $this->sex = $sex; //利用传入的参数$sex为成员属性$this->sex赋初值 $this->age = $age; //利用传入的参数$age为成员属性$this->age赋初值} //在类中添加__get()方法,在直接获取属性值时自动调用一次,以属性名作为参数传入并处理 public function __get($propertyName) { //在方法前利用private润色,防止工具外部调用 if($propertyName=="sex") { //如果参数传入的是”sex”则条件成立 return "保密"; //不让别人获取到性别,以“保密”替代 } else if($propertyName=="age") { //如果参数传入的是“age”则条件成立 if($this->age > 30) //如果工具中的年事大于30时条件成立 return $this->age-10; //返回工具中虚假的年事,比真实年事小10岁 else //如果工具中的年事不大于30时则实行下面代码 return $this->$propertyName; //让访问都可以获取到工具中真实的年事 } else { //如果参数传入的是其它属性名则条件成立 return $this->$propertyName; //对其它属性都没有限定,可以直接返回属性的值 } }}$person1=new Person("张三", "男", 40); //通过Person类实例化的工具,并通过布局方法为属性赋初值echo "姓名:".$person1->name."<br>"; //直接访问私有属性name,自动调用了__get()方法可以间接获取echo "性别:".$person1->sex."<br>"; //自动调用了__get()方法,但在方法中没有返回真实属性值echo "年事:".$person1->age."<br>"; //自动调用了__get()方法,根据工具本身的情形会返回不同的值?>
__set()魔术方法
<?phpclass Person { //声明一个人类Person,个中包含的三个成员属性被封装起来 //下面是声明人的成员属性,全都利用了private关键字封装 private $name; //第一个成员属性$name定义人的名子,此属性被封装 private $sex; //第二个成员属性$sex定义人的性别,此属性被封装 private $age; //第三个成员属性$age定义人的年事,此属性被封装 //声明一个布局方法,将来创建工具时,为工具的成员属性授予初值 function __construct($name="", $sex="男", $age=1) { $this->name = $name; //利用传入的参数$name为成员属性$this->name赋初值 $this->sex = $sex; //利用传入的参数$sex为成员属性$this->sex赋初值 $this->age = $age; //利用传入的参数$age为成员属性$this->age赋初值 } //声明魔术方法须要两个参数,真接为私有属性赋值时自动调用,并可以屏蔽一些造孽赋值 public function __set($propertyName, $propertyValue) { if($propertyName=="sex"){ //如果第一个参数是属性名sex则条件成立 if(!($propertyValue == "男" || $propertyValue == "女")) //第二个参数只能是男或女 return; //如果是非法参数返回空,则结束方法实行 } if($propertyName=="age"){ //如果第一个参数是属性名age则条件成立 if($propertyValue > 150 || $propertyValue <0) //第二个参数只能在0到150之间的整数 return; //如果是非法参数返回空,则结束方法实行 } //根据参数决定为那个属性被赋值,传入不同的成员属性名,赋上传入的相应的值 $this->$propertyName = $propertyValue; //$this->$propertyName = $propertyValue; 先解析$propertyName变量, 然后再调用属性 } //下面是声明人类的成员方法,设置为公有的可以在任何地方访问 public function say(){ //在类中声明说话的方法,将所有的私有属性说出 echo "我的名子叫:".$this->name.", 性别:".$this->sex.", 我的年事是:".$this->age."。<br>"; }}$person1=new Person("张三", "男", 20);//自动调用了__set()函数,将属性名name传给第一个参数,将属性值”李四”传给第二个参数$person1->name="李四"; //赋值成功//自动调用了__set()函数,将属性名sex传给第一个参数,将属性值”女”传给第二个参数$person1->sex="女"; //赋值成功//自动调用了__set()函数,将属性名age传给第一个参数,将属性值80传给第二个参数$person1->age=80; //赋值成功$person1->sex="保密"; //“保密”是一个造孽值,这条语句给私有属性sex赋值失落败$person1->age=800; //800是一个造孽值,这条语句私有属生age赋值失落败$person1->say(); //调用$person1工具中的say()方法,查看一下所有被重新设置的新值?>
__isset()和__unset()魔术方法
<?phpclass Person { //声明一个人类Person,个中包含的三个成员属性被封装起来 //下面是声明人的成员属性,全都利用了private关键字封装 private $name; //第一个成员属性$name定义人的名子,此属性被封装 private $sex; //第二个成员属性$sex定义人的性别,此属性被封装 private $age; //第三个成员属性$age定义人的年事,此属性被封装 //声明一个布局方法,将来创建工具时,为工具的成员属性授予初值 function __construct($name="", $sex="男", $age=1) { $this->name = $name; //利用传入的参数$name为成员属性$this->name赋初值 $this->sex = $sex; //利用传入的参数$sex为成员属性$this->sex赋初值 $this->age = $age; //利用传入的参数$age为成员属性$this->age赋初值 } //当在工具表面利用isset()测定私用成员属性时,自动调用,并在内部测定扣传给外部的isset()结果 public function __isset($propertyName) { //须要一个参数,是测定的私有属性的名称 if($propertyName=="name") //如果参数中传入的属性名即是”name’时条件成立 return false; //返回假,不充许在工具外部测定这个属性; return; 直接退出函数,其结果是一样 return isset($this->$propertyName); //其它的属性都可以被测定,并返回测定的结果 } //当在工具表面利用unset()方法删除私用属性时,自动被调用,并在内部把私用的成员属性删除 public function __unset($propertyName) { //须要一个参数,是要删除的私有属性名称 if($propertyName=="name") //如果参数中传入的属性名即是”name”时条件成立 return; //退出方法,不充许删除工具中的name属性 unset($this->$propertyName); //在工具的内部删除在工具外指定的私有属性 } /为何不用return unset($this->$propertyName); 是由于unset()函数返回的是空值 void unset ( mixed $var [, mixed $... ] ) / public function say() //在类中声明说话的方法,将所有的私有属性说出 { echo "我的名子叫:".$this->name.", 性别:".$this->sex.", 我的年事是:".$this->age."。<br>"; }}$person1=new Person("张三", "男", 40); //创建一个工具$person1,将成员属性分别赋上初值var_dump(isset($person1->name)); //输出bool(false),不充许测定工具是否存在name属性var_dump(isset($person1->sex)); //输出bool(true),利用isset()测定工具中存在sex私有属性var_dump(isset($person1->age)); //输出bool(true),利用isset()测定工具中存在age私有属性var_dump(isset($person1->id)); //输出bool(false),利用isset()测定工具中不存在id属性unset($person1->name); //删除工具中的私有属性name,但在__unset()中不充许删除unset($person1->sex); //删除工具中的私有属性sex,删除成功unset($person1->age); //删除工具中的私有属性age,删除成功$person1->say(); //工具中的sex和age属性被删除,输出:我的名子叫:张三, 性别:, 我的年事是:。?>
clone 克隆工具 和布局方法一样, 是对新克隆的类初始化
$this是副本(克隆后的类)的引用 $that则是原来工具(被克隆的类)利用
把稳:最新版本(php5.2)已经取消了$that, 高洛峰已经证明;
<?phpclass Person { //声明类Person,并在个中声明了三个成员属性,一个布局方法以及一个成员方法 private $name; //第一个私有成员属性$name用于存储人的名子 private $sex; //第二个私有成员属性$sex用于存储人的性别 private $age; //第三个私有成员属性$age用于存储人的age function __construct($name="", $sex="", $age=1) { //布局方法在工具出身时为成员属性赋初值 $this->name=$name; $this->sex=$sex; $this->age=$age; } function __clone() { //声明此方法则在工具克隆时自动调用,用来为新工具重新赋值 $this->name="我是".$that->name."的副本"; //为副本工具中的name属性重新赋值 $this->age=10; //为副本工具中的age属性重新赋值 } function say() { //一个成员方法用于打印出自己工具中全部的成员属性值 echo "我的名子叫:".$this->name." 性别:".$this->sex." 我的年事是:".$this->age."<br>"; }}$p1=new Person("张三", "男", 20); //创建一个工具并通过布局方法为工具中所有成员属性赋初值$p2=clone $p1; //利用clone克隆(复制)工具,并自动调用类中的__clone()方法$p1->say(); //调用原工具中的说话方法,打印原工具中的全部属性值$p2->say(); //调用副本工具中的说话方法,打印出克隆工具的全部属性值?>
__tostring 快速获取工具的字符串表示的最便捷的公式
<?phpclass TestClass { //声明一个测试类,在类中声明一个成员属性和一个__toString()方法 private $foo; //在类中声明的一个成员方法 function __construct($foo) { //通过布局方法传值为成员属性赋初值 $this->foo = $foo; //为成员属性赋值 } public function __toString() { //在类中定义一个__toString方法 return $this->foo; //返回一个成员属性$foo的值 }}$obj = new TestClass('Hello'); //创建一个工具并赋值给工具引用$objecho $obj; //直接输出工具引用则自动调用了工具中的__toString()方法输出Hello?>
__call 在工具中调用一个不可访问方法时, __call()会被调用。
<?phpclass TestClass { //声明一个测试类,在类中声明printHello()和__call()方法 function printHello() { //声明一个方法,可以让工具成功能调用 echo "Hello<br>"; //实行时输出一条语句 } function __call($functionName, $args) { //声明此方法用来处理调用工具中不存在的方法 echo "你所调用的函数:".$functionName."(参数:"; //输出调用不存在的方法名 print_r($args); //输出调用不存在的方法时的参数列表 echo ")不存在!
<br>\n"; //输出符加的一些提示信息 } } $obj=new TestClass(); //通过类TestClass实例化一个工具 $obj->myFun("one", 2, "three"); //调用工具中不存在的方法,则自动调用了工具中的__call()方法 $obj->otherFun(8,9); //调用工具中不存在的方法,则自动调用了工具中的__call()方法 $obj->printHello(); //调用工具中存在的方法,可以成功调用?>
实例:制作框架的事理
<?php//声明一个DB类(数据库操作类)的大略操作模型class DB {//声明一个私有成员属性数组,紧张是通过下标来定义可以参加连贯操作的全部方法名称private $sql = array("field" => "","where" => "","order" => "","limit" => "","group" => "","having" => "");//连贯操作调用field() where() order() limit() group() having()方法,组合SQL语句function __call($methodName, $args) {//将第一个参数(代表不存在方法的方法名称),全部转成小写办法,获取方法名称$methodName = strtolower($methodName);//如果调用的方法名和成员属性数组$sql下标对应上,则将第二个参数给数组中下标对应的元素if(array_key_exists($methodName, $this->sql)) {$this->sql[$methodName] = $args[0];} else {echo '调用类'.get_class($this).'中的方法'.$methodName.'()不存在';}//返回自己工具,则可以连续调用本工具中的方法,形成连贯操作return $this;}//大略的运用,没有实际意义,只是输出连贯操作后组合的一个SQL语句,是连贯操作末了调用的一个方法function select() {echo "SELECT {$this->sql['field']} FROM user {$this->sql['where']} {$this->sql['order']}{$this->sql['limit']} {$this->sql['group']} {$this->sql['having']}";}}$db = new DB();//连贯操作,也可以分为多行去连续调用多个方法$db -> field('sex, count(sex)')-> where('where sex in ("男", "女")')-> group('group by sex')-> having('having avg(age) > 25')-> select();//如果调用的方法不存在,也会有提示,下面演示的便是调用一个不存的方法query()$db -> query('select from user');
__callStatic() 用静态办法中调用一个不可访问方法时, __callStatic()会被调用。PHP 5.3.0之后版本支持
<?phpclass TestClass{public $p1 = 1;//当B的工具调用一个不存在的静态方法的时候,就会自动调用本方法//参数:$funcName:要调用的不存在的方法名;//参数:$argList:是一个数组,个中存放了调用该不存在的方法时的实参static function __callStatic($funcName, $argList){echo "<br />正在调用类的一个不存在的静态方法" . $funcName;echo "<br />其利用的实参数据为:";print_r($argList);}}TestClass::f1(1,2); //f1方法不存在,就会调用该类中的静态方法__callstaticTestClass::f2(); //f2方法不存在,就会调用该类中的静态方法__callstatic$obj = new TestClass();//$obj->f3(3, 4, 5); //虽然静态的方法也可以用工具访问, 但是对付利用__callStatic魔术方法不管用?>
__autoload() 自动加载类 //类名和类的文件名必须同等
自动加载机制的实行事理
答:当我们实例化一个工具时, 系统会探求与之对应的类文件, 首先在当前文件中去探求, 如果未找到, 则到autoload函数库中去探求,
如果还未找到, 则报错。反之, 则直策应用。
<?phpfunction __autoload($className) { //在方件的上方声明一个自动加载类的方法include("class_" . ucfirst($className) . ".php"); //在方法中利用include包含类所在的文件}$obj = new User(); //User类不存在则自动调用__autoload()函数,将类名“User”做为参数传入$obj2 = new Shop(); //Shop类不存在则自动调用__autoload()函数,将类名“Shop”做为参数传入?>
自定义的自动加载函数:
系统的自动加载函数只有一个__autoload(), 有时候略显不便, 我们有时候还须要分别定义不同的函数, 去作为自动加载函数,
以应对不同的环境, 此时就可以利用自定义加载函数。
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
autoload_function 欲注册的自动装载函数。如果没有供应任何参数, 则自动注册 autoload 的默认实现函数spl_autoload()。
throw 此参数设置了 autoload_function 无法成功注册时, spl_autoload_register()是否抛出非常。
prepend 如果是 true, spl_autoload_register() 会添加函数到行列步队之首, 而不是行列步队尾部。
<?php//1,先声明要作为自定义加载函数的函数名,可以多个:spl_autoload_register('auto1');spl_autoload_register('auto2');//2,分别定义这几个函数,个中写逻辑去加载须要的类文件。function auto1($className){echo "<h1>$className</h1>";$file = './class/' . $className . '.class.php';if(file_exists($file)){ //file_exists()判断文件是否存在require_once $file;}}function auto2($className){echo "<h4>$className</h4>";$file = './library/' . $className . '.class.php';if(file_exists($file)){ //file_exists()判断文件是否存在require_once $file;}}//3,利用类,须要的时候就会自动依次调用这几个函数去完成加载事情。$obj1 = new A();$obj2 = new B();var_dump($obj1,$obj2);?>
序列化和反序列化
序列化:便是将一个变量的数据的"内存存在形态", 转换为"硬盘存在形态"的过程。
php中, 将一个变量数据序列化, 是利用一个固定的系统函数来完成的:serialize()
把稳:序列化只能存储工具的属性值, 不能存储其方法, 因此反序列化之前要加载类;
反序列化:将之前序列化之后存储到硬盘的数据(字符串),反向规复本钱来的变量数据(内存形态)。
做法:通过这个unserialize()函数进行反序列化;
string serialize ( mixed $value )
serialize() 返回字符串, 此字符串包含了表示value的字节流, 可以存储于任何地方。
这有利于存储或通报PHP的值, 同时不丢失其类型和构造。serialize()可处理除了resource之外的任何类型。
想要将已序列化的字符串变回PHP的值, 可利用 unserialize()
mixed unserialize ( string $str )
unserialize() 对单一的已序列化的变量进行操作, 将其转换回 PHP 的值。
1 建立单独的文件 class_person.php 代码如下所示:
<?phpclass Person { //声明一个Person类,包含三个成员属性和一个成员方法private $name; //人的名子private $sex; //人的性别private $age; //人的年事function __construct($name="", $sex="", $age="") { //布局方法为成员属性赋初值$this->name=$name;$this->sex=$sex;$this->age=$age;}function say() { //这个人可以说话的方法, 说出自己的成员属性echo "我的名子叫:".$this->name." 性别:".$this->sex." 我的年事是:".$this->age."<br>";}}?>
2 将工具序列化, 得到的字符串保存在文件中
<?phprequire "class_Person.php"; //在本文件中包含Person类所在的脚本文件$person=new Person("张三", "男", 20); //能过Person类创建一个工具,工具的引用名为$person$person_string=serialize($person); //通过serialize函数将工具串行化,返一个字符串file_put_contents("file.txt", $person_string); //将工具串行化后返回的字符串保存到file.txt文件中?>
为何工具在写入文件时须要序列化?
答: 由于工具是复合数据类型, 而file_put_contents()第二个参数必须是字符串型
3 反序列化
<?phprequire "class_Person.php"; //在本文件中包含Person类所在的脚本文件$person_string=file_get_contents("file.txt"); //将file.txt文件中的字符串读出来并赋给变量$person_string$person=unserialize($person_string); //进行反串行化操作,形成工具$person。$person->say(); //调用工具中的say()方法,用来测试反串行化工具是否成功?>
提醒:序列化和反序列化函数也适用于数组, 将数组进行序列化后存入到数据库, 从数据库中读取后利用反序列化后再读取原数组;
__sleep 和 __wakeup魔术方法利用
当把一个工具, 进行序列化操作(serialize())的时候, 就会自动先去调用该类中的魔术方法__sleep()。
当对一个工具进行"反序列化"操作的时候, 会先调用该魔术方法__wakeup()。
反序列化时不会自动调用布局方法, 魔术方法__wakeup()就能起到浸染(比如连接数据库、选择数据库、设置字符集);
<?phpclass Person { //声明一个Person类,包含三个成员属性和一个成员方法private $name; //人的名子private $sex; //人的性别private $age; //人的年事function __construct($name="", $sex="", $age="") { //布局方法为成员属性赋初值$this->name=$name;$this->sex=$sex;$this->age=$age;}function say() { //这个人可以说话的方法, 说出自己的成员属性echo "我的名子叫:".$this->name." 性别:".$this->sex." 我的年事是:".$this->age."<br>";}function __sleep() { //在类中添加此方法,在串行化时自动调用并返回数组$arr=array("name", "age"); //数组中的成员$name和$age将被串行化,成员$sex则被忽略return($arr); //返回一个数组}function __wakeup() { //在反串行化工具时自动调用该方法,没有参数也没有返回值$this->age = 40; //在重新组织工具时,为新工具中的$age属性重新赋值}}$person1=new Person("张三", "男", 20); //通过Person类实例化工具,工具引用名为$person1//把一个工具串行化,返一个字符串,调用了__sleep()方法,忽略没在数组中的属性$sex$person_string=serialize($person1);echo $person_string."<br>"; //输出工具串行化的字符串//反串行化工具,并自动调用了__wakup()方法重新为新工具中的$age属性赋值$person2=unserialize($person_string); //反串行化工具形成工具$p2重新赋值$age为40$person2->say(); //调用新工具中say()方法输出的成员中已没有$sex属性了?>
__invoke(): 将工具当作函数来利用的时候,会自动调用该方法。 invoke: 调用
<?phpclass B{public $p1 = 1;function __invoke(){echo "工具当函数利用, 本方法被调用";}}$b1 = new B();$v1 = $b1(); //此时工具当函数利用?>__set_state(), 调用var_export()导出类时, 此静态方法会被调用。class Test{public $a;public function func(){echo '我只是一个方法';}}$test = new Test();var_export($test);
会输出:Test::__set_state(array( 'a' => NULL, ))
把稳a是NULL, 没有赋值