以双下划线(__)开头的方法称为魔术方法

-__construct():类的构造方法;

-__destruct():类的析构方法;

-__call($funName, $arguments):当访问未定义或没有访问权限的方法时,__call()会被调用;

-__callStatic($funName, $arguments):当访问未定义或没有访问权限的静态方法时,__call()会被调用;

-__get($propertyName):读取类的成员变量时__get()会被调用;

-__set($property, $value):写入类的成员变量时__set()会被调用;

-__isset($content):当针对未定义或没有访问权限的成员使用isset()或empty()时__isset()会被调用;

-__unset($content):在未定义或没有访问权限的成员上使用reset()时__unset()会被调用;

-__sleep():在执行serialize()时__sleep()会被调用;

-__wakeup():在执行deserialization()时__wakeup()会被调用;

-__toString():使用echo方法直接输出对象时,__toString()会被调用;

-__invoke():像调用函数一样调用对象时,对象的__invoke()会被调用;

-__set_state($an_array):调用var_export()时__set_state()会被调用;

-__clone():复制对象时__clone()会被调用;

-__autoload($className):试图加载未定义的类;

-__debuginfo():输出调试信息。
1. __construct()

PHP构造方法是对象创建之后自动调用的第一个方法。任何类都有构造方法。如果没有显式定义,那么类会有个默认的构造方法,该方法没有参数,方法体为空。

