PHP魔术方法使用

2020-12-22  本文已影响0人  爱折腾的傻小子

支持魔术方法

class MyClass
{
    public function __construct() {}

    public function __destruct() {}

    public function __call() {}

    public function __callStatic() {}

    public function __get() {}

    public function __set() {}

    public function __isset() {}

    public function __unset() {}

    public function __sleep() {}

    public function __wakeup() {}

    public function __toString() {}

    public function __invoke() {}

    public function __set_state() {}

    public function __clone() {}

    public function __debuginfo() {}
}

__construct 构造方法

<?php
class A {
    // 构造方法
    public function __construct() {
        echo "This is A construct\n";
    }
}

class B extends A{
    // 调用父类构造方法,再调用自己的构造方法
    public function __construct() {
        parent::__construct();
        echo "This is B construct\n";
    }
}

class C extends A{
    // 重写构造方法,之调用自己的构造方法
    public function __construct() {
        echo "This is C construct";
    }
}

new A();  // This is C construct

new B();  // This is A construct\n This is B construct\n 

new C();  // This is C construct

__destruct 析构方法

<?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;
  }

  // 成员方法
  public function say() 
  {
    echo "xxxxxxxxxxxxxxxxxxxx";
  }

  // 析构方法
  public function __destruct()
  {
    echo "我正在被注销,需要关闭些什么吗?";
  }
}

// 测试一
$person = new Person("小明");
$person->say();  // xxxxxxxxxxxxxxxxxxxx
unset($person);  // 我正在被注销,需要关闭些什么吗?
echo 123;        // 123

// 测试二
$person1 = new Person("小明");
$person1->say();   // xxxxxxxxxxxxxxxxxxxx
$person1 = null;   // 我正在被注销,需要关闭些什么吗?
echo 123;          // 123

__call 调用一个不可访问的方法时被调用。

// 参数 $function_name 会自动接收不存在的方法名
// 参数 $arguments 则以数组的方式接收不存在方法的多个参数
public function __call(string $function_name, array $arguments)
{
    // 方法体
}
<?php
class Person
{                            
  public function say()
  { 
     echo "Hello, world!";
  }            

  protected function isRed(){
     echo "yes is Red\n";
  }

  /**
   * 声明此方法用来处理调用对象中不存在的方法
   */
  public function __call($funName, $arguments)
  {
     echo "你所调用的函数:" . $funName . "(参数:" ;  // 输出调用不存在的方法名
     print_r($arguments); // 输出调用不存在的方法时的参数列表
     echo ")不存在!\n"; // 结束换行                     
   }                                         
}

$Person = new Person();           
$Person->run("teacher"); 
/*
你所调用的函数:run(参数:Array
(
    [0] => teacher
)
)不存在!
*/
$Person->eat("小明", "苹果");            
/*
你所调用的函数:eat(参数:Array
(
    [0] => 小明
    [1] => 苹果
)
)不存在!
*/
$Person->say();  // Hello, world!
$Person->isRed();  // 改方法是存在但是是私有的
/*
你所调用的函数:isRed(参数:Array
(
)
)不存在!
*/

__callStatic 静态调用一个不可访问的方法时被调用

<?php

class Person
{
  // 成员方法
  public function say()
  {
    echo "xoxoxoxoxooxox";
  }  

  // 私有静态方法 
  private static function s()
  {
    echo "1s1";
  }

  // __callStatic 魔术方法
  public static function __callStatic($funName, $arg)
  {
    echo "你所调用的静态方法:" . $funName . "(参数:" ;  // 输出调用不存在的方法名
    print_r($arguments); // 输出调用不存在的方法时的参数列表
    echo ")不存在!\n"; // 结束换行
  }
}

$Person = new Person();  
$Person::run("teacher");     // 你所调用的静态方法:run(参数:)不存在!
Person::run('weee');         // 你所调用的静态方法:run(参数:)不存在!
$Person::eat("小明", "苹果"); // 你所调用的静态方法:eat(参数:)不存在!
$Person::s();                // 你所调用的静态方法:s(参数:)不存在!
Person::s();                 // 你所调用的静态方法:s(参数:)不存在!
$Person->say();              // xoxoxoxoxooxox

__get 获得一个类的成员变量时调用

<?php

class Person
{
  private $name;
  private $age;

  public function __construct($name="", $age=1)
  {
      $this->name = $name;
      $this->age = $age;
  }

