魔术方法 Magic methods

__construct

__destrurct

__call

__callStatic

__get

__set

__isset

__unset

__sleep

__wakeup

__toString

__invoke

__set_state

__clone

__debugInfo

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

构造方法 __construct()

具有构造函数的类会在每次创建新对象时调用此方法,所以适合在对象调用时做一些初始化工作。

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

析构方法 __destruct(void)

析构方法会在某个对象的所有引用都被删除,或当前对象被显式销毁时执行。
析构函数即使在使用exit()终止脚本运行时也会被调用。在析构函数中调用exit()将会终止其余关闭操作的运行。
析构方法必须时public修饰。这个方法将会自动在外部调用,设置为protected、private将导致警告。
PHP手册

方法重载

call(string $name, array $arguments)、callStatic(string $name, array $arguments)
$name参数是要调用的方法名。$arguments参数是一个枚举数组,包含要传递给方法$name的参数。
案例:

class MethodTest {
  public function __call($name, $arguments) {
    echo "Calling object method '$name' " . implode(',',$arguments) . "\n";
  }
  public static function __callStatic($name, $arguments) {
    echo "Calling object method '$name' " . implode(',',$arguments) . "\n";
  }
}
$obj = new MethodTest();
$obj->runTest('in object context');
MethodTest::runTest('in static context');

属性重载

public void __set(string $name, mixed $value)
在给不可访问的属性赋值时,set()会调用
`public mixed
get(string $name)
读取不可访问的属性时,__get()会调用
public bool isset(string $name)`
当对不可访问的属性调用isset()或者empty()时,
isset()会被调用
public void __unset(string $name)
当对不可访问属性调用unset()时,__unset()会被调用

class ProperTest {
  // 被重载的数据保存在此
  private $data = [];
  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;
  }

  public function __isset($name) {
    echo "Is '$name' \n";
    unset($this->data[$name]);
  }

  // 非魔术方法
  public function getHidden() {
    return $this->hidden;
  }
}
echo "
\n";
$obj = new ProperTest();
$obj->a = 1; // Setting 'a' to '1'
echo $obj->a . "\n\n"; // Getting 'a' 1

var_dump(isset($obj->a)); // Is 'a' 
unset($obj->a);
var_dump(isset($obj->a)); // Is 'a'
echo "\n";
echo $obj->declared . "\n\n"; // 1

echo "Let's experiment with the private property named 'Hidden':\n";
echo "Privates are visable 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";

sleep() 和 wakeup()

public array __sleeep(void)
serialize()函数会检查类中是否存在一个魔术方法sleep()。如果存在,则调用,然后再执行序列化。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。
`void
wakeup(void)`

Note: __sleep()不能返回父类的私有成员的名字。这样会产生一个E_NOTICE级别的错误。

sleep()方法常用于提交未提交的数据,或类似的清理操作。同时,如果一些很大的对象,但不需要全部保存,这个功能很好用。
与之相反,unserialize()会检查是否存在一个
wakeup()方法。如果存在,则会先调用wakeup()方法,预先准备对象需要的资源。
wakeup()经常用在反序列化操作中,例如重新建立数据库连接,或者执行其他初始化操作。

__toString()

用于一个类被当成字符串应该怎样回应。例如:echo $obj;应该显示什么。此方法必须返回一个字符串。否则将发出一个致命的错误。

class TestClass {
  public $foo;
  public function __construct($foo) {
    $this->foo = $foo;
  }

  public function __toString() {
    return $this->foo;
  }
}
$class = new TestClass('Hello');
echo $class;

__invoke()

mixed __invoke([$...])
当尝试以调用函数的方式调用一个对象时,__invoke()方法会被自定调用

class CallableClass {
  public function __invoke($param1, $param2) {
    echo $param1 . " " . $param2 . "\n";
  }
}
$obj = new CallableClass();
$obj(123, 456);
var_dump(is_callable($obj));

__set_state()

static object __set_state(array $properties)
调用var_export()导出类时,此静态方法会被调用。本方法的唯一参数是一个数组,其中包含array('property'=>value,...)

class A {
  public $var1;
  public $var2;

  public static function __set_state($an_array) {
    $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) . ';');
echo $b->var2;

__debugInfo()

array __debugInfo(void)
调用var_dump()导出对象属性是,此方法会被调用。如果未在对象上定义该方法,则将显示所有public, protected, private属性。

__clone()

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