[关闭]
@52fhy 2016-02-28T10:57:16.000000Z 字数 5452 阅读 479

PHP笔记--魔术方法等

PHP


PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 __ 为前缀。

__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo()等方法在 PHP 中被称为"魔术方法"(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。

对象复制

  1. void __clone ( void )

在多数情况下,我们并不需要完全复制一个对象来获得其中属性。

如果对象 A 中保存着对象 B 的引用,当你复制对象 A 时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象 A 的一个副本。

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

  1. $copy_of_object = clone $object;

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

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

构造函数

  1. void __construct ([ mixed $args [, $... ]] )

PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。

Note: 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct() 。如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(假如没有被定义为 private 的话)。

析构函数

  1. void __destruct ( void )

PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct() 。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。

析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中调用 exit() 将会中止其余关闭操作的运行。

试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。

属性重载

  1. public void __set ( string $name , mixed $value )
  2. public mixed __get ( string $name )
  3. public bool __isset ( string $name )
  4. public void __unset ( string $name )

在给不可访问属性赋值时,__set() 会被调用。
读取不可访问属性的值时,__get() 会被调用。
当对不可访问属性调用 isset() 或 empty() 时,__isset()会被调用。
当对不可访问属性调用 unset() 时,__unset()会被调用。

参数 $name 是指要操作的变量名称。__set()方法的 $value 参数指定了 $name 变量的值。

属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被 声明为 static。从 PHP 5.3.0 起, 将这些魔术方法定义为 static 会产生一个警告。

方法重载

  1. public mixed __call ( string $name , array $arguments )
  2. public static mixed __callStatic ( string $name , array $arguments )

在对象中调用一个不可访问方法时,__call() 会被调用。
用静态方式中调用一个不可访问方法时,__callStatic() 会被调用。

$name 参数是要调用的方法名称。 $arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数。

注意这里的枚举数组的含义:
假如我们传的参数是多个,例如参数列表为:

  1. array('name'=>'test'),'test'

那么通过$arguments接收的参数为:

  1. array(array('name'=>'test'),'test')

通过func_get_args()接收的参数为:

  1. array($method_name, array(array('name'=>'test'),'test'))

这点需要注意。

__toString()

  1. public string __toString ( void )

__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

__invoke()

  1. mixed __invoke ([ $... ] )

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
Note:
本特性只在 PHP 5.3.0 及以上版本有效。

Example 使用 __invoke()

  1. <?php
  2. class CallableClass
  3. {
  4. function __invoke ( $x ) {
  5. var_dump ( $x );
  6. }
  7. }
  8. $obj = new CallableClass ;
  9. $obj ( 5 );
  10. var_dump ( is_callable ( $obj ));
  11. ?>

以上例程会输出:

  1. int(5)
  2. bool(true)

__debugInfo()

  1. array __debugInfo ( void )

当尝试用var_dump()去打印一个对象时, __debugInfo() 方法会被自动调用。这时候打印出的属性限定于__debugInfo()所允许访问的属性。如果没有定义该方法,则默认打印类里面所有的属性,包含public, protected,private。

This method is called by var_dump() when dumping an object to get the properties that should be shown. If the method isn't defined on an object, then all public, protected and private properties will be shown.

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

Example Using __debugInfo()

  1. <?php
  2. class C {
  3. private $prop ;
  4. public function __construct ( $val ) {
  5. $this -> prop = $val ;
  6. }
  7. public function __debugInfo () {
  8. return [
  9. 'propSquared' => $this -> prop ** 2 ,
  10. ];
  11. }
  12. }
  13. var_dump (new C ( 42 ));
  14. ?>

以上例程会输出:

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

__sleep() 和 __wakeup()

  1. public array __sleep ( void )
  2. void __wakeup ( void )

serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。

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

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

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

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

Example Sleep 和 wakeup

  1. <?php
  2. class Connection
  3. {
  4. protected $link ;
  5. private $server , $username , $password , $db ;
  6. public function __construct ( $server , $username , $password , $db )
  7. {
  8. $this -> server = $server ;
  9. $this -> username = $username ;
  10. $this -> password = $password ;
  11. $this -> db = $db ;
  12. $this -> connect ();
  13. }
  14. private function connect ()
  15. {
  16. $this -> link = mysql_connect ( $this -> server , $this -> username , $this -> password );
  17. mysql_select_db ( $this -> db , $this -> link );
  18. }
  19. public function __sleep ()
  20. {
  21. return array( 'server' , 'username' , 'password' , 'db' );
  22. }
  23. public function __wakeup ()
  24. {
  25. $this -> connect ();
  26. }
  27. }
  28. ?>

下面的笔记将不再是魔术方法相关。

call_user_func_array

  1. mixed call_user_func_array ( callable $callback , array $param_arr )
  2. 调用回调函数,并把一个数组参数作为回调函数的参数
  3. 参数:
  4. callback
  5. 被调用的回调函数。
  6. param_arr
  7. 要被传入回调函数的数组,这个数组得是索引数组。就算传入了关联数组,也会被转换为索引数组。
  8. 返回值:
  9. 返回回调函数的结果。如果出错的话就返回 FALSE
  1. <?php
  2. //注意从call_user_func_array接收的参数不再是数组了
  3. $func = function( $arg1 , $arg2 ) {
  4. return $arg1 * $arg2 ;
  5. };
  6. call_user_func_array ( $func , array( 2 , 4 ));
  7. //这里的第二个参数是个数组,不是给第一个参数里的回调函数传进去一个数组,而是传进去的连续参数:第一个参数是$args[0],第二个是$args[1]
  8. ?>

call_user_func

作用与call_user_func_array相同。接收的参数不同。call_user_func的第二个参数是回调函数的参数列表,即param2, ...;call_user_func_array的第二个参数是回调函数的参数合并的数组,即array(param2, ...)。

示例:

  1. function callback($param1, $param2){
  2. //code
  3. }
  4. call_user_func('callback', $param1, $param2);
  5. call_user_func_array('callback', array($param1, $param2));

php闭包函数简析

闭包函数(closures)也叫匿名函数,使用js的童鞋应该比较熟悉。PHP5.3开始引入了闭包的特性。

声明一个匿名函数是:

  1. $func = function() {
  2. }; //带结束符

匿名函数因为没有名字,如果要使用它,需要将其返回给一个变量。

在闭包函数里,如果要用到上文定义的一个变量,需要使用use关键字。直接使用变量是不行的。

  1. <?php
  2. $arr = [1,2,3];
  3. $max = 2;
  4. //匿名函数第一个括号里的参数是调用时赋予的。use里的参数是来自父类作用域的。
  5. $res = array_map(function($v1) use ($max){
  6. if($max > 1){
  7. return $v1 *= 2;
  8. }
  9. }, $arr);
  10. var_dump($res);

结果:

  1. array (size=3)
  2. 0 => int 2
  3. 1 => int 4
  4. 2 => int 6

PHP闭包的特性并没有太大惊喜,其实用CLASS就可以实现类似甚至强大得多的功能,更不能和js的闭包相提并论,只能期待PHP以后对闭包支持的改进。不过匿名函数还是挺有用的,比如在使用array_map、preg_replace_callback等之类的函数可以不用在外部声明回调函数了。使用闭包可以优雅的写PHP代码。

参考:
PHP闭包(Closure)初探 - 豆浆油条Melon的个人页面 - 开源中国社区
http://my.oschina.net/melonol/blog/126694

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注