  // 在直接获取属性值时自动调用一次,以属性名作为参数传入并处理
  // $propertyName 不存在的属性名称
  public function __get($propertyName)
  {
    if ($propertyName == "age") {
       if ($this->age > 30) {
         return $this->age - 10;
       } else {
         return $this->$propertyName;
       }
    } else {
      return $this->$propertyName;
    }
  }
}

$Person = new Person("小明", 60);   
echo "姓名:" . $Person->name . "";   // 姓名:小明
echo "年龄:" . $Person->age . "";    // 年龄:50

__set 设置一个类的成员变量时调用

<?php

class Person
{
    private $name;
    private $age;

    public function __construct($name="",  $age=25)
    {
        $this->name = $name;
        $this->age  = $age;
    }

    // 模式方法 __set
    // $property 被设置的属性 
    // $value 设置的值
    public function __set($property, $value) 
    {
      if ($property=="age")
        {
            if ($value > 150 || $value < 0) {
                return;
            }
        }
        $this->$property = $value;
    }

    // 成员方法
    public function say()
    {
      echo "我叫".$this->name.",今年".$this->age."岁了";
    }
}

$Person=new Person("小明", 25);
$Person->say();     // 我叫小明,今年25岁了
$Person->name = "小红";
$Person->age = 16;
$Person->age = 160; 
$Person->say();     // 我叫小红,今年16岁了

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

<?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;
    }

    // $content 
    public function __isset($content) 
    {
      echo "当在类外部使用isset()函数测定私有成员{$content}时,自动调用\n";
      echo  isset($this->$content)."\n";
    }

    // 成员方法
    public function say()
    {
      echo "我叫".$this->name.",今年".$this->age."岁了";
    }
}

$person = new Person("小明", 25); // 初始赋值

echo isset($person->sex),"\n";    // 1

echo isset($person->name),"\n"; // 这里打印的是空串 魔术方法没有返回值的原因
/*
当在类外部使用isset()函数测定私有成员name时,自动调用
1

*/
echo isset($person->age),"\n";// 这里打印的是空串 魔术方法没有返回值的原因
/*
当在类外部使用isset()函数测定私有成员age时,自动调用
1

*/

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

class Person
{
    public $sex;
    private $name;
    private $age;

    public function __construct($name="",  $age=25, $sex='男')
    {
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    // $content 
    public function __unset($content) 
    {
      echo "当在类外部使用unset()函数来删除私有成员时自动调用的\n";
      echo  isset($this->$content);
    }

    // 成员方法
    public function say()
    {
      echo "我叫".$this->name.",今年".$this->age."岁了";
    }
}

$person = new Person("小明", 25); // 初始赋值
unset($person->sex);
unset($person->name);   
/*
当在类外部使用unset()函数来删除私有成员时自动调用的
1
*/
unset($person->age);
/*
当在类外部使用unset()函数来删除私有成员时自动调用的
1
*/

__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;
    }

    // $content 
    public function __sleep() 
    {
      echo "当在类外部使用serialize()时会调用这里的__sleep()方法\n";
      $this->name = base64_encode($this->name);
      return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称
    }

    // 成员方法
    public function say()
    {
      echo "我叫".$this->name.",今年".$this->age."岁了";
    }
}

$person = new Person('小明'); // 初始赋值
echo serialize($person);
/*
当在类外部使用serialize()时会调用这里的__sleep()方法
O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}
*/

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

<?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;
    }

    // $content 
    public function __sleep() 
    {
      echo "当在类外部使用serialize()时会调用这里的__sleep()方法\n";
      $this->name = base64_encode($this->name);
      return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属性名称
    }
    
    public function __wakeup() {

        echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法";

        $this->name = 2;

        $this->sex = '男';

        // 这里不需要返回数组
    }

    // 成员方法
    public function say()
    {
      echo "我叫".$this->name.",今年".$this->age."岁了";
    }
}

$person = new Person('小明'); // 初始赋值
var_dump(serialize($person));
/*
当在类外部使用serialize()时会调用这里的__sleep()方法
string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}"
*/
var_dump(unserialize(serialize($person)));
/*
当在类外部使用serialize()时会调用这里的__sleep()方法
当在类外部使用unserialize()时会调用这里的__wakeup()方法
object(Person)#2 (3) {
  ["sex"]=>
  string(3) "男"
  ["name"]=>
  int(2)
  ["age"]=>
  int(25)
}
*/

__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 '这里必须返回一个字符串';
    }
}

$person = new Person('小明'); // 初始赋值

