No.7 PHP续集——PHP魔术方法详解

PHP魔术方法详解

PHP中把以两个下划线__开头的方法称为魔术方法(Magic methods),这些方法在PHP中充当了举足轻重的作用。

其他:

1、通过alt+inside来打开Override Methods:

2、在 PHP 中,这一种文档注释(DocBlock Comment)。

//方法
/**
 * @return mixed
 */

@return mixed 部分用于描述该方法的返回值类型和说明。 mixed 表示返回值的类型不固定或不确定。 文档注释通常用于为代码提供更清晰的文档说明,以提高代码的可理解性和可维护性。许多现代的 PHP 开发工具和框架可以利用这些文档注释来提供自动提示和生成相关的文档。

一、 __construct(),类的构造函数

php中构造方法是对象创建完成后第一个被对象自动调用的方法。在每个类中都有一个构造方法,如果没有显示地声明它,那么类中都会默认存在一个没有参数且内容为空的构造方法。

1、 构造方法的作用

通常构造方法被用来执行一些有用的初始化任务,如对成员属性在创建对象时赋予初始值。

2、 构造方法的在类中的声明格式
function __constrct([参数列表]){
    方法体 //通常用来对成员属性进行初始化赋值
}
3、 在类中声明构造方法需要注意的事项

1、在同一个类中只能声明一个构造方法,原因是,PHP不支持构造函数重载。 2、构造方法名称是以两个下画线开始的__construct()

下面是它的例子:

<?php
class Person
{
    public $name;
    public $age;
    public $sex;
    /**
     * 显示声明一个构造方法且带参数
     */
    public function __construct($name="", $sex="男", $age=22)
    {
        $this->name = $name;
        $this->sex = $sex;
        $this->age = $age;
    }
    /**
     * say 方法
     */
    public function say()
    {
        echo "我叫:". $this->name . ",性别:" . $this->sex . ",年龄:" .$this->age;
    }
}
创建对象$Person1且不带任参数
$Person1 = new Person();
echo $Person1->say(); //输出:我叫:,性别:男,年龄:22
创建对象$Person2且带参数“小明
$Person2 = new Person("小明",'男',18);
echo $Person2->say(); //输出:我叫:小明,性别:男,年龄:18
 
二、__destruct(),类的析构函数
  • 通过上面的讲解,现在我们已经知道了什么叫构造方法。那么与构造方法对应的就是析构方法。

  • 析构方法允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件、释放结果集等。

  • 析构方法是PHP5才引进的新内容。

  • 析造方法的声明格式与构造方法 __construct() 比较类似,也是以两个下划线开始的方法

  • __destruct() ,这种析构方法名称也是固定的。

1、 析构方法的声明格式
function __destruct()
{
    //方法体
}
 

注意:析构函数不能带有任何参数。

2、 析构方法的作用

一般来说,析构方法在PHP中并不是很常用,它属类中可选择的一部分,通常用来完成一些在对象销毁前的清理任务。

<?php
class Person{
    public $name;
    public $age;
    public $sex;
​
    public function __construct($name="", $sex="男", $age=22)
    {
    $this->name = $name;
    $this->sex = $sex;
    $this->age = $age;
    }
​
    /**
    * say 说话方法
    */
    public function say()
    {
    echo "我叫:".$this->name.",性别:".$this->sex.",年龄:".$this->age;
    }
​
    /**
    * 声明一个析构方法
    */
    public function __destruct()
    {
        echo "我觉得我还可以再抢救一下,我的名字叫".$this->name;
    }
}
​
$Person = new Person("小明");
unset($Person); //销毁上面创建的对象$Person
 
三、 __call(),在对象中调用一个不可访问方法时调用

该方法有两个参数,第一个参数 $function_name 会自动接收不存在的方法名,第二个 $arguments则以数组的方式接收不存在方法的多个参数。

1、 __call() 方法的格式:
function __call(string $function_name, array $arguments)
{
    // 方法体
}
 
