zfxcms ^回到顶部

您的当前位置:首页 > php资讯 > PHP基础 > php魔术方法(二)

php魔术方法(二)

所属分类: PHP基础   2020-03-08 13:33:20  编辑:admin  浏览次数 563 次

九、 __sleep(),执行serialize()时,先会调用这个函数

serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,则该方法会优先被调用,然后才执行序列化操作。

此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。

如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。

注意:

__sleep() 不能返回父类的私有成员的名字。这样做会产生一个 E_NOTICE 级别的错误。可以用 Serializable 接口来替代。

 作用:

__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。

具体请参考如下代码:

<?php

class Person

{

  public $sex;

  public $name;

  public $age;

 

  public function __construct($name="", $age=25, $sex='男')

  {

    $this->name = $name;

    $this->age = $age;

    $this->sex = $sex;

  }

  /**

   * @return array

   */

  public function __sleep() {

    echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";

    $this->name = base64_encode($this->name);

    return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称

  }

}

$person = new Person('小明'); // 初始赋值

echo serialize($person);

echo '<br/>';

代码运行结果:

当在类外部使用serialize()时会调用这里的__sleep()方法

O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}



十、 __wakeup(),执行unserialize()时,先会调用这个函数



如果说 __sleep() 是白的,那么 __wakeup() 就是黑的了。


那么为什么呢?


因为:


与之相反,`unserialize()` 会检查是否存在一个 `__wakeup()` 方法。如果存在,则会先调用 `__wakeup` 方法,预先准备对象需要的资源。



作用:



__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。



还是看代码:


<?php

class Person

{

  public $sex;

  public $name;

  public $age;

 

  public function __construct($name="", $age=25, $sex='男')

  {

    $this->name = $name;

    $this->age = $age;

    $this->sex = $sex;

  }

 

  /**

   * @return array

   */

  public function __sleep() {

    echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";

    $this->name = base64_encode($this->name);

    return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称

  }

 

  /**

   * __wakeup

   */

  public function __wakeup() {

    echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";

    $this->name = 2;

    $this->sex = '男';

    // 这里不需要返回数组

  }

}

 

$person = new Person('小明'); // 初始赋值

var_dump(serialize($person));

var_dump(unserialize(serialize($person)));

 

运行结果:


当在类外部使用serialize()时会调用这里的__sleep()方法

string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}" 当在类外部使用serialize()时会调用这里的__sleep()方法

 当在类外部使用unserialize()时会调用这里的__wakeup()方法

object(Person)#2 (3) { ["sex"]=> string(3) "男" ["name"]=> int(2) ["age"]=> int(25) }



十一、 __toString(),类被当成字符串时的回应方法



作用:


__toString() 方法用于一个类被当成字符串时应怎样回应。例如 `echo $obj;` 应该显示些什么。



注意:


此方法必须返回一个字符串,否则将发出一条 `E_RECOVERABLE_ERROR` 级别的致命错误。



警告:


不能在 __toString() 方法中抛出异常。这么做会导致致命错误。



代码:


<?php

class Person

{

  public $sex;

  public $name;

  public $age;

 

  public function __construct($name="", $age=25, $sex='男')

  {

    $this->name = $name;

    $this->age = $age;

    $this->sex = $sex;

  }

 

  public function __toString()

  {

    return 'go go go';

  }

}

 

$person = new Person('小明'); // 初始赋值

echo $person;

 

结果:


go go go



那么如果类中没有 __toString() 这个魔术方法运行会发生什么呢?让我们来测试下:


代码:


<?php

class Person

{

  public $sex;

  public $name;

  public $age;

 

  public function __construct($name="", $age=25, $sex='男')

  {

    $this->name = $name;

    $this->age = $age;

    $this->sex = $sex;

  }

   

}

 

$person = new Person('小明'); // 初始赋值

echo $person;

 

结果:


Catchable fatal error: Object of class Person could not be converted to string in D:\phpStudy\WWW\test\index.php on line 18

很明显,页面报了一个致命错误,这是语法所不允许的。


十二、 __invoke(),调用函数的方式调用一个对象时的回应方法



作用:


当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。



注意:


本特性只在 PHP 5.3.0 及以上版本有效。



直接上代码:


<?php

class Person

{

  public $sex;

  public $name;

  public $age;

 

  public function __construct($name="", $age=25, $sex='男')

  {

    $this->name = $name;

    $this->age = $age;

    $this->sex = $sex;

  }

 

  public function __invoke() {

    echo '这可是一个对象哦';

  }

 

}

 

$person = new Person('小明'); // 初始赋值

$person();

 

查看运行结果:


这可是一个对象哦



当然,如果你执意要将对象当函数方法使用,那么会得到下面结果:


Fatal error: Function name must be a string in D:\phpStudy\WWW\test\index.php on line 18



十三、 __set_state(),调用var_export()导出类时,此静态方法会被调用。



作用:


自 PHP 5.1.0 起,当调用 var_export() 导出类时,此静态方法会被自动调用。



参数:


本方法的唯一参数是一个数组,其中包含按 array('property' => value, ...) 格式排列的类属性。



下面我们先来看看在没有加 __set_state() 情况按下,代码及运行结果如何:


上代码:


<?php

class Person

{

  public $sex;

  public $name;

  public $age;

 

  public function __construct($name="", $age=25, $sex='男')

  {

    $this->name = $name;

    $this->age = $age;

    $this->sex = $sex;

  }

 

}

 

$person = new Person('小明'); // 初始赋值

var_export($person);

 

看结果:


Person::__set_state(array( 'sex' => '男', 'name' => '小明', 'age' => 25, ))



