目录

  1. 1. __invoke():
  2. 2. __construct():
  3. 3. __destruct():
  4. 4. __wakeup():
  5. 5. __sleep():
  6. 6. __toString():
  7. 7. __set():
  8. 8. __get():
  9. 9. __call():
  10. 10. __isset()
  11. 11. __unset()
  12. 12. __serialize()
  13. 13. __unserialize()
  14. 14. ::
  15. 15. __clone()

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

PHP魔术方法

2023/4/30 Web 反序列化 PHP
  |     |   总文章阅读量:

个人对php魔术方法的总结

官方文档

__invoke():

当尝试以调用函数的方式(如eval:代码执行)调用对象的时候,就会调用该方法

<?php
class InvokeDemo
{
    public function __construct()
    {
        echo '我被创建了'.PHP_EOL;
    }

    public function __invoke()
    {
        // TODO: Implement __invoke() method.
        echo '触发invoke'.PHP_EOL;
    }
}
$invokeDemo = new InvokeDemo();
print_r($invokeDemo());
	输出:我被创建了
         触发invoke

__construct():

具有构造函数的类在创建新对象的时候,回调此方法

eg:

<?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 = new Person();
echo $Person1->say(); //输出:我叫:,性别:男,年龄:22

$Person2 = new Person("1998");
echo $Person2->say(); //输出:我叫:1998,性别:男,年龄:22

$Person3 = new Person("李四","男",25);
echo $Person3->say(); //输出:我叫:李四,性别:男,年龄:25

__destruct():

反序列化的时候,或者对象销毁的时候调用

__wakeup():

执行unserialize()时,先会调用这个函数

  • 当序列化字符串中表示对象属性个数的值大于真实的属性个数会跳过__wakeup的执行(CVE-2016-7124)(版本:PHP5 < 5.6.25 PHP7 < 7.0.10)
  • 如果__wakeup内有属性在创建时尚未设置,则不会被调用

__sleep():

执行serialize()时,先会调用这个函数

  • 不能返回父类的私有成员的名字

__toString():

把类当成字符串的时候调用,当使用echo或者print输出对象时,将对象转化成字符串

<?php

class people{
    public function __toString(){
        return "I am toString module";
    }
}
$peo = new people();
echo $peo;
?>
    
// 输出:I am toString module

注:该方法需要return返回字符串,否则会报错,不过也不影响

  • 对一个对象进行echo操作或者print操作会触发__toString
  • 声明的变量赋值为对象后与字符串做弱类型比较的时候就能触发__toString
  • 声明的变量赋值为对象后进行正则匹配的时候就能触发__toString
  • 声明的变量被赋值为对象后进行strolower的时候就能触发__toString

__set():

在给不可访问的(protected或者private)或者不存在的属性赋值的时候,会被调用

<?php
class Test{
    private $data;
    public function __set($name, $value) 
    {
        echo "Setting '$name' to '$value'\n";
    }
}
$obj = new Test;
$obj->a = 1;

// 输出
// Setting 'a' to '1'

__get():

读取(例如echo)不可访问或者不存在的属性的时候,进行赋值

<?php
class Test
{
    private $data;
    public function __set($name, $value)
    {
        echo "Setting '$name' to '$value'\n";
    }
    public function __get($name)
    {
        echo "Getting '$name'\n";
    }
}
$obj = new Test;
$obj->a = 1;
echo $obj->a;

// 输出
// Setting 'a' to '1'
// Getting 'a'

__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();

输出:
你所调用的函数:run(参数:Array ( [0] => teacher ) )不存在!
你所调用的函数:eat(参数:Array ( [0] => 小明 [1] => 苹果 ) )不存在!
Hello, world!

可以看出来这里的$args本质是一个数组

__isset()

当对不可访问属性调用 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}时,自动调用";
        echo isset($this->$content);
    }
}
$person = new Person("小明", 25); // 初始赋值
echo isset($person->sex), "";
echo isset($person->name), "";
echo isset($person->age), "";

// public 可以 isset()
>>>1当在类外部使用isset()函数测定私有成员name时,自动调用1 // __isset() 内 第一个echo
// __isset() 内第二个echo

__unset()

当对不可访问(protected 或 private)或不存在的属性调用unset()时,__unset()会被调用


__serialize()

serialize()函数会检查类中是否存在一个魔术方法__serialize()。如果存在,该方法将在任何序列化之前优先执行

PHP>=7.4

  • 如果类中同时定义了__serialize()和__sleep()两个魔术方法,则只有__serialize()方法会被调用,__sleep()方法会被忽略掉

__unserialize()

unserialize()检查是否存在具有名为__unserialize()的魔术方法

PHP>=7.4

  • 如果类中同时定义了__unserialize()和__wakeup()两个魔术方法,则只有__unserialize()方法会被调用,__wakeup()方法会被忽略掉

::

类名::方法名
//外部调用类方法,或参数。只需要去找到::后面的方法即可,直接调用

__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 = 'C1oudfL0w0';
            $this -> url  = 'https://c1oudfl0w0.github.io/blog/';
        }
    }

    $obj  = new Website('Arcaea', 'https://arcaea.lowiro.com/zh');
    $obj2 = clone $obj;
    $obj  -> output();
    $obj2 -> output();
?>

// Arcaea,https://arcaea.lowiro.com/zh
// C1oudfL0w0,https://c1oudfl0w0.github.io/blog/

如果在类中设置一个空的,访问权限为private的 __clone() 方法的话,可以起到禁止克隆的作用