2、 __call() 方法的作用:

为了避免当调用的方法不存在时产生错误,而意外的导致程序中止,可以使用 __call() 方法来避免。该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去。

<?php
class Person
{
    function say()
    {
        echo "Hello, world!<br>";
    }
    /**
     * 声明此方法用来处理调用对象中不存在的方法
     */
    function __call($funName, $arguments)
    {
        echo "你所调用的函数:" . $funName . "(参数:" ; // 输出调用不存在的方法名
        print_r($arguments); // 输出调用不存在的方法时的参数列表
        echo ")不存在!<br>\n"; // 结束换行
    }
}
$Person = new Person();
$Person->run("teacher"); // 调用对象中不存在的方法,则自动调用了对象中的__call()方法
$Person->eat("小明", "苹果");
$Person->say();
 
四、__callStatic(),用静态方式中调用一个不可访问方法时调用

此方法与上面所说的 call() 功能除了 callStatic() 是未静态方法准备的之外,其它都是一样的。

<?php
class Person
{
    function say()
    {
        echo "Hello, world!<br>";
    }
    /**
     * 声明此方法用来处理调用对象中不存在的方法
     */
    public static function __callStatic($funName, $arguments)
    {
        echo "你所调用的静态方法:" . $funName . "(参数:" ; // 输出调用不存在的方法名
        print_r($arguments); // 输出调用不存在的方法时的参数列表
        echo ")不存在!<br>\n"; // 结束换行
    }
}
$Person = new Person();
$Person::run("teacher"); // 调用对象中不存在的方法,则自动调用了对象中的__call()方法
$Person::eat("小明", "苹果");
$Person->say();
 

__call__callStatic二者的区别:

__call__callStatic 这两个魔术方法的主要区别在于调用方式和作用范围:

  1. __call 方法:

    • 作用范围:用于处理对象实例上不可访问(不存在或不可见)的方法调用。

    • 调用方式:通过对象实例进行方法调用时触发,即使用 $object->methodName() 的形式。

  2. __callStatic 方法:

    • 作用范围:用于处理以静态方式(使用 类名::methodName() )对类中不可访问(不存在或不可见)的静态方法的调用。

    • 调用方式:通过静态方式调用不可访问的方法时触发。

<?php
class MyClass {
​
    public function __call($name, $arguments) {
        echo "通过对象调用不存在的方法: $name ,参数: ";
        print_r($arguments);
        echo "<br>";
    }
​
    public static function __callStatic($name, $arguments) {
        echo "通过静态方式调用不存在的方法: $name ,参数: ";
        print_r($arguments);
        echo "<br>";
    }
}
​
$obj = new MyClass();
$obj->nonExistingMethod(1, 2, 3);
​
MyClass::staticNonExistingMethod(4, 5, 6);
?>
 
五、__get(),获得一个类的成员变量时调用

在 php 面向对象编程中,类的成员属性被设定为 private 后,如果我们试图在外面调用它则会出现“不能访问某个私有属性”的错误。那么为了解决这个问题,我们可以使用魔术方法 __get() 。

魔术方法__get()的作用: 在程序运行过程中,通过它可以在对象的外部获取私有成员属性的值。

<?php
class Person{
    //属性只能在类中方法 私有的
    private $name;
    private $age;
​
    public function __construct($name="",$age=1)
    {
        $this->name=$name;
        $this->age=$age;
    }
    //__get 直接获取属性值时自动调用一次 以属性名作为参数传入并处理
    public function __get($PName)
    {
        if ($PName=="age")
        {
            return $this->age;
        }
        else
        {
            return $this->name;
        }
    }
}
$A=new Person("小明",60);//通过实例化对象 并通过构造赋值
echo "姓名:".$A->name."<br>";//直接访问私有属性name 自动调用__get方法可以间接获取
echo "年龄:".$A->age."<br>";//直接调用__get方法 根据对象本身的情况返回不同的值
?>

 

