ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] * * * * * ## 1 魔术方法的意义 php引擎将**类的相关接口**以魔术方法的形式暴露给用户, 用户可以通过**自定义相关接口**,实现灵活的功能组织 参考官方手册分为**三类 ** >[info] 1 构造函数与析构函数 `__construct() __destruct()` >[info] 2 重载 ~~~ __set() __get() __isset() __unset() __call() __callStatic() ~~~ >[info] 3 其他 ~~~ __sleep() __wakeup() __invoke() __toString() __set_state() __debugInfo ~~~ * * * * * ## 2 构造函数与析构函数 >[info] 1 构造函数 __construct() 在类中定义一个方法作为构造函数, 在每次创建新对象优先调用此方法, 通常完成对象的属性设置等初始化工作 为了调用父类的构造函数, 需要在子类构造函数中调用parent::__construct() 子类没有定义构造函数的会继承父类的非private类构造函数 ~~~ <?php class BaseClass { function __construct () { print "In BaseClass constructor\n" ; } } class SubClass extends BaseClass { function __construct () { parent :: __construct (); print "In SubClass constructor\n" ; } } class OtherSubClass extends BaseClass { // inherits BaseClass's constructor } // In BaseClass constructor $obj = new BaseClass (); // In BaseClass constructor // In SubClass constructor $obj = new SubClass (); // In BaseClass constructor $obj = new OtherSubClass (); ?> ~~~ php4中使用与类名相同的方法名作为构造函数, 为了向后兼容,php5中查找__consrtuct()构造函数失败时,会查找类同名构造函数, 命名空间中,与类名同名的方法不再作为构造函数 >[info] 2 析构函数__destruct() 类的对象的引用都被删除或者对象被显示销毁时, 调用析构函数,进行清理工作 ~~~ <?php class MyDestructableClass { function __construct () { print "In constructor\n" ; $this -> name = "MyDestructableClass" ; } function __destruct () { print "Destroying " . $this -> name . "\n" ; } } $obj = new MyDestructableClass (); ?> ~~~ 调用父类的析构函数, 需要在子类的析构函数中显示调用parent::__destruct() 子类如果自己没有定义析构函数则会继承父类的 析构函数在使用exit()终止脚本运行时也会被调用。 ## 3 重载 传统语言的重载是提供**同名的类方法的不同参数实现**, 而**php的重载是对不可访问属性和方法的动态实现** PHP的重载指的是动态地设置类的属性和方法, 与其他语言不通,php使用魔术方法实现重载 当调用未定义或不可见的类属性或方法时,重载方法会被调用 大部分重载方法**只能在对象中进行**,(除__callStatic)必须声明为public, (除__callStatic)不可声明为static,**参数不能使用引用传递** >[info] 属性重载 ~~~ public void __set ( string $name , mixed $value ) public mixed __get ( string $name ) public bool __isset ( string $name ) public void __unset ( string $name ) ~~~ 给不可访问属性赋值时 `__set()`会被调用 读取不可访问属性的值时,`__get()`会被调用 当对不可访问属性使用isset() empty()时,`__isset()`会被调用 当对不可访问属性调用unset()时,`__unset()`会被调用 ~~~ <?php class PropertyTest { /** 被重载的数据保存在此 */ private $data = array(); /** 重载不能被用在已经定义的属性 */ public $declared = 1 ; /** 只有从类外部访问这个属性时,重载才会发生 */ private $hidden = 2 ; public function __set ( $name , $value ) { echo "Setting ' $name ' to ' $value '\n" ; $this -> data [ $name ] = $value ; } public function __get ( $name ) { echo "Getting ' $name '\n" ; if ( array_key_exists ( $name , $this -> data )) { return $this -> data [ $name ]; } $trace = debug_backtrace (); trigger_error ( 'Undefined property via __get(): ' . $name . ' in ' . $trace [ 0 ][ 'file' ] . ' on line ' . $trace [ 0 ][ 'line' ], E_USER_NOTICE ); return null ; } /** PHP 5.1.0之后版本 */ public function __isset ( $name ) { echo "Is ' $name ' set?\n" ; return isset( $this -> data [ $name ]); } /** PHP 5.1.0之后版本 */ public function __unset ( $name ) { echo "Unsetting ' $name '\n" ; unset( $this -> data [ $name ]); } /** 非魔术方法 */ public function getHidden () { return $this -> hidden ; } } echo "<pre>\n" ; $obj = new PropertyTest ; $obj -> a = 1 ; echo $obj -> a . "\n\n" ; var_dump (isset( $obj -> a )); unset( $obj -> a ); var_dump (isset( $obj -> a )); echo "\n" ; echo $obj -> declared . "\n\n" ; echo "Let's experiment with the private property named 'hidden':\n" ; echo "Privates are visible inside the class, so __get() not used...\n" ; echo $obj -> getHidden () . "\n" ; echo "Privates not visible outside of class, so __get() is used...\n" ; echo $obj -> hidden . "\n" ; ?> ~~~ 输出: Setting 'a' to '1' Getting 'a' 1Is 'a' set? bool(true) Unsetting 'a' Is 'a' set? bool(false)1Let's experiment with the private property named 'hidden': Privates are visible inside the class, so __get() not used... 2 Privates not visible outside of class, so __get() is used... Getting 'hidden'Notice: Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29 * * * * * >[info] 方法重载 ~~~ public mixed __call ( string $name , array $arguments ) public static mixed __callStatic ( string $name , array $arguments ) ~~~ 在对象中调用一个不可访问方法是 __call()会被调用 用静态方式调用一个不可访问方法__callStatic()会被调用 ~~~ <?php class MethodTest { public function __call ( $name , $arguments ) { // 注意: $name 的值区分大小写 echo "Calling object method ' $name ' " . implode ( ', ' , $arguments ). "\n" ; } /** PHP 5.3.0之后版本 */ public static function __callStatic ( $name , $arguments ) { // 注意: $name 的值区分大小写 echo "Calling static method ' $name ' " . implode ( ', ' , $arguments ). "\n" ; } } $obj = new MethodTest ; $obj -> runTest ( 'in object context' ); MethodTest :: runTest ( 'in static context' ); // PHP 5.3.0之后版本 ?> ~~~ 输出: Calling object method 'runTest' in object context Calling static method 'runTest' in static context ## 4 其他 >[info] 1 __slepp() __wakeup() ~~~ public array __sleep ( void ) void __wakeup ( void ) ~~~ `__sleep() ` serialize()序列化对象时, 会优先回调__sleep()方法 然后执行序列化 通常用于返回包含对象中所有应被序列化的变量名称的数组。 如果该方法未返回内容,则NULL被序列化。 `__wakeup()` unserialize()解序列化时, 会优先回调__wakeup()方法 然后执行解序列化 通常用来准备对象需要的资源 ~~~ <?php class Connection { protected $link ; private $server , $username , $password , $db ; public function __construct ( $server , $username , $password , $db ) { $this -> server = $server ; $this -> username = $username ; $this -> password = $password ; $this -> db = $db ; $this -> connect (); } private function connect () { $this -> link = mysql_connect ( $this -> server , $this -> username , $this -> password ); mysql_select_db ( $this -> db , $this -> link ); } public function __sleep () { return array( 'server' , 'username' , 'password' , 'db' ); } public function __wakeup () { $this -> connect (); } } ?> ~~~ >[info] 2 __invoke() `mixed __invoke ([ $... ] )` 当以函数的方式调用一个对象时, __invoke会被自动调用 ~~~ <?php class CallableClass { function __invoke ( $x ) { var_dump ( $x ); } } $obj = new CallableClass ; $obj ( 5 ); var_dump ( is_callable ( $obj )); ?> ~~~ 输出: int(5) bool(true) * * * * * >[info]3 __toString `public string __toString ( void )` echo $obj,输出对象时的回调方法 必须返回一个字符串,否则发出致命错误 ~~~ <?php class TestClass { public $foo ; public function __construct ( $foo ) { $this -> foo = $foo ; } public function __toString () { return $this -> foo ; } } $class = new TestClass ( 'Hello' ); echo $class ; ?> ~~~ 输出:Hello * * * * * >[info] 4 __set_state() `static object __set_state()` 使用var_export()导出类时, 此类静态方法会被调用 ~~~ <?php class A { public $var1 ; public $var2 ; public static function __set_state ( $an_array ) // As of PHP 5.1.0 { $obj = new A ; $obj -> var1 = $an_array [ 'var1' ]; $obj -> var2 = $an_array [ 'var2' ]; return $obj ; } } $a = new A ; $a -> var1 = 5 ; $a -> var2 = 'foo' ; eval( '$b = ' . var_export ( $a , true ) . ';' ); // $b = A::__set_state(array( // 'var1' => 5, // 'var2' => 'foo', // )); var_dump ( $b ); ?> ~~~ >[info] 5 __debuginfo() `array __debugInfo ( void )` 使用var_dump()输出对象的信息是时回调。 未定义的的输出所有public protected属性, private属性不会输出 ~~~ <?php class C { private $prop ; public function __construct ( $val ) { $this -> prop = $val ; } public function __debugInfo () { return [ 'propSquared' => $this -> prop ** 2 , ]; } } var_dump (new C ( 42 )); ?> ~~~ 输出: object(C)#1 (1) { ["propSquared"]=> int(1764) } ## 5 tp5中的魔术方法使用