1) 构造方法的用法
构造函数通常用来执行初始化工作,如在创建对象时设置成员变量的初始值。 2) 声明类的构造方法的格式
function __construct([参数列表]){
方法体 // 通常用于设置成员变量的初始值
}
注意:同一个类只能有一个构造方法,因为PHP不支持构造方法重载。 完整的示例如下:
<?php
class Person
{
public $name;
public $age;
public $sex;
/**
* 显示定义带有参数的构造方法
*/
public function __construct($name="", $sex="Male", $age=22)
{
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
/**
* say方法
*/
public function say()
{
echo "Name:" . $this->name . ",Sex:" . $this->sex . ",Age:" . $this->age;
}
}
不使用任何参数创建对象$Person1。
$Person1 = new Person();
echo $Person1->say(); //输出: Name:,Sex:Male,Age:22 使用参数"James"创建对象$Person2。
$Person2 = new Person("Jams");
echo $Person2->say(); // 输出: Name: Jams, Sex: Male, Age: 22 使用三个参数创建$Person3。
$Person3 = new Person ("Jack", "Male", 25);
echo $Person3->say(); // 输出: Name: Jack, Sex: Male, Age: 25 2. __destruct() 现在我们知道了构造方法,那么相对的就是析构方法。
析构方法可以在对象销毁之前执行一些操作,如关闭文件、清空结果集,等等。
析构方法是PHP5引入的新特性。 析构方法的声明格式与构造方法 __construct() 类似,就是说__destruct()也以双下划线开头,其名称也是固定的。
1) 析构方法的声明格式
function __destruct()
{
// 方法体
}
注意:析构方法不能带任何参数。 2) 析构方法的用法 一般来说,PHP中析构方法并不是太常用。在类中它是可选的,通常用于在对象销毁之前执行某些清理工作。 下面的例子演示了如何使用析构方法: <?php
class Person{
public $name;
public $age;
public $sex;
public function __construct($name="", $sex="Male", $age=22)
{
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
/**
* say方法
*/
public function say()
{
echo "Name:".$this->name.",Sex:".$this->sex.",Age:".$this->age;
}
/**
* 定义析构方法
*/
public function __destruct()
{
echo "Well, my name is ".$this->name;
}
}
$Person = new Person("John");
unset($Person); // 销毁上面创建的$Person对象
以上程序的输出结果为:
Well, my name is John
3. __call() 该方法有两个参数。第一个参数$function_name自动接收未定义方法的名称,第二个参数$arguments以数组的方式接收该方法调用的多个参数。 1) __call()方法的用法
function __call(string $function_name, array $arguments)
{
// 方法体
}
程序中调用未定义的方法时,__call()方法会自动被调用。 示例如下:
<?php
class Person
{
function say()
{
echo "Hello, world!<br>";
}
function __call($funName, $arguments)
{
echo "The function you called:" . $funName . "(parameter:" ; // 输出不存在的方法的名称
print_r($arguments); // 输出不存在的方法的参数列表
echo ")does not exist!!<br>\n";
}
}
$Person = new Person();
$Person->run("teacher"); // 如果对象内不存在的方法被调用,则 __call() 方法会被自动调用
$Person->eat("John", "apple");
$Person->say();
输出结果如下: The function you called: run (parameter: Array([0] => teacher)) does not exist!
The function you called: eat (parameter: Array([0] => John[1] => apple)) does not exist!
Hello world! 4. __callStatic()
当程序中调用未定义的静态方法时,__callStatic()方法会被调用。
__callStatic()的用法与__call()类似。示例如下:
<?php
class Person
{
function say()
{
echo "Hello, world!<br>";
}
public static function __callStatic($funName, $arguments)
{
echo "The static method you called:" . $funName . "(parameter:" ; // 输出不存在的方法的名称
print_r($arguments); // 输出不存在的方法的参数列表
echo ")does not exist!<br>\n";
}
}
$Person = new Person();
$Person::run("teacher"); // 如果对象内不存在的方法被调用,则 __callStatic() 方法会被自动调用
$Person::eat("John", "apple");
$Person->say();
输出结果如下: The static method you called: run (parameter: Array([0] => teacher)) does not exist!
The static method you called: eat (parameter: Array([0] => John[1] => apple)) does not exist!
Hello world! 5. __get()
当试图访问外部对象的私有属性时,程序会抛出异常并结束执行。但我们可以使用__get()方法来解决这个问题。它能在对象外部取得对象的私有方法。示例如下:
<?php
class Person
{
private $name;
private $age;
function __construct($name="", $age=1)
{
$this->name = $name;
$this->age = $age;
}
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("John", 60); // 用Person类初始化对象,并通过构造方法给属性赋初始值
echo "Name:" . $Person->name . "<br>"; // 访问私有属性时, __get() 方法会自动被调用,这样就能间接取得属性值
echo "Age:" . $Person->age . "<br>"; // __get() 方法自动被调用,并返回不同的值
输出结果如下: Name: John
Age: 50 6. __set() __set($property, $value) 方法用来设置对象的私有属性。当试图设置对象中未定义的属性时,就会触发__set()方法,调用参数为被设置的属性名和属性值。 示例代码如下: <?php
class Person
{
private $name;
private $age;
public function __construct($name="", $age=25)
{
$this->name = $name;
$this->age = $age;
}
public function __set($property, $value) {
if ($property=="age")
{
if ($value > 150 || $value < 0) {
return;
}
}
$this->$property = $value;
}
public function say(){
echo "My name is ".$this->name.",I'm ".$this->age." years old";
}
}
$Person=new Person("John", 25); // 注意下面的代码会改变初始值
$Person->name = "Lili"; // "name" 属性成功赋值。如果没有 __set() 方法,程序就会抛出异常
$Person->age = 16; // "age" 属性成功赋值
$Person->age = 160; // 160 是个非法值,所以赋值失败
$Person->say(); // 输出:My name is Lili, I'm 16 years old.
下面是输出结果: My name is Lili, I'm 16 years old 7. __isset() 在介绍__isset()方法之前,我先介绍下isset()方法。isset()方法主要用于判断某个变量是否被设置。 在对象外部使用isset()方法有两种情况: 如果参数是公有属性,那么可以利用isset()方法判断属性是否被设置; 如果参数是私有属性,isset()方法将无法使用。 那么,是否有办法判断私有属性被设置呢?当然,只需要在类里定义__isset()方法,就可以在对象外部利用isset()方法判断某个私有属性是否被设置了。 对未定义或没有权限访问的属性调用isset()或empty()时,就会调用__isset()方法。示例如下: <?php
class Person
{
public $sex;
private $name;
private $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @param $content
*
* @return bool
*/
public function __isset($content) {
echo "The {$content} property is private,the __isset() method is called automatically.<br>";
echo isset($this->$content);
}
}
$person = new Person("John", 25); // 赋初始值
echo isset($person->sex),"<br>";
echo isset($person->name),"<br>";
echo isset($person->age),"<br>";
输出结果如下: 1
The name property is private,the __isset() method is called automatically.
1
The age property is private,the __isset() method is called automatically.
1 8. __unset() 与__isset()类似,在未定义或无权限访问的属性上调用unset()方法时会触发__unset()方法。示例如下: <?php
class Person
{
public $sex;
private $name;
private $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @param $content
*
* @return bool
*/
public function __unset($content) {
echo "It is called automatically when we use the unset() method outside the class.<br>";
echo isset($this->$content);
}
}
$person = new Person("John", 25); // 赋初始值
unset($person->sex),"<br>";
unset($person->name),"<br>";
unset($person->age),"<br>";
输出结果如下: It is called automatically when we use the unset() method outside the class.
1
It is called automatically when we use the unset() method outside the class.
1
9. __sleep() serialize()方法会检查类中是否存在__sleep()魔术方法。如果存在,就会调用该方法来执行序列化操作。 __sleep()方法通常用来在保存数据之前指定哪些属性需要被序列化。如果对象中包含一些完全不需要序列化的巨大对象,__sleep()就能派上用场了。 具体用法请参考以下代码: <?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @return array
*/
public function __sleep() {
echo "It is called when the serialize() method is called outside the class.<br>";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 返回值中的元素必须是属性的名称
}
}
$person = new Person('John'); // Initially assigned.
echo serialize($person);
echo '<br/>';
输出结果如下: It is called when the serialize() method is called outside the class.
O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;} 10. __wakeup() 与__sleep()方法相对的就是__wakeup()方法,常用来反序列化,如重建数据连接,或执行其他初始化操作等。 示例如下: <?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
/**
* @return array
*/
public function __sleep() {
echo "It is called when the serialize() method is called outside the class.<br>";
$this->name = base64_encode($this->name);
return array('name', 'age'); // 返回值中的元素必须是属性的名称
}
/**
* __wakeup
*/
public function __wakeup() {
echo "It is called when the unserialize() method is called outside the class.<br>";
$this->name = 2;
$this->sex = 'Male';
// 这里不需要返回数组
}
}
$person = new Person('John'); // 赋初始值
var_dump(serialize($person));
var_dump(unserialize(serialize($person)));
输出结果如下: It is called when the serialize() method is called outside the class.
string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}"
It is called when the unserialize() method is called outside the class.
object(Person)#2 (3) { ["sex"]=> string(3) "Male" ["name"]=> int(2) ["age"]=> int(25) } 11. __toString() 使用echo方法直接输出对象时会调用其__toString()方法。 注意:该方法必须返回字符串,否则会抛出"E_RECOVERABLE_ERROR"级别的异常。在__toString()方法中也不能抛出异常。 示例如下: <?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __toString()
{
return 'go go go';
}
}
$person = new Person('John'); // 赋初始值
echo $person;
返回结果如下: go go go
如果类中没有定义__toString()会怎样?我们来试试看。 <?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
}
$person = new Person('John'); // 赋初始值
echo $person;
返回结果如下: Catchable fatal error: Object of class Person could not be converted to string in D:\phpStudy\WWW\test\index.php on line 18
可见,它会在页面上报告致命错误,说明这种用法不允许。 12. __invoke() 当试图用调用函数的方式调用对象时,就会自动调用其__invoke()方法。 注意:该功能只在PHP 5.3.0以及以上版本上有效。 示例如下: <?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __invoke() {
echo 'This is an object';
}
}
$person = new Person('John'); // 赋初始值
$person();
输出结果如下: This is an object
如果在未定义__invoke()方法的情况下将对象作为函数使用,就会得到以下的结果: Fatal error: Function name must be a string in D:\phpStudy\WWW\test\index.php on line 18 13. __set_state() 从PHP 5.1.0开始,__set_state()方法会在调用var_export()导出类代码时自动被调用。
__set_state()方法的参数是个数组,包含所有属性的值,格式为array('property' => value, ...)。 下面的示例中没有定义__set_state()方法: <?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
}
$person = new Person('John'); // 赋初始值
var_export($person);
输出结果如下:
Person::__set_state(array( 'sex' => 'Male', 'name' => 'John', 'age' => 25, ))
可见,输出结果是对象的属性。
下面来看看如果定义了__set_state()方法会怎样: <?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='Male')
{
$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('John'); // 赋初始值
$person->name = 'Jams';
var_export($person);
输出结果如下:
Person::__set_state(array( 'sex' => 'Male', 'name' => 'Jams', 'age' => 25, )) 14. __clone() PHP中可以使用clone关键字来复制对象,其格式如下: $copy_of_object = clone $object;
但是,clone关键字只会进行浅复制,所有引用的属性依然会指向原来的变量。 如果对象里定义了__clone()方法,那么复制时就会调用__clone()方法,从而允许我们修改被复制的值(如果需要的话)。 示例如下: <?php
class Person
{
public $sex;
public $name;
public $age;
public function __construct($name="", $age=25, $sex='Male')
{
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
public function __clone()
{
echo __METHOD__."your are cloning the object.<br>";
}
}
$person = new Person('John'); // 赋初始值
$person2 = clone $person;
var_dump('persion1:');
var_dump($person);
echo '<br>';
var_dump('persion2:');
var_dump($person2);
输出结果如下: Person::__clone your are cloning the object.
string(9) "persion1:" object(Person)#1 (3) { ["sex"]=> string(3) "Male" ["name"]=> string(6) "John" ["age"]=> int(25) }
string(9) "persion2:" object(Person)#2 (3) { ["sex"]=> string(3) "Male" ["name"]=> string(6) "John" ["age"]=> int(25) } 15. __autoload() __autoload()方法可以尝试加载未定义的类。 以前,如果在整个程序的生命周期内创建100个对象,就要用include()或require()包含100个类文件,或者在同一个类文件内定义100个类,例如: /**
* file non_autoload.php
*/
require_once('project/class/A.php');
require_once('project/class/B.php');
require_once('project/class/C.php');
.
.
.
if (ConditionA) {
$a = new A();
$b = new B();
$c = new C();
// …
} else if (ConditionB) {
$a = newA();
$b = new B();
// …
}
那么使用__autoload()方法呢? /**
* 文件 autoload_demo.php
*/
function __autoload($className) {
$filePath = “project/class/{$className}.php”;
if (is_readable($filePath)) {
require($filePath);
}
}
if (ConditionA) {
$a = new A();
$b = new B();
$c = new C();
// …
} else if (ConditionB) {
$a = newA();
$b = new B();
// …
}
当PHP引擎第一次使用类A时,如果A没有找到,就会调用__autoload方法,参数为类名"A"。然后我们需要在__autoload()方法中根据类名找到相应的类文件并包含该文件。如果文件没有找到,PHP引擎就会抛出异常。 16. __debugInfo() 执行var_dump()方法的时候会调用__debugInfo()方法。如果__debugInfo()没有定义,则var_dump()方法会输出对象中的所有属性。 示例如下: <?php
class C {
private $prop;
public function __construct($val) {
$this->prop = $val;
}
/**
* @return array
*/
public function __debugInfo() {
return [
'propSquared' => $this->prop ** 2,
];
}
}
var_dump(new C(42));
输出结果如下:
object(C)#1 (1) { ["propSquared"]=> int(1764) }
注意:__debugInfo()方法只能用于PHP 5.6.0及更高版本。
总结
上面介绍了16个PHP魔术方法,其中最常用的有__set()、__get()和__autoload()。如果你还有问题,可以从PHP官方网站上获得帮助。 原文:https://www.tutorialdocs.com/article/16-php-magic-methods.html

1. __construct()

PHP构造方法是对象创建之后自动调用的第一个方法。任何类都有构造方法。如果没有显式定义,那么类会有个默认的构造方法,该方法没有参数,方法体为空。

1) 构造方法的用法

构造函数通常用来执行初始化工作,如在创建对象时设置成员变量的初始值。

2) 声明类的构造方法的格式

