php中的魔术方法
PHP魔术方法:
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo() 等方法在 PHP 中被称为"魔术方法"(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。
__construct(),类的构造函数__destruct(),类的析构函数__call(),在对象中调用一个不可访问方法(私有或者不存在)时调用__callStatic(),用静态方式中调用一个不可访问方法时调用__get(),获得一个类的成员变量时调用__set(),设置一个类的成员变量时调用__isset(),当对不可访问属性调用isset()或empty()时调用__unset(),当对不可访问属性调用unset()时被调用。__sleep(),执行serialize()时,先会调用这个函数__wakeup(),执行unserialize()时,先会调用这个函数__toString(),类被当成字符串时的回应方法__invoke(),调用函数的方式调用一个对象时的回应方法__set_state(),调用var_export()导出类时,此静态方法会被调用。__clone(),当对象复制完成时调用
__construct()和__destruct()
构造函数__construct()在对象被创建的时候调用,析构函数__destruct()在对象消亡的时候被调用
<?php  class ConDes{    protected $a = '';    function __construct(){        echo '在构造函数中<br>'; } function __destruct(){ echo '在析构函数中<br>'; }}$val = new ConDes();unset($val);?><pre name="code" class="php">
在构造函数中在析构函数中
__call()和__callStatic()在对象中调用一个不可访问方法时会调用这两个方法,后者为静态方法。
<?php  class MethodTest  {    public function __call ($name, $arguments) {    var_dump($arguments);        echo "object method $name and ".implode(',',$arguments)."<br>"; } public static function __callStatic ($name, $arguments) { echo "static method $name and ".implode(',',$arguments)."<br>"; }}$obj = new MethodTest;$obj->runTest('in object context','another arg');MethodTest::runTest('in static context'); ?>
array (size=2)
0 => string 'in object context' (length=17)
1 => string 'another arg' (length=11)
object method runTest and in object context,another arg
static method runTest and in static context
__get(),__set(),__isset()和__unset()
当获取一个不可访问的类成员变量或设置一个不可访问的类成员变量时调用这两个函数。
<?phpclass MethodTest { private $data = array(); private $a = ''; public $bbb = ''; public function __set($name, $value){ $this->data[$name] = $value; echo '__set'; var_dump($this->data); } public function __get($name){ echo '__get'; var_dump($this->data); if(array_key_exists($name, $this->data)) return $this->data[$name]; return NULL; } public function __isset($name){ echo '__isset'; return isset($this->data[$name]); } public function __unset($name){ echo '__unset'; unset($this->data[$name]); }}$in = new MethodTest();$in->a = 'aaaa';$aaa = $in->a;$res = isset($in->c)? 'set':'not set';echo '<br>'.$res.'<br>';unset($in->a);?>
__set
array (size=1)
'a' => string 'aaaa' (length=4)
__get
array (size=1)
'a' => string 'aaaa' (length=4)
__isset
not set
__unset
__sleep()和__wakeup()
当我们在执行serialize()和unserialize()时,会先调用这两个函数。例如我们在序列化一个对象时,这个对象有一个数据库链接,想要在反序列化中恢复链接状态,则可以通过重构这两个函数来实现链接的恢复。
<span class="hljs-preprocessor"></span>
<?phpclass Connection {    public $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() { echo 'sleep<br>'; return array('server', 'username', 'password', 'db'); } public function __wakeup() { echo 'wakeup<br>'; $this->connect(); }}$a = new Connection('localhost','mosi','moshi','test');$sql = 'select id,username from user limit 1';$res = mysql_query($sql,$a->link);$res = mysql_fetch_array($res);var_dump($res);$sres = serialize($a);mysql_close($a->link);//unset($a);$unsres = unserialize($sres);var_dump($unsres);$sql = 'select id,username from user limit 1';$ress = mysql_query($sql,$unsres->link);$ress = mysql_fetch_array($ress);var_dump($ress);?>
<span class="hljs-preprocessor">array (size=4)<br> 0 => string '1' (length=1)<br> 'id' => string '1' (length=1)<br> 1 => string 'm0sh1' (length=5)<br> 'username' => string 'm0sh1' (length=5)<br>sleep<br>wakeup<br>object(Connection)[2]<br> public 'link' => resource(6, mysql link)<br> private 'server' => string 'localhost' (length=9)<br> private 'username' => string 'moshi' (length=4)<br> private 'password' => string 'moshi' (length=5)<br> private 'db' => string 'test' (length=4)<br>array (size=4)<br> 0 => string '1' (length=1)<br> 'id' => string '1' (length=1)<br> 1 => string 'm0sh1' (length=5)<br> 'username' => string 'm0sh1' (length=5)</span>
<span class="hljs-preprocessor"><br></span>
<span class="hljs-preprocessor">__toString()<br><br>对象当成字符串时的回应方法。例如使用echo $obj;</span>
<span class="hljs-preprocessor"></span>
<?php class TestClass { public function __toString() { return 'this is a object'; }}$class = new TestClass();echo $class; ?>
<span class="hljs-preprocessor"></span>
<span class="hljs-preprocessor"></span>
this is a object
这个方法只能返回字符串,而且不可以在这个方法中抛出异常,否则会出现致命错误。
__invoke()
调用函数的方式调用一个对象时的回应方法。
<?phpclass Invoke{ public function __invoke(){ echo 'in invoke<br>'; }}class noInvoke{}$obj = new Invoke();$obj();var_dump(is_callable($obj));$obj2 = new noInvoke();//$obj2();var_dump(is_callable($obj2));
in invoke
boolean true
boolean false
__set_state()
调用var_export()导出类时,此静态方法会被调用。
<?php class A { public $var1; public $var2; public static function __set_state ($arr) { $obj = new A; $obj->var1 = 'var11'; $obj->var2 = $arr['var2']; return $obj; }}$a = new A;$a->var1 = 5;$a->var2 = 'foo';var_dump($a); var_export($a); eval('$ress = '.var_export($a,true).';');var_dump($ress);?>
输出:
object(A)[1]
public 'var1' => int 5
public 'var2' => string 'foo' (length=3)
A::__set_state(array( 'var1' => 5, 'var2' => 'foo', ))
object(A)[2]
public 'var1' => string 'var11' (length=5)
public 'var2' => string 'foo' (length=3)
__clone()
当对象复制完成时调用。
<?php class Singleton { private static $_instance = NULL; // 私有构造方法 private function __construct() {} public static function getInstance() { if (is_null(self::$_instance)) { self::$_instance = new Singleton(); } return self::$_instance; } // 防止克隆实例 public function __clone(){ die('Clone is not allowed error: ' . E_USER_ERROR); }}$a = Singleton::getInstance();$b = Singleton::getInstance();if( $a === $b ){ echo 'equal<br>';}$c = clone $b;?>
equal
Clone is not allowed error: 256
PHP 魔术常量:简介在这里