很明显,将对象中的属性都打印出来了


加了 __set_state() 之后:


继续上代码:


<?php

class Person

{

  public $sex;

  public $name;

  public $age;

 

  public function __construct($name="", $age=25, $sex='男')

  {

    $this->name = $name;

    $this->age = $age;

    $this->sex = $sex;

  }

 

  public static function __set_state($an_array)

  {

    $a = new Person();

    $a->name = $an_array['name'];

    return $a;

  }

 

}

 

$person = new Person('小明'); // 初始赋值

$person->name = '小红';

var_export($person);

 

继续看结果:


Person::__set_state(array( 'sex' => '男', 'name' => '小红', 'age' => 25, ))



十四、 __clone(),当对象复制完成时调用



在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 GTK 窗口对象,该对象持有窗口相关的资源。你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。还有一种情况:如果对象 A 中保存着对象 B 的引用,当你复制对象 A 时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象 A 的一个副本。


作用:


对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。对象中的 __clone() 方法不能被直接调用。


语法:


$copy_of_object = clone $object;



注意:


当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。


当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。

 看代码:

 

<?php

class Person

{

  public $sex;

  public $name;

  public $age;

 

  public function __construct($name="", $age=25, $sex='男')

  {

    $this->name = $name;

    $this->age = $age;

    $this->sex = $sex;

  }

 

  public function __clone()

  {

    echo __METHOD__."你正在克隆对象<br>";

  }

 

}

 

$person = new Person('小明'); // 初始赋值

$person2 = clone $person;

 

var_dump('persion1:');

var_dump($person);

echo '<br>';

var_dump('persion2:');

var_dump($person2);

 

看结果:


Person::__clone你正在克隆对象

string(9) "persion1:" object(Person)#1 (3) { ["sex"]=> string(3) "男" ["name"]=> string(6) "小明" ["age"]=> int(25) } 

 string(9) "persion2:" object(Person)#2 (3) { ["sex"]=> string(3) "男" ["name"]=> string(6) "小明" ["age"]=> int(25) }



克隆成功。


十五、__autoload(),尝试加载未定义的类


作用:


你可以通过定义这个函数来启用类的自动加载。



在魔术函数 __autoload() 方法出现以前,如果你要在一个程序文件中实例化100个对象,那么你必须用include或者require包含进来100个类文件,或者你把这100个类定义在同一个类文件中 —— 相信这个文件一定会非常大,然后你就痛苦了。


但是有了 __autoload() 方法,以后就不必为此大伤脑筋了,这个类会在你实例化对象之前自动加载制定的文件。


还是通过例子来看看吧:


先看看以往的方式:

 

/** 

 * 文件non_autoload.php 

 */

   

require_once('project/class/A.php'); 

require_once('project/class/B.php'); 

require_once('project/class/C.php'); 

   

if (条件A) { 

  $a = new A(); 

  $b = new B(); 

  $c = new C(); 

  // … 业务逻辑 

} else if (条件B) { 

  $a = newA(); 

  $b = new B(); 

  // … 业务逻辑 

}

 

看到了吗?不用100个,只是3个看起来就有点烦了。而且这样就会有一个问题:如果脚本执行“条件B”这个分支时,C.php这个文件其实没有必要包含。因为,任何一个被包含的文件,无论是否使用,均会被php引擎编译。如果不使用,却被编译,这样可以被视作一种资源浪费。更进一步,如果C.php包含了D.php,D.php包含了E.php。并且大部分情况都执行“条件B”分支,那么就会浪费一部分资源去编译C.php,D.php,E.php三个“无用”的文件。


那么如果使用 __autoload() 方式呢?


/** 

 * 文件autoload_demo.php 

 */

function __autoload($className) { 

  $filePath = “project/class/{$className}.php”; 

  if (is_readable($filePath)) { 

    require($filePath); 

  } 

   

if (条件A) { 

  $a = new A(); 

  $b = new B(); 

  $c = new C(); 

  // … 业务逻辑 

} else if (条件B) { 

  $a = newA(); 

  $b = new B(); 

  // … 业务逻辑 

}

 

ok,不论效率怎么用,最起码界面看起来舒服多了,没有太多冗余的代。


再来看看这里的效率如何,我们分析下:


当php引擎第一次使用类A,但是找不到时,会自动调用 __autoload 方法,并将类名“A”作为参数传入。所以,我们在 __autoload() 中需要的做的就是根据类名,找到相应的文件,并包含进来,如果我们的方法也找不到,那么php引擎就会报错了。


注意:


这里可以只用require,因为一旦包含进来后,php引擎再遇到类A时,将不会调用__autoload,而是直接使用内存中的类A,不会导致多次包含。


扩展:


其实php发展到今天,已经有将 `spl_autoload_register` — 注册给定的函数作为 __autoload 的实现了,但是这个不在啊本文讲解之内,有兴趣可以自行看手册。



十六、__debugInfo(),打印所需调试信息


注意:


该方法在PHP 5.6.0及其以上版本才可以用,如果你发现使用无效或者报错,请查看啊你的版本。



看代码:


<?php

class C {

  private $prop;

 

  public function __construct($val) {

    $this->prop = $val;

  }

 

  /**

   * @return array

   */

  public function __debugInfo() {

    return [

      'propSquared' => $this->prop ** 2,

    ];

  }

}

 

var_dump(new C(42));

结果:

object(C)#1 (1) { ["propSquared"]=> int(1764) }

再次注意:

这里的 `**` 是乘方的意思,也是在PHP5.6.0及其以上才可以使用,详情请查看PHP手册

PHP文章检索

PHP文章目录