function __constrct([参数列表]){
   方法体 // 通常用于设置成员变量的初始值
}

注意:同一个类只能有一个构造方法,因为PHP不支持构造方法重载。

完整的示例如下:

<?php
   class Person
   {                                                                    
           public $name;      
           public $age;      
           public $sex;      
       /**
        * 显示定义带有参数的构造方法
        */                                                                                      
       public function __construct($name="", $sex="Male", $age=22)
       {    
           $this->name = $name;
           $this->sex = $sex;
           $this->age = $age;
       }
       /**
        * say方法
        */
       public function say()
       {
           echo "Name:" . $this->name . ",Sex:" . $this->sex . ",Age:" . $this->age;
       }  
   }

不使用任何参数创建对象$Person1。

$Person1 = new Person();
echo $Person1->say(); //输出: Name:,Sex:Male,Age:22

使用参数"James"创建对象$Person2。

$Person2 = new Person("Jams");
echo $Person2->say(); // 输出: Name: Jams, Sex: Male, Age: 22

使用三个参数创建$Person3。

$Person3 = new Person ("Jack", "Male", 25);
echo $Person3->say(); // 输出: Name: Jack, Sex: Male, Age: 25

2. __destruct()

现在我们知道了构造方法,那么相对的就是析构方法。

析构方法可以在对象销毁之前执行一些操作,如关闭文件、清空结果集,等等。

