在 PHP 中,魔术方法是一种特殊的方法,由对象中的某些动作或事件自动调用。这些方法具有以双下划线开头和结尾的预定义名称,因此称为“魔术方法”。它们提供了一种在类中实现通用功能或行为而无需显式调用方法的方法。
__construct()
construct() 方法是PHP类的构造器方法,当从类创建对象时会自动调用该方法。它通常用作构造函数方法,这它负责初始化对象的属性并执行在使用对象之前所需的任何设置任务。
construct() 方法的示例如下:
class MyClass {
public function __construct() {
// Initialization code and setup tasks go here
}
}
当使用 new 关键字创建 MyClass 类的对象时,将自动调用 __construct() 方法,允许您执行任何必要的初始化:
$obj = new MyClass(); // Calls the __construct() method
我们可以将参数传递给 __construct() 方法以提供对象属性的初始值。这是一个例子:
class MyClass {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
$obj = new MyClass('John');
echo $obj->getName(); // Outputs "John"
在此示例中, construct() 方法接受一个 $name 参数,然后将其分配给对象的 $name 属性。 getName() 方法检索并返回 $name 属性的值。
使用 construct() 方法可以确保在从类创建对象时执行必要的设置任务,从而提供一种方便的方法来初始化对象属性并在使用对象之前执行任何其他所需的操作。
另外,从php8.0开始,当构造器参数带访问控制(如private,protected)时,PHP 会同时把它当作对象属性和构造器参数, 并赋值到属性。 构造器可以是空的,或者包含其他语句。 参数值赋值到相应属性后执行正文中额外的代码语句,上面的例子可以改进为如下代码:
class MyClass {
public function __construct(private string $name) {
}
public function getName() {
return $this->name;
}
}
$obj = new MyClass('John');
echo $obj->getName(); // Outputs "John"
__destruct()
destruct() 魔术方法用于定义当不再引用对象或脚本执行结束时应执行的清理或完成任务。当对象即将被销毁时,PHP 会自动调用它。
下面是在类中定义 destruct() 方法的示例:
class MyClass {
public function __destruct() {
// Cleanup tasks and resource releases go here
}
}
当您需要在销毁对象之前释放资源、关闭数据库连接或执行任何其他清理任务时, destruct() 方法会很有用。它还可以用于记录或完成操作。
这是一个演示 destruct() 用法的示例:
class DatabaseConnection {
private $connection;
public function __construct() {
// Open the database connection
$this->connection = mysqli_connect('localhost', 'username', 'password', 'database');
}
public function query($sql) {
// Perform a database query using the connection
$result = mysqli_query($this->connection, $sql);
// Process the result
// ...
}
public function __destruct() {
// Close the database connection
mysqli_close($this->connection);
}
}
// Create an instance of the class
$db = new DatabaseConnection();
// Perform some database operations
$db->query('SELECT * FROM users');
// ...
// When the script ends or the object is no longer referenced,
// the __destruct() method will be automatically called,
// closing the database connection and performing any cleanup tasks.
在此示例中, construct() 方法打开数据库连接, destruct() 方法关闭连接。这可确保在对象被销毁或脚本执行结束时正确关闭连接并释放资源。
请务必注意, __destruct() 方法由 PHP 自动调用,您无需显式调用它。当一个对象不再被引用或脚本执行结束时,它基于 PHP 的垃圾回收机制被触发。
__call()
call() 魔术方法用于处理对对象中不可访问或不存在的方法的调用。当调用一个不可访问或不存在于对象中的方法时,PHP 会自动调用 call() 方法,允许您动态处理方法调用。
call() 方法接收两个参数:被调用方法的名称和传递给该方法的参数数组。您可以使用这些参数来实现自定义逻辑来处理方法调用并提供动态响应。
下面是在类中定义 call() 方法的示例:
class MyClass {
public function __call($name, $arguments) {
// Custom logic to handle the method call
}
}
让我们看一个演示 __call() 用法的示例:
class Calculator {
public function add($a, $b) {
return $a + $b;
}
public function __call($name, $arguments) {
if ($name === 'multiply') {
if (count($arguments) >= 2) {
$result = 1;
foreach ($arguments as $arg) {
$result *= $arg;
}
return $result;
}
}
throw new Exception("Method $name does not exist");
}
}
$calc = new Calculator();
echo $calc->add(2, 3); // Outputs 5
echo $calc->multiply(2, 3, 4); // Outputs 24
在此示例中, Calculator 类有一个 add() 方法,这是一个显式定义的常规方法。但是,当调用名为 multiply() 的方法(该方法未在类中定义)时,将调用 call() 方法。在 call() 方法内部,实现了自定义逻辑来检查方法名称是否为 'multiply' 。如果匹配,该方法将所有参数相乘并返回结果。否则,将抛出异常以指示该方法不存在。
使用 __call() 时,它提供了动态处理方法调用的灵活性,允许您实现回退行为或处理未在类中显式定义的方法调用
__callStatic()
callStatic() 魔术方法用于处理对类中不可访问或不存在的方法的静态方法调用。它类似于 call() 魔术方法,但专门用于处理静态方法调用。
当在类上调用静态方法并且该方法不可访问或不存在时,PHP 会自动调用 callStatic() 方法,允许您动态处理静态方法调用。
callStatic() 方法必须定义为类中的静态方法,并接收两个参数:被调用方法的名称和传递给该方法的参数数组。您可以使用这些参数来实现自定义逻辑来处理静态方法调用并提供动态响应。
下面是如何在类中定义 __callStatic() 方法的示例:
class MyClass {
public static function __callStatic($name, $arguments) {
// Custom logic to handle the static method call
}
}
让我们看一个演示 __callStatic() 用法的示例:
class Math {
public static function __callStatic($name, $arguments) {
if ($name === 'sum') {
return array_sum($arguments);
}
throw new Exception("Static method $name does not exist");
}
}
echo Math::sum(2, 3, 4); // Outputs 9
在此示例中, Math 类未显式定义 sum() 静态方法。当调用类中未定义的静态方法 Math::sum() 时,将调用 callStatic() 方法。在 callStatic() 方法内部,实现了自定义逻辑来检查方法名称是否为 'sum' 。如果匹配,该方法使用 array_sum() 计算参数的总和并返回结果。否则,抛出一个异常,表明该静态方法不存在。
使用 __callStatic() ,您可以处理动态静态方法调用并为类中不可访问或不存在的静态方法实现自定义行为。
__get()
get() 魔术方法用于拦截和处理访问对象不可访问或不存在的属性的尝试。当使用对象的属性语法访问未定义或不可访问的属性时,它会自动调用。
get() 方法接收一个参数,即被访问的属性的名称。您可以使用此参数来实现自定义逻辑来处理属性访问并返回动态值。
下面是如何在类中定义 __get() 方法的示例:
class MyClass {
private $data = array();
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
} else {
throw new Exception("Property $name does not exist");
}
}
}
在此示例中, MyClass 类有一个名为 $data 的私有属性,它是一个关联数组。当在未定义或不可访问的类的对象上访问属性时,将自动调用 get() 方法。在 get() 方法内部,实现了自定义逻辑来检查属性是否存在于 $data 数组中。如果是,则返回相应的值。否则,将抛出异常以指示该属性不存在。
这是使用 __get() 方法的示例:
$obj = new MyClass();
$obj->name = "John";
echo $obj->name; // Outputs "John"
echo $obj->age; // Throws an exception: "Property age does not exist"
在此示例中, $name 属性已定义且可访问,因此在通过 $obj->name 访问时返回其值。但是,当尝试访问不存在的 $age 属性时, get() 方法会抛出异常。
使用 get() ,您可以提供自定义逻辑来处理属性访问,允许您在访问未定义或不可访问的属性时检索动态值或实施回退行为。
__set()
set() 魔术方法用于拦截和处理将值分配给对象的不可访问或不存在的属性的尝试。当使用对象的属性语法为未定义或不可访问的属性赋值时,它会自动调用。
set() 方法接收两个参数:被赋值的属性的名称和被赋值的值。您可以使用这些参数来实现自定义逻辑来处理属性分配并执行任何必要的操作。
下面是如何在类中定义 __set() 方法的示例:
class MyClass {
private $data = array();
public function __set($name, $value) {
$this->data[$name] = $value;
}
}
在此示例中, MyClass 类有一个名为 $data 的私有属性,它是一个关联数组。当给未定义或不可访问的类对象的属性赋值时,将自动调用 set() 方法。在 set() 方法内部,实现了自定义逻辑,将分配的值存储在 $data 数组中,属性名称作为键。
这是使用 __set() 方法的示例:
$obj = new MyClass();
$obj->name = "John";
echo $obj->name; // Outputs "John"
在此示例中,当使用 $obj->name = "John" 将值 "John" 分配给不存在的 name 属性时,会自动调用 set() 方法。在 set() 方法中,值存储在 $data 数组中,键为 "name" 。稍后,当使用 $obj->name 访问该属性时,会从 $data 数组中检索并打印该值。
使用 __set() ,您可以提供自定义逻辑来处理属性分配,允许您在为未定义或不可访问的属性分配值时存储值或执行其他操作。
__isset()
isset() 魔术方法用于拦截和处理使用 isset() 函数或 isset() 语言构造检查对象是否存在不可访问或不存在的属性。当在对象的未定义或不可访问的属性上调用 isset() 时,它会自动调用。
isset() 方法接收一个参数,这是被检查是否存在的属性的名称。您可以使用此参数来实现自定义逻辑来确定该属性是否存在,并返回一个布尔值。
下面是如何在类中定义 __isset() 方法的示例:
class MyClass {
private $data = array();
public function __isset($name) {
return isset($this->data[$name]);
}
}
在此示例中, MyClass 类有一个名为 $data 的私有属性,它是一个关联数组。当在未定义或不可访问的对象的属性上调用 isset() 时,将自动调用 isset() 方法。在 isset() 方法内部,实现了自定义逻辑以使用 isset() 函数检查属性是否存在于 $data 数组中。如果属性存在,该方法返回 true ,否则返回 false 。
这是使用 __isset() 方法的示例:
$obj = new MyClass();
$obj->name = "John";
echo isset($obj->name); // Outputs "1" (true)
echo isset($obj->age); // Outputs "" (false)
在此示例中, isset($obj->name) 检查对象上是否存在 name 属性。由于被赋值, isset() 方法返回 true ,输出为“1”。但是,当调用不存在的属性 isset($obj->age) 时, isset() 方法返回 false ,输出为空字符串。
使用 __isset() ,您可以提供自定义逻辑来处理属性存在性检查,从而允许您确定对象中是否存在未定义或不可访问的属性。
__unset()
unset() 魔术方法用于拦截和处理对象不可访问或不存在的属性的取消设置操作。当在对象的未定义或不可访问的属性上调用 unset() 函数时,它会自动调用。
unset() 方法接收一个参数,即要取消设置的属性的名称。您可以使用此参数来实现自定义逻辑来处理取消设置操作并执行任何必要的操作。
下面是如何在类中定义 __unset() 方法的示例:
class MyClass {
private $data = array();
public function __unset($name) {
unset($this->data[$name]);
}
}
在此示例中, MyClass 类有一个名为 $data 的私有属性,它是一个关联数组。当对未定义或不可访问的对象的属性调用 unset() 函数时,将自动调用 unset() 方法。在 unset() 方法内部,实现了自定义逻辑以使用 unset() 函数从 $data 数组中删除属性。
这是使用 __unset() 方法的示例:
$obj = new MyClass();
$obj->name = "John";
unset($obj->name);
echo isset($obj->name); // Outputs "" (false)
在此示例中, unset($obj->name) 语句取消设置对象的 name 属性。结果,自动调用 unset() 方法,并从 $data 数组中删除该属性。稍后,当调用 isset($obj->name) 时,它会返回 false ,因为该属性已不存在。
使用 unset() ,您可以提供自定义逻辑来处理对属性的取消设置操作,允许您在取消设置对象的未定义或不可访问的属性时执行清理任务或其他操作。
__sleep()
当使用serialize序列化对象时,此函数会自动调用。此方法主要用于加解密一些类属性,这样防止可以防止序列化后一些类属性被明文显示。
<?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @return array
*/
public function __sleep() {
echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称
}
/**
* __wakeup
*/
public function __wakeup() {
echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
$this->name = 2;
$this->sex = '男';
// 这里不需要返回数组
}
}
$person = new Person('小明'); // 初始赋值
var_dump(serialize($person));
var_dump(unserialize(serialize($person)));
__wakeup()
使用使用 unserialize 反序列化对象时,此函数会自动调用,这个一般和sleep配合使用,具体代码见sleep
__toString()
这个魔术方法比较常用了。toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo$obj;
应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR
级别的致命错误。不能在 toString() 方法中抛出异常。这么做会导致致命错误。
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='男')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
}
$person = new Person('小明'); // 初始赋值
echo $person;
__invoke()
invoke用于将对象当做方法使用,如果在类中添加invoke方法,可以把实例化对象当方法使用。
class Test
{
public $name = 'cjl';
public $age = 18;
/**
* 打印类中两个变量
*/
public function __invoke()
{
var_dump($this->name, $this->age);
exit;
}
}
$obj = new Test; //实例化类
$obj();
通过__invoke魔术方法,可以实现类默认方法的功能,比如在laravel中,我们可以简化路由的书写
Route::get('/', WelcomeController::class);
__set_state($array)
这个魔术方法比较少见,主要用于var_export时置入自定义数组
<?php
// PHP魔术方法之__set_state()
//该方法的唯一参数是一个数组,其中包含array('property'=>value,...)格式排列的属性
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);
?>
__clone()
这个魔术方法主要针对clone 关键字,在PHP中,对象模型都是通过引用来调用对象的,但有时需要建立一个对象的副本,在改变原有对象时不希望影响到对象副本。如果使用new关键字重新创建对象,再为属性赋上相同的值,这样做会比较烦琐而且也容易出错。在 PHP 中可以根据现有的对象克隆出一个完全一样的对象,克隆以后,原本对象和副本对象是完全独立互不干扰的。
clone() 方法不能够直接被调用,只有当通过 clone 关键字克隆一个对象时才可以使用该对象调用 clone() 方法。当创建对象的副本时,PHP 会检查 clone() 方法是否存在。如果不存在,那么它就会调用默认的 clone() 方法,复制对象的所有属性。如果 clone() 方法已经定义过,那么 clone() 方法就会负责设置新对象的属性。所以在 __clone() 方法中,只需要覆盖那些需要更改的属性就可以了。
<?php
class Website{
public $name, $url;
public function __construct($name, $url){
$this -> name = $name;
$this -> url = $url;
}
public function output(){
echo $this -> name.','.$this -> url.'<br>';
}
public function __clone(){
$this -> name = '老俊说技术副本';
$this -> url = 'https://www.laojunsay.com/';
}
}
$obj = new Website('老俊说技术', 'https://www.laojunsay.com/');
$obj2 = clone $obj;
$obj -> output();
$obj2 -> output();
?>
以上代码将输出
老俊说技术,https://www.laojunsay.com/
老俊说技术副本,https://www.laojunsay.com/
__debugInfo()
该方法在var_dump()类对象的时候被调用,如果没有定义该方法,则var_dump会打印出所有的类属性
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)
}