在这篇文章中,我将给你一个小抄,快速参考PHP中所有可用的魔法方法。
一般来说,PHP中的魔法方法允许你对对象进行各种操作。它们也允许你通过操纵对象来处理某些类型的情况。
PHP魔法方法
本文的目的是浏览PHP中所有的神奇方法,并对每个方法进行简单的解释。
__construct() 方法
如果你在你的类中定义了这个方法,当一个对象被实例化时,它将被自动调用。这个方法的目的是给对象的属性分配一些默认值。这个方法也被称为构造函数。
让我们看一下一个快速的例子来了解它是如何工作的。
<?php
class Student {
private $name;
private $email;
public function __construct($name, $email)
{
echo "Calling Student Constructor for $name.\n";
$this->name = $name;
$this->email = $email;
}
}
// Output: Calling Student Constructor for Monty.
$a_student = new Student('Monty', 'monty@tutsplus.com');
// Output: Calling Student Constructor for John.
$j_student = new Student('John', 'john@tutsplus.com');
?>
在上面的例子中,当你在实例化一个新对象时,用new student('John','john@tutsplus.com'),它首先调用__construct() 方法。在__construct() 方法中,我们已经将参数中传递的值分配给了对象的属性。
请记住,如果子类已经定义了自己的构造函数,那么父类的构造函数就不会被隐式调用。
__destruct() 方法
__destruct() 方法被称为析构器,它在对象被销毁时被调用。一般来说,当脚本停止或退出时也会调用它。这个方法的目的是提供一个机会来保存对象的状态或任何其他你想执行的清理工作。
让我们看一下下面的例子。
<?php
class Student {
private $name;
private $email;
public function __construct($name, $email)
{
echo "Calling Student Constructor for $name.\n";
$this->name = $name;
$this->email = $email;
}
public function __destruct()
{
echo "Calling Destructor for $this->name.\n";
// Save object state/other clean ups
}
}
// Output: Calling Student Constructor for Monty.
$a_student = new Student('Monty', 'monty@tutsplus.com');
// Output: Calling Student Constructor for John.
$j_student = new Student('John', 'john@tutsplus.com');
/*
Calling Destructor for John.
Calling Destructor for Monty.
*/
?>
就像构造器一样,如果子代没有自己的析构器,它也可以继承父代的析构器。
__set() 方法
当你试图为不可访问或不存在的对象属性设置数据时,会调用__set() 魔法方法。这个方法的目的是为你没有明确定义的对象属性设置额外的对象数据。
让我们回到我们的例子中来理解它是如何工作的。
<?php
class Student {
private $data = array();
public function __set($name, $value)
{
$this->data[$name] = $value;
}
}
$objStudent = new Student();
// __set() called
$objStudent->phone = '0491 570 156';
?>
正如你在上面的例子中所看到的,我们正试图设置phone ,而这个属性是不存在的。因此,__set() 方法被调用。__set() 方法的第一个参数是被访问的属性的名称,第二个参数是我们试图设置的值。
__get() 方法
在上一节中的__set() 方法的例子中,我们讨论了如何为不存在的属性设置值。__get() 方法正好与之相反。当你试图从不可访问或不存在的对象属性中读取数据时,会调用__get() 魔法方法。这个方法的目的是为这类属性提供值。
让我们看看它是如何工作的。
<?php
class Student {
private $data = array();
public function __set($name, $value)
{
$this->data[$name] = $value;
}
public function __get($name)
{
if(isset($this->data[$name])) {
return $this->data[$name];
}
}
}
$objStudent = new Student();
// __set() called
$objStudent->phone = '0491 570 156';
// __get() called
// Output: 0491 570 156
echo $objStudent->phone;
?>
__call() 和__callStatic() 方法
如果说__get() 和__set() 方法是在你处理不存在的属性时调用的,那么__call() 方法是在你试图调用不可访问的方法时调用的,也就是你在类中没有定义的方法。
<?php
class Student {
public function __call($methodName, $arguments)
{
// $methodName = getStudentDetails
// $arguments = array('1')
}
}
$objStudent = new Student();
$objStudent->getStudentDetails(1);
?>
正如你所看到的,我们已经调用了getStudentDetails ,这个方法没有被定义,因此调用了__call() 这个神奇的方法。第一个参数是被调用方法的名称,第二个参数是该方法中传递的参数数组。
__callStatic() 方法与__call() 方法非常相似。唯一的例外是,当你试图在静态上下文中调用不可访问的方法时,它会被调用。因此,如果你试图访问任何未定义的静态方法,__callStatic() 函数将被调用。
在阅读了__get() 、__set() 和__call() 方法之后,你们中的一些人正在考虑使用它们来管理你的类的所有成员和方法。只是尽量避免把大量的代码放到它们里面。通常情况下,对于获取和设置成员或定义类方法,有明确的方法定义对可读性和维护来说是更好的。
__isset() 和__unset() 方法
当你在不可访问或不存在的对象属性上调用isset() 方法时,会调用__isset() 魔法方法。让我们通过一个例子看看它是如何工作的。
<?php
class Student {
private $data = array();
public function __isset($name)
{
return isset($this->data[$name]);
}
}
$objStudent = new Student();
echo isset($objStudent->phone);
?>
在上面的例子中,电话属性没有在类中定义,因此它将调用__isset() 方法。
另一方面,__unset() 是当你对不可访问或不存在的对象属性调用unset() 方法时调用的方法。
__toString() 方法
__toString() 魔法允许你定义当类的一个对象被当作一个字符串时,你想显示什么。如果你在你的对象上使用echo 或print ,而你没有定义__toString() 方法,它会给出一个错误。
让我们试着用下面的例子来理解它。
<?php
class Student {
private $name;
private $email;
public function __construct($name, $email)
{
$this->name = $name;
$this->email = $email;
}
public function __toString()
{
return 'Student name: '.$this->name
. '<br>'
. 'Student email: '.$this->email;
}
}
$objStudent = new Student('John', 'john@tutsplus.com');
echo $objStudent;
?>
在上面的例子中,当你呼应$objStudent 对象时,它将调用__toString() 方法。在该方法中,你可以决定你想显示的内容。如果你没有定义__toString() 方法,这将会导致一个错误!
__sleep() 和__wakeup() 方法
与我们到目前为止讨论的方法相比,__sleep() 魔法是不同的。当你在对象上调用serialize() 函数时,它被调用。在一个非常大的对象的情况下,你只想在序列化过程中保存选定的属性并清理该对象。__sleep() 方法必须返回一个数组,该数组包含对象所有应该被序列化的属性名称。
再次,让我们修改一下我们的例子,看看它是如何工作的。
<?php
class Student {
private $name;
private $email;
private $phone;
private $db_connection_link;
public function __construct($name, $email, $phone)
{
$this->name = $name;
$this->email = $email;
$this->phone = $phone;
}
public function __sleep()
{
return array('name', 'email', 'phone');
}
public function __wakeup()
{
$this->db_connection_link = your_db_connection_function();
}
}
?>
Student 在上面的例子中,当你serialize() 对象时,它将调用__sleep() 方法,它将只保留name,email, 和phone 变量的值。
另一方面,__wakeup() 魔法的用途是在对象上调用unserialize() 函数时重新建立任何连接和启动任务。
__serialize() 和__unserialize() 方法
在PHP7.4中引入了两个新的魔法方法,叫做__serialize() 和__unserialize() 。在许多方面,它们与__sleep() 和__wakeup() 方法相似,但它们帮助我们克服了这两种方法的一些局限性。例如,用__sleep() ,创建一个与对象的内存形式不同的序列化表示,会很困难,需要使用假的属性。
<?php
class Student {
private $name;
private $email;
private $phone;
private $db_connection_link;
public function __construct($name, $email, $phone)
{
$this->name = $name;
$this->email = $email;
$this->phone = $phone;
}
public function __serialize()
{
return ['name' => $this->name, 'email' => $this->email, 'mobile' => $this->phone];
}
public function __wakeup($data)
{
$this->name = $data['name'];
$this->email = $data['email'];
$this->phone = $data['mobile'];
$this->db_connection_link = your_db_connection_function();
}
}
?>
同样值得注意的是,当你为一个对象同时定义了__serialize() 和__sleep() ,只有__serialize() 被调用。同样地,当你为一个对象同时定义了__unserialize() 和__wakeup() 时,只有__unserialize() 才会被调用。
__invoke() 方法
__invoke() 魔法是一个特殊的方法,当你试图把一个对象当作一个函数来调用时,它就会被调用。首先,让我们看看它是如何工作的,然后我们再看看这个神奇方法的目的。
<?php
class Student {
private $name;
private $email;
public function __construct($name, $email)
{
$this->name = $name;
$this->email = $email;
}
public function __invoke()
{
echo 'Student object is called as a function!';
}
}
$objStudent = new Student('John', 'john@tutsplus.com');
$objStudent();
?>
正如你所看到的,$objStudent 对象被当作是一个函数,由于我们已经定义了__invoke() 方法,它将被调用而不是给你一个错误。__invoke() 方法的主要目的是,如果你想把你的对象当作可调用的,你可以实现这个方法。
__clone() 方法
如果你想复制一个现有的对象,你可以使用clone 关键字来实现。但是在克隆之后,如果你想修改被克隆对象的属性,你可以在你的类中定义__clone() 魔法方法。
<?php
Class Student_School {
}
class Student {
private $name;
private $email;
private $object_student_school;
public function __construct()
{
$this->object_student_school = new Student_School();
}
public function __clone()
{
$this->object_student_school = clone $this->object_student_school;
}
}
$objStudentOne = new Student();
$objStudentTwo = clone $objStudentOne;
?>
上述方法的问题是,它在克隆时对对象进行了浅层复制,因此,被克隆对象的内部对象不会被克隆。
在上面的例子中,如果你没有定义__clone() 方法,被克隆的对象,$objStudentTwo ,仍然会指向被$objStudentOne 对象引用的同一个Student_School 对象。因此,通过实现__clone() 方法,我们确保了Student_School 对象与主对象一起被克隆。
__debugInfo() 方法
当你试图使用var_dump() 函数来转储一个对象时,会调用__debugInfo() 魔法方法。如果你没有在你的类中定义这个方法,它将转储所有的公共、私有和受保护的属性。因此,如果你想限制转储时显示的信息,你可以使用这个方法。
<?php
class Student {
public $name;
private $email;
private $ssn;
public function __debugInfo()
{
return array('student_name' => $this->name);
}
}
$objStudent = new Student();
var_dump($objStudent);
// object(Student)#1 (1) { ["student_name"]=> NULL }
?>
这个方法应该返回一个键值对的数组,当var_dump() 函数在对象上被调用时,这些键值对将被显示。正如你所看到的,你可以完全控制你想在用var_dump() 函数转储对象时显示什么。
__set_state() 方法
__set_state() 方法是一个静态方法,与var_export() 函数一起使用。var_export() 函数输出关于一个变量的结构化信息。当你使用这个函数导出类时,你需要在你的类中定义__set_state() 方法。
<?php
class Student {
public $name;
private $email;
public function __construct($name, $email)
{
$this->name = $name;
$this->email = $email;
}
public static function __set_state(array $array)
{
$obj = new Student;
$obj->name = $array['name'];
$obj->email = $array['email'];
return $obj;
}
}
$objStudent = new Student('John','John@yahoo.com');
var_export($objStudent);
// Output: Student::__set_state(array( 'name' => 'John', 'email' => 'John@yahoo.com', ))
?>
正如你所看到的,导出的字符串是有效的PHP代码,你可以用它来恢复原始对象。
总结
在这篇文章中,我们已经浏览了PHP中所有可用的神奇方法。对于每个方法,我都提供了一个简短但有意义的例子,这应该有助于你理解它的目的。我希望你能在日常的PHP开发中把这篇文章作为一个快速参考或小抄。