析构方法是PHP5引入的新特性。

析构方法的声明格式与构造方法 __construct() 类似,就是说__destruct()也以双下划线开头,其名称也是固定的。

1) 析构方法的声明格式

function __destruct()
{
   // 方法体
}

注意:析构方法不能带任何参数。

2) 析构方法的用法

一般来说,PHP中析构方法并不是太常用。在类中它是可选的,通常用于在对象销毁之前执行某些清理工作。

下面的例子演示了如何使用析构方法:

<?php
class Person{    
   public $name;        
   public $age;        
   public $sex;        
   public function __construct($name="", $sex="Male", $age=22)
   {  
       $this->name = $name;
       $this->sex  = $sex;
       $this->age  = $age;
   }
   /**
    * say方法
    */
   public function say()
   {
       echo "Name:".$this->name.",Sex:".$this->sex.",Age:".$this->age;
   }  
   /**
    * 定义析构方法
    */
   public function __destruct()
   {
           echo "Well, my name is ".$this->name;
   }
}
$Person = new Person("John");
unset($Person); // 销毁上面创建的$Person对象

以上程序的输出结果为:

Well, my name is John

3. __call()

该方法有两个参数。第一个参数$function_name自动接收未定义方法的名称,第二个参数$arguments以数组的方式接收该方法调用的多个参数。

