目录
13.魔术方法
定义:PHP把所有以__(两个下划线)开头的类方法当成魔术方法
__construct, __destruct (参看 构造方法和析构方法),
__call, __callStatic, __get, __set, __isset, __unset (参看 重载),
__sleep, __wakeup, __toString, __set_state 和 __clone 等方法在PHP中被称为“魔术方法”(Magic methods)。
你在命名自己的类方法时不能使用这些方法名。
serialize()
作用: 第一. 在序列化之前,关闭对象可能具有的任何数据库连接等.
第二. 指定对象中需要被序列化的成员属性,如果某个属性比较大而不需要储存下来,可以不把它写进__sleep要返回的数组中,这样该属性就不会被序列化
在用serialize序列化对象时,会自动调用__sleep方法,__sleep方法必须返回一个数组,包含需要串行化的属性。
PHP会抛弃其它属性的值, 如果没有__sleep方法,PHP将保存所有属性,包括private属性。
unserialize() 从字节流中创建了一个对象之后,马上检查是否具有__wakeup 的函数的存在。
如果存在,__wakeup 立刻被调用。使用 __wakeup 的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务。
下面给出一个序列化的代码:共serialize.php和unserialize.php两个文件。
<?php class User { public $name; public $id; function __construct() { $this->id = uniqid(); //give user a unique ID 赋予一个不同的ID } function __sleep() { return(array("name")); //do not serialize this->id 不串行化id } function __wakeup() { $this->id = uniqid(); //give user a unique ID } } $u = new User; $u->name = "HAHA"; $s = serialize($u); //serialize it 串行化 注意不串行化id属性,id的值被抛弃 $u2 = unserialize($s); //unserialize it 反串行化 id被重新赋值 //$u and $u2 have different IDs $u和$u2有不同的ID var_dump($u); var_dump($u2); ?>
---------- PHP debug ----------
object(User)#1 (2) {
["name"]=>
string(4) "HAHA"
["id"]=>
string(13) "47fa045529f69"
}
object(User)#2 (2) {
["name"]=>
string(4) "HAHA"
["id"]=>
string(13) "47fa04552a49a"
}
---序列化--------反序列化--------------------
class ClassA { var $int; var $str; var $bool; var $obj; var $pr; } $a = new ClassA(); $a->int = 1; $a->str = "Hello"; $a->bool = false; $a->obj = $a; $a->pr = &$a->str; echo serialize($a); //O:6:"ClassA":5:{s:3:"int";i:1;s:3:"str";s:5:"Hello";s:4:"bool";b:0;s:3:"obj";r:1;s:2:"pr";R:3;}
在这个例子中,首先序列化的对象是 ClassA 的一个对象,那么给它编号为 1,接下来要序列化的是这个对象的几个成员,第一个被序列化的成员是 int 字段,那它的编号就为 2,接下来被序列化的成员是 str,那它的编号就是 3,依此类推,到了 obj 成员时,它发现该成员已经被序列化了,并且编号为 1,因此它被序列化时,就被序列化成了 r:1; ,在接下来被序列化的是 pr 成员,它发现该成员实际上是指向 str 成员的一个引用,而 str 成员的编号为 3,因此,pr 就被序列化为 R:3; 了。
===============
//下面举个简单的例子,来说明 Serializable 接口的使用:序列化--------反序列化-
class MyClass implements Serializable { public $member; function MyClass() { $this->member = ‘member value‘; } public function serialize() { return wddx_serialize_value($this->member); } public function unserialize($data) { $this->member = wddx_deserialize($data); } } $a = new MyClass(); echo serialize($a); echo "\n"; print_r(unserialize(serialize($a)));
/*
输出结果为(浏览器中的源代码):
C:7:"MyClass":90:{<wddxPacket
version=‘1.0‘><header/><data><string>member
value</string></data></wddxPacket>}
MyClass Object
(
[member] => member value
)
因此如果想用其它语言来实现 PHP 序列化中的 C 标示的话,也需要提供一种这样的机制,让用户自定义类时,
能够自己在反序列化时处理 <data> 内容,否则,这些内容就无法被反序列化了。
*/
、、、、
__sleep 和 __wakeup
serialize() 函数会检查是否存在一个魔术方法 __sleep.如果存在,__sleep()方法会先被调用,
然后才执行序列化操作。这个功能可以用于清理对象,并返回一个包含对象中所有变量名称的数组。如果该方法不返回任何内容,则NULL被序列化,导致
一个E_NOTICE错误。
__sleep方法常用于提交未提交的数据,或类似的操作。同时,如果你有一些很大的对象,不需要保存,这个功能就很好用。
与之相反,unserialize()会检查是否存在一个__wakeup方法。如果存在,则会先调用 __wakeup方法,预先准备对象数据。
__wakeup经常用在反序列化操作中,否则是字符串;例如重新建立数据库连接,或执行其它初始化操作
------------------------------------------------------------------------------------------------
14. Final关键字
如果父类中的方法被声明为final,则子类无法覆盖该方法; 如果一个类被声明为final,则不能被继承。
语法:
类使用 final 关键字的例子:
final class Person { ...... } class BaseClass { public function test() { echo "BaseClass::test() called\n"; } final public function moreTesting() { echo "BaseClass::moreTesting() called\n"; } } class ChildClass extends BaseClass { public function moreTesting() { echo "ChildClass::moreTesting() called\n"; } }
---------------------------------------------
15.对象复制
对象复制可以通过clone关键字来完成(如果对象中存在__clone()方法,会先被调用)。对象中的 __clone()方法不能直接调用。
$copy_of_object = clone $object;
clone 关键字用于克隆一个完全一样的对象,
__clone()
__clone() 方法来重写原本的属性和方法。是深复制
如果想在克隆后改变原对象的内容,需要在类中添加一个特殊的 __clone() 方法来重写原本的属性和方法。
__clone() 方法只会在对象被克隆的时候自动调用。
clone 关键字用于克隆一个完全一样的对象,__clone() 方法来重写原本的属性和方法。
对象克隆
有的时候我们需要在一个项目里面使用两个或多个一样的对象,如果使用 new 关键字重新创建对象,再赋值上相同的属性,这样做比较烦琐而且也容易出错。
PHP 提供了对象克隆功能,可以根据一个对象完全克隆出一个一模一样的对象,而且克隆以后,两个对象互不干扰。
使用关键字 clone 来克隆对象。语法: $object2 = clone $object;
例子1:
2,
__clone()
如果想在克隆后改变原对象的内容,需要在类中添加一个特殊的 __clone() 方法来重写原本的属性和方法。
__clone() 方法只会在对象被克隆的时候自动调用。
//例子1:
/* class Person { private $name; private $age; public function __construct($name, $age) { $this->name=$name; $this->age=$age; } public function say() { echo "my name is :".$this->name."<br />"; echo "my age is :".$this->age; } } $p1 = new Person("haha", 20); $p2 = clone $p1; $p2->say(); */ class Person { private $name; private $age; function __construct($name, $age) { $this->name = $name; $this->age = $age; } function say() { echo "my name is :".$this->name."<br/>"; echo " my age is :".$this->age."<br />"; } function __clone() { $this->name = "my is error ".$this->name; $this->age = 30; } } $p1 = new Person("haha", 20); $p1->say(); $p2 = clone $p1; $p2->say();
------------------------------------------------------------------
16.对象比较
(==) 如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。
(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。
实例1
class A{ $a=new A; $b=new A; if($a==$b){ echo "true"; }else{ echo "false"; } $c=new A; $d=&$c; if($c===$d){ echo "true"; }else{ echo "false"; } } 实例2 function bool2str($bool) { if ($bool === false) { return ‘FALSE‘; } else { return ‘TRUE‘; } } function compareObjects(&$o1, &$o2) { echo ‘o1 == o2 : ‘ . bool2str($o1 == $o2) . "\n"; echo ‘o1 != o2 : ‘ . bool2str($o1 != $o2) . "\n"; echo ‘o1 === o2 : ‘ . bool2str($o1 === $o2) . "\n"; echo ‘o1 !== o2 : ‘ . bool2str($o1 !== $o2) . "\n"; } class Flag { public $flag; function Flag($flag = true) { $this->flag = $flag; } } class OtherFlag { public $flag; function OtherFlag($flag = true) { $this->flag = $flag; } } $o = new Flag(); $p = new Flag(); $q = $o; $r = new OtherFlag(); echo "Two instances of the same class\n"; compareObjects($o, $p); echo "\nTwo references to the same instance\n"; compareObjects($o, $q); echo "\nInstances of two different classes\n"; compareObjects($o, $r);
-----------------------------------------
17.对象和引用
引用:php的引用是别名,就是两个不同的变量名字指向相同的内容
class A { public $foo = 1; } $a = new A; $b = $a; // $a ,$b都是同一个标识符的拷贝 // ($a) = ($b) = <id> $b->foo = 2; echo $a->foo."\n"; $c = new A; $d = &$c; // $c ,$d是引用 // ($c,$d) = <id> $d->foo = 2; echo $c->foo."\n"; $e = new A; function foo($obj) { // ($obj) = ($e) = <id> $obj->foo = 2; } foo($e); echo $e->foo."\n";
-------------------------------------------------------------------------------
对象序列化
序列化对象 - 在会话中存放对象
所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。
unserialize()函数能够重新把字符串变回php原来的值。
序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
为了能够unserialize()一个对象,这个对象的类必须已经定义过。
如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。
如果要想在另外一个文件中解序列化一个对象,这个对象的类必须在解序列化之前定义,可以通过包含一个定义该类的文件或使用函数spl_autoload_register()来实现
实例1:
class A { public $one = 1; public function show_one() { echo $this->one; } } // page1.php: include("classa.inc"); $a = new A; $s = serialize($a);
// 把变量$s保存起来以便文件page2.php能够读到
file_put_contents(‘store‘, $s);
// page2.php:
// 要正确了解序列化,必须包含下面一个文件
include("classa.inc");
$s = file_get_contents(‘store‘);
$a = unserialize($s);
// 现在可以使用对象$a里面的函数 show_one()
$a->show_one();
------------------------------------
后期静态绑定)
后期绑定“的意思是说,static::不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为”静态绑定“,因为它可以用于(但不限于)静态方法的调用
后期静态绑定的功能:用于在继承范围内引用静态调用的类。
self:: 的限制
使用self:: 或者 __CLASS__对当前类的静态引用,取决于定义当前方法所在的类:
实例1:
<?php class A { public static function who() { echo __CLASS__; } public static function test() { self::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test(); ?>
------------------------------------------------------------------------------------------
18.$this关键字
$this 的含义是表示 实例化后的 具体对象!
this的用法:
1,this是指向对象实例的一个指针,self是对类本身的一个引用,parent是对父类的引用。
1,类的内部使用:如果从类的内部访问不为常量const或者static变量或者方法,那么就必须使用自引用的$this
2,在构造函数中 指该构造函数创建新对象
3,引用$this,代表当前的类,解决变量命名冲突和不确定性问题\
--------------------------------
class Test{ function __call($name,$args){ if($name==‘add‘ && count($args)==2){ $type=‘num‘; } foreach ($args as $key=>$val){ if(!is_int($val) || is_float($val)){ $type=‘string‘; } } $method=$name.ucfirst($type); if(method_exists($this,$method)){ call_user_func_array(array($this,$method),$args); } } function addNum(){ echo $i+$j; } function addString(){ echo $i.$j; } } $test = new Test(); $test->add(3,5); $test->add(4,‘4‘);
/*
* 常量 const
在类里面定义常量用 const 关键字,而不是通常的 define() 函数。
语法: const constant = "value";
例子:
运行该例子输出:
中国
我是中国人
*
*/
Class Person{ // 定义常量 const COUNTRY = "china"; public function myCountry() { //内部访问常量 echo "my is ".self::COUNTRY." person<br />"; } } // 输出常量 echo Person::COUNTRY."<br />"; // 访问方法 $p1 = new Person(); $p1 -> myCountry(); Person::myCountry();
--------------------
/*
* PHP 对象的存储与传输(序列化 serialize 对象)
对象的存储与传输
在实际项目应用中,有些任务在一两个页面是无法完成的,由于变量到脚本执行完毕就释放,我们本页所生成的对象想在其它页面使用时便碰到了麻烦。
如果需要将对象及其方法传递到我们想使用对象的页面,比较简单可行的办法是将对象序列化后存储起来或直接传输给需要的页面,另一种办法是将对象注册为 session 变量。
序列化对象
对象序列化,就是将对象转换成可以存储的字节流。当我们需要把一个对象在网络中传输时或者要把对象写入文件或是数据库时,就需要将对象进行序列化。
序列化完整过程包括两个步骤:一个是序列化,就是把对象转化为二进制的字符串,serialize()
函数用于序列化一个对象;另一个是反序列化,就是把对象被序列转化的二进制字符串再转化为对象,unserialize()
函数来反序列化一个被序列化的对象。这样整个过程下来,对象内的类型结构及数据都是完整的。
语法:
string serialize( mixed value )
mixed unserialize( string str [, string callback] )
*
*/
class Person { private $name; private $age; function __construct($name, $age) { $this->name = $name; $this->age = $age; } function say() { echo "my name is ".$this->name."<br />"; echo " my age is ".$this->age; } } $p1 = new Person("haha", 20); $p1_string = serialize($p1); //将对象序列化后写入文件 $fh = fopen("p1.text", "w"); fwrite($fh, $p1_string); fclose($fh);
--------------
<?php
/*
* PHP面向对象之this 关键字
1,PHP5中为解决变量的命名冲突和不确定性问题,引入关键字“$this”代表其所在当前对象。
2,$this在构造函数中指该构造函数所创建的新对象。
3,在类中使用当前对象的属性和方法,必须使用$this->取值。方法内的局部变量,不属于对象,不使用$this关键字取值。
局部变量和全局变量与 $this 关键字
4,使用当前对象的属性必须使用$this关键字。
局部变量的只在当前对象的方法内有效,所以直接使用。
注意:局部变量和属性可以同名,但用法不一样。在使用中,要尽量避免这样使用,以免混淆。
1234567891011121314 <!-- 验证属性和局部变量使用方法的类 -->
<?php class A{ private $a = 99; //这里写一个打印参数的方法. public function printInt($a){ echo "这里的 \$a 是传递的参数 $a "; echo "<br>"; echo "这里的 \$this->a 是属性 $this->a"; } } $a = new A(); // 这里的$a 可不是类中的任何一个变量了. $a->printInt(88); ?>
运行结果:
12 这里的 $a 是传递的参数 88 这里的 $this->a 是属性 99
用$this调用对象中的其它方法
1234567891011121314151617 <!--写一个类,让他自动完成最大值的换算.-->
<?php class Math{ //两个数值比较大小. public function Max($a,$b){ return $a>$b?$a:$b; } //三个数值比较大小. public function Max3($a,$b,$c){ //调用类中的其它方法. $a = $this->Max($a,$b); return $this->Max($a,$c); } } $math = new Math(); echo "最大值是 ".$math->Max3(99,100,88); ?>
运行结果:
1 最大值是 100
使用$this调用构造函数
调用构造函数和析构函数的方法一致。
12345678910111213141516
<? class A{ private $a = 0; public function __construct(){ $this->a = $this->a + 1 ; } public function doSomeThing(){ $this->__construct(); return $this->a; } } $a = new A(); // 这里的$a 可不是类中的任何一个变量了. echo "现在 \$a 的值是" . $a->doSomeThing(); ?>
运行结果:
1 现在 $a 的值是2
$this 到底指的什么?
$this 就是指当前对象,我们甚至可以返回这个对象使用 $this
12345678910111213
<?php class A{ public function getASelf(){ return $this; } public function __toString(){ return "这是类A的实例."; } } $a = new A(); // 创建A的实例; $b = $a->getASelf(); //调用方法返回当前实例. echo $a; //打印对象会调用它的__toString方法. ?>
程序运行结果:
1 这是类A的实例.
通过 $this 传递对象
在这个例子中,我们写一个根据不同的年龄发不同工资的类.
我们设置处理年龄和工资的业务模型为一个独立的类.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
<?php class User{ private $age ; private $sal ; private $payoff ; //声明全局属性. //构造函数,中创建Payoff的对象. public function __construct(){ $this->payoff = new Payoff(); } public function getAge(){ return $this->age; } public function setAge($age){ $this->age = $age; } // 获得工资. public function getSal(){ $this->sal = $this->payoff->figure($this); return $this->sal; } } //这是对应工资与年龄关系的类. class Payoff{ public function figure($a){ $sal =0; $age = $a->getAge(); if($age >80 || $age <16 ){ $sal = 0; }elseif ($age > 50){ $sal = 1000; }else{ $sal = 800; } return $sal; } } //实例化User $user = new User(); $user->setAge(55); echo $user->getAge()."age ,his sal is " . $user->getSal(); echo "<br>"; $user->setAge(20); echo $user->getAge()."age , his sal is " . $user->getSal(); echo "<br>"; $user->setAge(-20); echo $user->getAge()."age , his sal is " . $user->getSal(); echo "<br>"; $user->setAge(150); echo $user->getAge()."age , his sal is " . $user->getSal(); ?>
运行结果:
1234 55age ,his sal is 1000 20age , his sal is 800 -20age , his sal is 0 150age , his sal is 0.
**/