echo $person;   // 这里必须返回一个字符串
<?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;
    }
}

$person = new Person('小明'); // 初始赋值

echo $person;   // 这里必须返回一个字符串
/*
Catchable fatal error: Object of class Person could not be converted to string in /usercode/file.php on line 19
*/

__invoke 调用函数的方式调用一个对象时的回应方法

<?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

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('小明'); // 初始赋值
$person();
/*
Fatal error: Uncaught Error: Function name must be a string in /usercode/file.php:18
Stack trace:
#0 {main}
  thrown in /usercode/file.php on line 18
*/

__set_state 调用var_export()导出类时,此静态方法会被调用。

<?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;
    }
}

$person = new Person('小明'); // 初始赋值
var_export($person);
/*
Person::__set_state(array(
   'sex' => '男',
   'name' => '小明',
   'age' => 25,
))
*/
<?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 static function __set_state($an_array)
    {
        $a = new Person();
        $a->name = $an_array['name'];
        return $a;
    }
}

$person = new Person('小明'); // 初始赋值
$person->name = '小红';
var_export($person);
/*
Person::__set_state(array(
   'sex' => '男',
   'name' => '小红',
   'age' => 25,
))
*/

__clone

<?php

class Person
{
    private $sex;
    private $name;
    private $age;

    // 构造方法
    public function __construct($name="",  $age=25, $sex='男')
    {
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }
    
    // 成员方法
    public function say()
    {
        echo "我的名字叫:".$this->name;
        echo " 我的年龄是:".$this->age."<br />";
    }
    
    public function __clone()
    {
        $this->name = "我是假的".$this->name;
        $this->age = 30;
    }
}

$p1 = new Person("张三", 20);
$p1->say();         // 我的名字叫:张三 我的年龄是:20
$p2 = clone $p1;
$p2->say();         // 我的名字叫:我是假的张三 我的年龄是:30
<?php

class SingetonBasic
{
    // 静态变量要私有化,防止类外修改,保存对象实例
    private static $instance;
    
    // 构造函数私有化,类外不能直接新建对象
    private function __construct(){}
    
    // 禁止使用关键字clone
    private function __clone(){}
    
    // 外部开放方法创建对象
    public static function getInstance() 
    {
        if (! self::$instance instanceof self) {
            self::$instance = new self();
        }
        
        return self::$instance;
    }
}

$a = SingetonBasic::getInstance();
$b = SingetonBasic::getInstance();
var_dump($a === $b);  //结果为:boolean true   a和b指向的是同一个对象

$c = clone $a;  // 报错
/*
Fatal error: Uncaught Error: Call to private SingetonBasic::__clone() from context '' in /usercode/file.php:29 Stack trace: #0 {main} thrown in /usercode/file.php on line 29
*/
<?php

class B
{
    public $val = 10;
}

class A
{
    public $val = 20;
    public $b;
    
    public function __construct()
    {
        $this->b = new B();
    }
}

$oa = new A();
$ob = clone $oa;
$oa->val = 30;
$oa->b->val = 40;
echo "<pre>";
var_dump($oa);
/*
object(A)#1 (2) {
  ["val"]=>
  int(30)
  ["b"]=>
  object(B)#2 (1) {
    ["val"]=>
    int(40)
  }
}
*/
var_dump($ob);
/*
object(A)#3 (2) {
  ["val"]=>
  int(20)
  ["b"]=>
  object(B)#2 (1) {
    ["val"]=>
    int(40)          // 这里本应该是10 结果出现40导致克隆不完全 $this->b 还用引用 A对象
  }
}
*/
<?php

class B
{
    public $val = 10;
}

class A
{
    public $val = 20;
    public $b;
    
    public function __construct()
    {
        $this->b = new B();
    }
    
    public function __clone()
    {
        $this->b = clone $this->b;
    }
}

$oa = new A();
$ob = clone $oa;
$oa->val = 30;
$oa->b->val = 40;
echo "<pre>";
var_dump($oa);
/*
object(A)#1 (2) {
  ["val"]=>
  int(30)
  ["b"]=>
  object(B)#2 (1) {
    ["val"]=>
    int(40)
  }
}
*/
var_dump($ob);
/*
object(A)#3 (2) {
  ["val"]=>
  int(20)
  ["b"]=>
  object(B)#4 (1) {
    ["val"]=>
    int(10)
  }
}
*/

__debuginfo 打印所需调试信息


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) }
*/
上一篇下一篇

猜你喜欢

热点阅读