1) __call()方法的用法

function __call(string $function_name, array $arguments)
{
   // 方法体
}

程序中调用未定义的方法时,__call()方法会自动被调用。

示例如下:

<?php
class Person
{                            
   function say()
   {
          echo "Hello, world!<br>";
   }    
   function __call($funName, $arguments)
   {
         echo "The function you called:" . $funName . "(parameter:" ;  // 输出不存在的方法的名称
         print_r($arguments); // 输出不存在的方法的参数列表
         echo ")does not exist!!<br>\n";                  
   }                                        
}
$Person = new Person();          
$Person->run("teacher"); // 如果对象内不存在的方法被调用,则 __call() 方法会被自动调用
$Person->eat("John", "apple");            
$Person->say();

输出结果如下:

The function you called: run (parameter: Array([0] => teacher)) does not exist!
The function you called: eat (parameter: Array([0] => John[1] => apple)) does not exist!
Hello world!

4. __callStatic()

当程序中调用未定义的静态方法时,__callStatic()方法会被调用。

__callStatic()的用法与__call()类似。示例如下:

<?php
class Person
{
   function say()
   {
       echo "Hello, world!<br>";
   }
   public static function __callStatic($funName, $arguments)
   {
       echo "The static method you called:" . $funName . "(parameter:" ;  // 输出不存在的方法的名称
       print_r($arguments); // 输出不存在的方法的参数列表
       echo ")does not exist!<br>\n";
   }
}
$Person = new Person();
$Person::run("teacher"); // 如果对象内不存在的方法被调用,则 __callStatic() 方法会被自动调用
$Person::eat("John", "apple");
$Person->say();

输出结果如下:

The static method you called: run (parameter: Array([0] => teacher)) does not exist!
The static method you called: eat (parameter: Array([0] => John[1] => apple)) does not exist!
Hello world!

5. __get()

当试图访问外部对象的私有属性时,程序会抛出异常并结束执行。但我们可以使用__get()方法来解决这个问题。它能在对象外部取得对象的私有方法。示例如下:

<?php
class Person
{
   private $name;
   private $age;
   function __construct($name="", $age=1)
   {
       $this->name = $name;
       $this->age = $age;
   }
   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("John", 60);   // 用Person类初始化对象,并通过构造方法给属性赋初始值
echo "Name:" . $Person->name . "<br>";   // 访问私有属性时, __get() 方法会自动被调用,这样就能间接取得属性值
echo "Age:" . $Person->age . "<br>";    // __get() 方法自动被调用,并返回不同的值

输出结果如下:

Name: John
Age: 50

6. __set()

__set($property, $value) 方法用来设置对象的私有属性。当试图设置对象中未定义的属性时,就会触发__set()方法,调用参数为被设置的属性名和属性值。

示例代码如下:

<?php
class Person
{
   private $name;
   private $age;
   public function __construct($name="",  $age=25)
   {
       $this->name = $name;
       $this->age  = $age;
   }
   public function __set($property, $value) {
       if ($property=="age")
       {
           if ($value > 150 || $value < 0) {
               return;
           }
       }
       $this->$property = $value;
   }
   public function say(){
       echo "My name is ".$this->name.",I'm ".$this->age." years old";
   }
}
$Person=new Person("John", 25); // 注意下面的代码会改变初始值
$Person->name = "Lili";     // "name" 属性成功赋值。如果没有 __set() 方法,程序就会抛出异常
$Person->age = 16; // "age" 属性成功赋值
$Person->age = 160; // 160 是个非法值,所以赋值失败
$Person->say();  // 输出:My name is Lili, I'm 16 years old.

下面是输出结果:

My name is Lili, I'm 16 years old

7. __isset()

在介绍__isset()方法之前,我先介绍下issset()方法。isset()方法主要用于判断某个变量是否被设置。

在对象外部使用isset()方法有两种情况:

  • 如果参数是公有属性,那么可以利用isset()方法判断属性是否被设置;