六、__set(),设置一个类的成员变量时调用

__set() 的作用:

set( $property, $value )` 方法用来设置私有属性, 给一个未定义的属性赋值时,此方法会被触发,传递的参数是被设置的属性名和值。

<?php
class Person
{
    private $name;
    public $age;
    public function __construct($name = "", $age = 25)
    {
        $this->name = $name;
        $this->age = $age;
    }
    /**
     *  声明魔术方法需要两个参数,直接为私有属性赋值时自动调用,并可以屏蔽一些非法赋值
     * @param $property
     * @param $value
     */
    public function __set($property, $value)
    {
        $this->$property = $value;
    }
    /**
     * 在类中声明说话的方法,将所有的私有属性说出
     */
    public function say()
    {
        echo "我叫" . $this->name . ",今年" . $this->age . "岁了";
    }
}
$Person = new Person("小明", 20); //注意,初始值将被下面所改变
$Person->name = "小红";
$Person->age = 16;
$Person->say(); //输出:我叫小红,今年16岁了

 

__get__set 这两个魔术方法的区别主要体现在以下几个方面:

  1. 触发时机:

    • __get 方法在尝试获取一个不可访问(不存在、未定义或私有且未通过特定方法访问)的类成员变量时被自动调用。

    • __set 方法在尝试设置一个不可访问(不存在、未定义或私有且未通过特定方法设置)的类成员变量时被自动调用。

  2. 功能:

    • __get 方法主要用于控制对类成员变量的读取操作,您可以在方法内部自定义返回的值或者执行一些特定的逻辑。

    • __set 方法主要用于控制对类成员变量的写入操作,您可以在方法内部决定如何处理这个写入操作,例如进行数据验证、转换或其他相关处理。

例如:

<?php
class MyClass {
    private $privateVar = 10;
​
    public function __get($name) {
        if ($name === 'privateVar') {
            return "无法直接获取私有变量 $name";
        }
        return "变量 $name 不存在";
    }
​
    public function __set($name, $value) {
        if ($name === 'privateVar') {
            echo "无法直接设置私有变量 $name";
        } else {
            echo "设置了不存在的变量 $name 为 $value";
        }
    }
}
​
$obj = new MyClass();
echo $obj->privateVar;  // 触发 __get
echo "<br>";
$obj->newVar = 20;      // 触发 __set
?>

 

七、 __isset(),当对不可访问属性调用isset()或empty()时调用

dissent()的作用:当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用

<?php
class Person
{
    public $sex;
    private $name;
    private $age;
    public function __construct($name="", $age=25, $sex='男')
    {
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }
    /**
     * @param $content
     * @return bool
     */
    public function __isset($content) {
        echo "当在类外部使用isset()函数测定私有成员{$content}时,自动调用<br>";
    }
}
$person = new Person("小明", 25,"女"); // 初始赋值
echo isset($person->sex),"<br>";
echo isset($person->name);
echo isset($person->age);
echo $person->sex."<br>";
//输出为:
1
当在类外部使用isset()函数测定私有成员name时,自动调用
当在类外部使用isset()函数测定私有成员age时,自动调用
女

 

八、 __unset(),当对不可访问属性调用unset()时被调用。

1、 如果一个对象里面的成员属性是公有的,就可以使用这个函数在对象外面删除对象的公有属性。

2、 如果对象的成员属性是私有的,我使用这个函数就没有权限去删除。

  • 虽然有以上两种情况,但我想说的是同样如果你在一个对象里面加上 unset() 这个方法,就可以在对象的外部去删除对象的私有成员属性了。在对象里面加上了 unset() 这个方法之后,在对象外部使用“unset()”函数删除对象内部的私有成员属性时,对象会自动调用 __unset() 函数来帮我们删除对象内部的私有成员属性。

<?php
class Person
{
    public $sex;
    private $name;
    private $age;
    public function __construct($name="小红", $age=25, $sex='男')
    {
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }
    /**
     * @param $content
     *
     * @return bool
     */
    public function __unset($content) {
        echo "当在类外部使用unset()函数测定私有成员{$content}时,自动调用<br>";
    }
​
    public function say()
    {
        echo "我叫" . $this->name . ",今年" . $this->age . "岁了";
    }
}
$person = new Person("小明", 25,"女"); // 初始赋值
echo $person->sex."<br>";
$person->say();echo "<br>";
unset($person->name);
//$person->name();        //报错
​

 

<?php
class MyClass {
    public $publicProperty = "Hello";
}
​
$obj = new MyClass();
unset($obj->publicProperty);
​
// 下面这行会产生错误,因为 $publicProperty 已被删除
echo $obj->publicProperty; 
?>

 

九、__sleep(),执行serialize()时,先会调用这个函数
  • serialize() 函数会检查类中是否存在一个魔术方法 __sleep() 。如果存在,则该方法会优先被调用,然后才执行序列化操作。

  • 此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。

  • 如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。

__sleep() 不能返回父类的私有成员的名字。这样做会产生一个 E_NOTICE 级别的错误。可以用Serializable 接口来替代。

<?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>";
        return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名
        称
}
}
$person = new Person('abc'); // 初始赋值
echo serialize($person);
echo '<br/>';

 

当在类外部使用serialize()时会调用这里的__sleep()方法

__sleep 魔术方法在 PHP 中的主要作用是:
    当对一个对象使用 serialize 函数进行序列化操作时,会自动调用 __sleep 方法。
​
它的作用包括但不限于以下几点:
清理或关闭可能在序列化过程中不需要或无法正确序列化的资源,例如文件句柄、数据库连接等。
选择要序列化的属性。通过返回一个包含属性名称的数组,决定哪些属性将被序列化。这有助于控制序列化的数据量和内容,只保留必要的属性进行存储或传输。
在您给出的示例中,当执行 serialize($person) 时,会调用 __sleep 方法并输出相应的提示信息,然后只序列化 name 和 age 这两个属性。

十、__wakeup(),执行unserialize()时,先会调用这个函数
  • 如果说 __sleep()是白的,那么__wakeup()就是黑的了。

  • 与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用__wakeup 方法,预先准备对象需要的资源。

<?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>";
        return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名
        称
}
    /**
     * __wakeup
     */
    public function __wakeup() {
        echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
        $this->name = 2;
        $this->sex = 'm';
// 这里不需要返回数组
    }
}
$person = new Person('abc'); // 初始赋值
var_dump(serialize($person));
var_dump(unserialize(serialize($person)));

 

__sleep__wakeup 这两个魔术方法的区别主要体现在以下几个方面:

  1. 触发时机:

    • __sleep 方法在对象执行 serialize 序列化操作时被自动调用。

    • __wakeup 方法在对象执行 unserialize 反序列化操作时被自动调用。

  2. 主要用途:

    • __sleep 通常用于在序列化之前进行一些清理或准备工作,例如关闭资源、只选择部分属性进行序列化等。

    • __wakeup 常用于在反序列化之后重新初始化对象的某些状态、重新建立数据库连接或其他需要恢复的资源。

例如:

<?php
class MyObject {
    private $resource;
    public $data;
​
    public function __construct() {
        // 模拟打开一个资源
        $this->resource = fopen('E:\network_security_course\zhibangyang\a.资料\阶段1\g.php基础(深入的没看)\代码1\php_project\php01\somefile.txt', 'r');
        $this->data = 'Some data';
    }
​
    public function __sleep() {
        // 关闭资源
        fclose($this->resource);
        return ['data'];
    }
​
    public function __wakeup() {
        // 重新打开资源
        $this->resource = fopen('E:\network_security_course\zhibangyang\a.资料\阶段1\g.php基础(深入的没看)\代码1\php_project\php01\somefile.txt', 'r');
    }
}
​
$obj = new MyObject();
$serialized = serialize($obj);
echo "序列化后的结果: ". $serialized. "<br>";  // 输出序列化后的结果
​
// 反序列化
$unserialized = unserialize($serialized);
echo "反序列化后的对象数据: ";
print_r($unserialized);  // 输出反序列化后的对象信息
?>

在上述示例中,__sleep 关闭了资源,__wakeup 重新打开了资源。

十一、__toString(),类被当成字符串时的回应方法

作用:

  • __toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。

  • 此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

  • 不能在 __toString() 方法中抛出异常。这么做会导致致命错误。

<?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;
    }
    public function __toString()
    {
        return 'go go go';
    }
}
$person = new Person('小明'); // 初始赋值
echo $person;

那么如果类中没有 __toString() 这个魔术方法运行会发生什么呢?

Catchable fatal error: Object of class Person could not be converted to string in D:\phpStudy\WWW\test\index.php on line 18

 

很明显,页面报了一个致命错误,这是语法所不允许的。

  • 在 PHP 中,__toString 魔术方法用于当对象被当作字符串使用时自动调用。

  • 在您给出的示例中,当执行 echo $person; 时,由于对象 $person 被用于输出,此时就会自动调用 __toString 方法,并输出该方法返回的字符串 go go go

  • __toString 方法的主要作用是允许您自定义对象在需要以字符串形式表示时的输出内容,使得对象在输出时更具有可读性和可用性。例如,您可以在这个方法中返回对象的一些关键属性值的组合,或者任何您希望表示该对象的字符串信息。

十二、__invoke(),调用函数的方式调用一个对象时的回应方法

作用:

  • 当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。 本特性只在 PHP 5.3.0 及以上版本有效。

<?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;
    }
    public function __invoke() {
        echo '这可是一个对象哦';
    }
}
$person = new Person('小明'); // 初始赋值
$person();
?>

 

  • 在 PHP 中,__invoke 魔术方法用于当尝试以函数的方式调用一个对象时被自动调用。

  • 在您给出的示例中,当执行 $person(); 时,就会自动调用 __invoke 方法,并输出 这可是一个对象哦

  • __invoke 方法使得对象可以像函数一样被调用,为对象的使用提供了更多的灵活性和便利性。例如,您可以在这个方法中实现一些特定的逻辑或操作,使得对象在特定场景下具有类似函数的行为。

十三、__clone(),当对象复制完成时调用

在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 GTK 窗口对象,该对象持有窗口相关的资源。你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。还有一种情况:如果对象 A 中保存着对象 B 的引用,当你复制对象 A 时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象 A 的一个副本。

对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的__clone() 方法)。对象中的__clone()方法不能被直接调用。

<?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;
    }
    public function __clone()
    {
        echo __METHOD__."你正在克隆对象<br>";
    }
}
$person = new Person('小明'); // 初始赋值
$person2 = clone $person;
var_dump('persion1:');
echo '<br>';
var_dump($person);
echo '<br>';
var_dump('persion2:');
echo '<br>';
var_dump($person2);

在 PHP 中,__clone 魔术方法用于在对象被克隆时自动调用。

  • 在您给出的示例中,当执行 $person2 = clone $person; 时,就会自动调用 __clone 方法,并输出 Person::__clone 你正在克隆对象<br>

  • __clone 方法的主要作用是允许您在对象克隆时进行一些自定义的操作。例如,您可以在这个方法中处理一些特殊的属性,比如对于引用类型的属性进行特殊处理,或者重置一些属性的值,以确保克隆后的对象具有正确的状态。

  • 在默认情况下,对象的克隆是浅克隆,即基本数据类型的属性会被复制,而引用类型的属性仍然指向相同的内存地址。通过 __clone 方法,您可以实现深克隆,即完全独立地复制引用类型的属性。

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值