  • 如果参数是私有属性,isset()方法将无法使用。

那么,是否有办法判断私有属性被设置呢?当然,只需要在类里定义__isset()方法,就可以在对象外部利用isset()方法判断某个私有属性是否被设置了。

对未定义或没有权限访问的属性调用isset()或empty()时,就会调用__isset()方法。示例如下:

<?php
class Person
{
   public $sex;
   private $name;
   private $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
   /**
    * @param $content
    *
    * @return bool
    */
   public function __isset($content) {
       echo "The {$content} property is private,the __isset() method is called automatically.<br>";
       echo  isset($this->$content);
   }
}
$person = new Person("John", 25); // 赋初始值
echo isset($person->sex),"<br>";
echo isset($person->name),"<br>";
echo isset($person->age),"<br>";

输出结果如下:

1
The name property is private,the __isset() method is called automatically.
1
The age property is private,the __isset() method is called automatically.
1

8. __unset()

与__isset()类似,在未定义或无权限访问的属性上调用unset()方法时会触发__unset()方法。示例如下:

<?php
class Person
{
   public $sex;
   private $name;
   private $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
   /**
    * @param $content
    *
    * @return bool
    */
   public function __unset($content) {
       echo "It is called automatically when we use the unset() method outside the class.<br>";
       echo  isset($this->$content);
   }
}
$person = new Person("John", 25); // 赋初始值
unset($person->sex),"<br>";
unset($person->name),"<br>";
unset($person->age),"<br>";

输出结果如下:

It is called automatically when we use the unset() method outside the class.
1
It is called automatically when we use the unset() method outside the class.
1

9. __sleep()

serialize()方法会检查类中是否存在__sleep()魔术方法。如果存在,就会调用该方法来执行序列化操作。

__sleep()方法通常用来在保存数据之前指定哪些属性需要被序列化。如果对象中包含一些完全不需要序列化的巨大对象,__sleep()就能派上用场了。

具体用法请参考以下代码:

<?php
class Person
{
   public $sex;
   public $name;
   public $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
   /**
    * @return array
    */
   public function __sleep() {
       echo "It is called when the serialize() method is called outside the class.<br>";
       $this->name = base64_encode($this->name);
       return array('name', 'age'); // 返回值中的元素必须是属性的名称
   }
}
$person = new Person('John'); // Initially assigned.
echo serialize($person);
echo '<br/>';

输出结果如下:

It is called when the serialize() method is called outside the class.
O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}

10. __wakeup()

与__sleep()方法相对的就是__wakeup()方法,常用来反序列化,如重建数据连接,或执行其他初始化操作等。

示例如下:

<?php
class Person
{
   public $sex;
   public $name;
   public $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
   /**
    * @return array
    */
   public function __sleep() {
       echo "It is called when the serialize() method is called outside the class.<br>";
       $this->name = base64_encode($this->name);
       return array('name', 'age'); // 返回值中的元素必须是属性的名称
   }
   /**
    * __wakeup
    */
   public function __wakeup() {
       echo "It is called when the unserialize() method is called outside the class.<br>";
       $this->name = 2;
       $this->sex = 'Male';
       // 这里不需要返回数组
   }
}
$person = new Person('John'); // 赋初始值
var_dump(serialize($person));
var_dump(unserialize(serialize($person)));

输出结果如下:

It is called when the serialize() method is called outside the class.
string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}"
It is called when the unserialize() method is called outside the class.
object(Person)#2 (3) { ["sex"]=> string(3) "Male" ["name"]=> int(2) ["age"]=> int(25) }

11. __toString()

使用echo方法直接输出对象时会调用其__toString()方法。

注意:该方法必须返回字符串,否则会抛出"E_RECOVERABLE_ERROR"级别的异常。在__toString()方法中也不能抛出异常。

示例如下:

<?php
class Person
{
   public $sex;
   public $name;
   public $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
   public function __toString()
   {
       return  'go go go';
   }
}
$person = new Person('John'); // 赋初始值
echo $person;

返回结果如下:

go go go

如果类中没有定义__toString()会怎样?我们来试试看。

<?php
class Person
{
   public $sex;
   public $name;
   public $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
}
$person = new Person('John'); // 赋初始值
echo $person;

返回结果如下:

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

可见,它会在页面上报告致命错误,说明这种用法不允许。

12. __invoke()

当试图用调用函数的方式调用对象时,就会自动调用其__invoke()方法。

注意:该功能只在PHP 5.3.0以及以上版本上有效。

示例如下:

<?php
class Person
{
   public $sex;
   public $name;
   public $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
   public function __invoke() {
       echo 'This is an object';
   }
}
$person = new Person('John'); // 赋初始值
$person();

输出结果如下:

This is an object

如果在未定义__invoke()方法的情况下将对象作为函数使用,就会得到以下的结果:

Fatal error: Function name must be a string in D:\phpStudy\WWW\test\index.php on line 18

13. __set_state()

从PHP 5.1.0开始,__set_state()方法会在调用var_export()导出类代码时自动被调用。

__set_state()方法的参数是个数组,包含所有属性的值,格式为array('property' => value, ...)。

下面的示例中没有定义__set_state()方法:

<?php
class Person
{
   public $sex;
   public $name;
   public $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
}
$person = new Person('John'); // 赋初始值
var_export($person);

输出结果如下:

Person::__set_state(array( 'sex' => 'Male', 'name' => 'John', 'age' => 25, ))

可见,输出结果是对象的属性。

下面来看看如果定义了__set_state()方法会怎样:

<?php
class Person
{
   public $sex;
   public $name;
   public $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $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('John'); // 赋初始值
$person->name = 'Jams';
var_export($person);

输出结果如下:

Person::__set_state(array( 'sex' => 'Male', 'name' => 'Jams', 'age' => 25, ))

14. __clone()

PHP中可以使用clone关键字来复制对象,其格式如下:

$copy_of_object = clone $object;

但是,clone关键字只会进行浅复制,所有引用的属性依然会指向原来的变量。

如果对象里定义了__clone()方法,那么复制时就会调用__clone()方法,从而允许我们修改被复制的值(如果需要的话)。

示例如下:

<?php
class Person
{
   public $sex;
   public $name;
   public $age;
   public function __construct($name="",  $age=25, $sex='Male')
   {
       $this->name = $name;
       $this->age  = $age;
       $this->sex  = $sex;
   }
   public function __clone()
   {
       echo __METHOD__."your are cloning the object.<br>";
   }
}
$person = new Person('John'); // 赋初始值
$person2 = clone $person;
var_dump('persion1:');
var_dump($person);
echo '<br>';
var_dump('persion2:');
var_dump($person2);

输出结果如下:

Person::__clone your are cloning the object.
string(9) "persion1:" object(Person)#1 (3) { ["sex"]=> string(3) "Male" ["name"]=> string(6) "John" ["age"]=> int(25) }
string(9) "persion2:" object(Person)#2 (3) { ["sex"]=> string(3) "Male" ["name"]=> string(6) "John" ["age"]=> int(25) }

15. __autoload()

__autoload()方法可以尝试加载未定义的类。

以前,如果在整个程序的生命周期内创建100个对象,就要用include()或require()包含100个类文件,或者在同一个类文件内定义100个类,例如:

/**
* file non_autoload.php
*/
require_once('project/class/A.php');
require_once('project/class/B.php');
require_once('project/class/C.php');
.
.
.
if (ConditionA) {
   $a = new A();
   $b = new B();
   $c = new C();
   // …
} else if (ConditionB) {
   $a = newA();
   $b = new B();
   // …
}

那么使用__autoload()方法呢?

/**
* 文件 autoload_demo.php
*/
function  __autoload($className) {
   $filePath = “project/class/{$className}.php”;
   if (is_readable($filePath)) {
       require($filePath);
   }
}
if (ConditionA) {
   $a = new A();
   $b = new B();
   $c = new C();
   // …
} else if (ConditionB) {
   $a = newA();
   $b = new B();
   // …
}

当PHP引擎第一次使用类A时,如果A没有找到,就会调用__autoload方法,参数为类名"A"。然后我们需要在__autoload()方法中根据类名找到相应的类文件并包含该文件。如果文件没有找到,PHP引擎就会抛出异常。

16. __debugInfo()

执行var_dump()方法的时候会调用__debugInfo()方法。如果__debugInfo()没有定义,则var_dump()方法会输出对象中的所有属性。

示例如下:

<?php
class C {
   private $prop;
   public function __construct($val) {
       $this->prop = $val;
   }
   /**
    * @return array
    */
   public function __debugInfo() {
       return [
           'propSquared' => $this->prop ** 2,
       ];
   }
}
var_dump(new C(42));

输出结果如下:

object(C)#1 (1) { ["propSquared"]=> int(1764) }

注意:__debugInfo()方法只能用于PHP 5.6.0及更高版本。

总结

上面介绍了16个PHP魔术方法,其中最常用的有__set()、__get()和__autoload()。如果你还有问题,可以从PHP官方网站上获得帮助。

原文:https://www.tutorialdocs.com/article/16-php-magic-methods.html

PHP 必知的 16 个编程法则的更多相关文章

  1. PHP 16 个编程法则

    HP是最好的编程语言.对于PHP开发者来说,掌握一些编程法则是十分重要的.而在PHP中,以双下划线(__)开头的方法称为魔术方法,它们扮演着非常重要的角色. 常用的魔术方法包括: -__constru ...

  2. 脑残式网络编程入门(三):HTTP协议必知必会的一些知识

    本文原作者:“竹千代”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.前言 无论是即时通讯应用还是传统的信息系统,Http协议都是我们最常打交 ...

  3. 【SQL必知必会笔记(1)】数据库基础、SQL、MySQL8.0.16下数据库、表的创建及数据插入

    文章目录 1.数据库基础 1.1 数据库(database) 1.2 表(table) 1.3 列和数据类型 1.4 行 1.5 主键 2.什么是SQL 3.创建后续练习所需数据库.表(MySQL8. ...

  4. Visual Studio 使用及调试必知必会

    原文:Visual Studio 使用及调试必知必会   一:C# CODING 技巧 1:TODO 然后 CTRL + W + T,打开任务列表,选中 Comments,就会显示所有待做的任务 2: ...

  5. 迈向高阶:优秀Android程序员必知必会的网络基础

    1.前言 网络通信一直是Android项目里比较重要的一个模块,Android开源项目上出现过很多优秀的网络框架,从一开始只是一些对HttpClient和HttpUrlConnection简易封装使用 ...

  6. 高效开发之SASS篇 灵异留白事件——图片下方无故留白 你会用::before、::after吗 link 与 @import之对比 学习前端前必知的——HTTP协议详解 深入了解——CSS3新增属性 菜鸟进阶——grunt $(#form :input)与$(#form input)的区别

    高效开发之SASS篇   作为通往前端大神之路的普通的一只学鸟,最近接触了一样稍微高逼格一点的神器,特与大家分享~ 他是谁? 作为前端开发人员,你肯定对css很熟悉,但是你知道css可以自定义吗?大家 ...

  7. 第3节:Java基础 - 必知必会(上)

    第3节:Java基础 - 必知必会(上) 本篇是基础篇的第一小节,我们从最基础的java知识点开始学习.本节涉及的知识点包括面向对象的三大特征:封装,继承和多态,并且对常见且容易混淆的重要概念覆盖和重 ...

  8. 必知必会之Java注解

    必知必会之Java注解 目录 不定期更新中-- 元注解 @Documented @Indexed @Retention @Target 常用注解 @Deprecated @FunctionalInte ...

  9. Java面试必知必会(扩展)——Java基础

    float f=3.4;是否正确? 不正确 3.4是双精度,将双精度赋值给浮点型属于向下转型,会造成精度损失: 因此需要强制类型转换: 方式一:float f=(float)3.4 方式二:float ...

随机推荐

  1. redis优化

    一.配置文件优化 bind 127.0.0.1 //允许连接的ip,如果就本机连接最后127.0.0.1 protected-mode yes //是否开启保护模式.默认开启,如果没有设置bind项的 ...

  2. ABP中的拦截器之ValidationInterceptor(下)

    在上篇我分析了整个ABP中ValitationInterceptor的整个过程,就其中涉及到的Validator过程没有详细的论述,这篇文章就这个过程进行详细的论述,另外任何一个重要的特性如何应用是最 ...

  3. Python——模块——随机模块

    1.引用模块 import random 2.随机整数 random.randint(a,b) 3.随机浮点数 random.uniform(a,b) 4.从列表中随机取元素 random.choic ...

  4. Python——编译标准

    注意事项 1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包, ...

  5. 支付宝支付demo(亲测)

    支付宝支付demo 这个是java后台调起支付的demo,直接将body返回给安卓端即可调起支付 package com.dyy.test; import java.text.SimpleDateFo ...

  6. Java集合:HashMap底层实现和原理(源码解析)

    Note:文章的内容基于JDK1.7进行分析.1.8做的改动文章末尾进行讲解. 一.先来熟悉一下我们常用的HashMap: 1.概述 HashMap基于Map接口实现,元素以键值对的方式存储,并且允许 ...

  7. 中国剩余定理 CRT

    中国剩余定理 CRT 正常版本CRT 要解的是一个很容易的东西 \[ \begin{aligned} x\equiv a_1(mod\ m_1)\\ x\equiv a_2(mod\ m_2)\\ . ...

  8. 【CF1151E】Number of Components

    [CF1151E]Number of Components 题面 CF 题解 联通块个数=点数-边数. 然后把边全部挂在较小的权值上. 考虑从小往大枚举左端点,等价于每次删掉一个元素,那么删去点数,加 ...

  9. vue学习笔记(三)- vue2.x引入Element-ui

    webpack+vue2.x+element-ui 作者:狐狸家的鱼 本文链接:vue2.x引入Element-ui GitHub:sueRimn 1.新建项目 vue init webpack vu ...

  10. solr面板的使用

    创建数据库 先别着急点击Add Core,先去目录下创建几个文件. 去solrhome目录下创建一个文件夹,比如test,这个文件夹就是数据库文件夹.