PHP 魔术方法详解

__construct() 构造函数

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

1.构造方法的作用:

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

2.构造方法的在类中的声明格式:

function __constrct([参数列表]){

    方法体 //通常用来对成员属性进行初始化赋值

}

3.在类中声明构造方法需要注意的事项:

1).在同一个类中只能声明一个构造方法,原因是,PHP不支持构造函数重载。

2).构造方法名称是以两个下画线开始的__construct()

4.代码示例:

class Constructs
{

    private $name;

    private $age;

    private $sex;

    
    public function __construct($name,$age,$sex)
    {
        
        $this->name = $name;

        $this->sex = $sex;

        $this->age = $age;

    }


    public function info(){

        return '我是'.$this->name.' , 今年 '.$this->age.' 岁了 , 我是'.$this->sex.'孩子喔。';

    }


}

$info = new Constructs('小洪帽','15','男');

print_r($info->info());

__destruct() 析构函数

通过上面的讲解,现在我们已经知道了什么叫构造方法。那么与构造方法对应的就是析构方法。

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

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

析造方法的声明格式与构造方法 __construct() 比较类似,也是以两个下划线开始的方法 __destruct() ,这种析构方法名称也是固定的。

1.析构方法的声明格式:

public function __destruct(){
        
    //注意:析构函数不能带有任何参数。

}

2.析构方法的作用:

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

3.代码示例:

class Constructs
{

    private $name;

    private $age;

    private $sex;
    
    public function __construct($name,$age,$sex)
    {
        
        $this->name = $name;

        $this->sex = $sex;

        $this->age = $age;

    }


    /**
     * [__destruct 析构方法]
     */
    public function __destruct(){

        echo '我们下次再见,Bey~';

    }


    public function info(){

        return '我是'.$this->name.' , 今年 '.$this->age.' 岁了 , 我是'.$this->sex.'孩子喔。';

    }


}

$info = new Constructs('小洪帽','15','男');

unset($info);//销毁上面创建的对象 然后输出:我们下次再见,Bey~

__call() 在对象中调用一个不可访问方法时调用

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

1.使用格式:

public function __call(){

        
        
}

2.方法的作用:

 1)为了避免当调用的方法不存在时产生错误,而意外的导致程序中止,可以使用 __call() 方法来避免。

 2)该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去。

3.代码示例:

class Constructs
{


    public function info(){

        echo 'Hello World!';

    }

    /**
     * [__call 被调用不存在的方法时调用此方法]
     * @param  [type] $function_name [被调用的方法名]
     * @param  [type] $arguments     [传入的参数]
     * @return [type]                [description]
     */
    public function __call($function_name,$arguments){

        print_r($function_name);
        echo '<br>';
        print_r($arguments);
        echo '<br>';

    }


}

$info = new Constructs();

$info->index('首页');
$info->user('小洪帽',25);
$info->info();

__callStatic() 静态方式调用一个不可访问方法时调用

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

代码示例:

class Constructs
{


    public function info(){

        echo 'Hello World!';

    }

    /**
     * [__callStatic 静态方式调用一个不可访问方法时调用]
     * @param  [type] $function_name [被调用的方法名]
     * @param  [type] $arguments     [传入的参数]
     * @return [type]                [description]
     */
    public static function __callStatic($function_name,$arguments){

        print_r($function_name);
        echo '<br>';
        print_r($arguments);
        echo '<br>';

    }


}

$info = new Constructs();

//注意:静态方式调用 -> 更改为 :: ;否则会报出(不推荐:非静态方法)的错误

$info::index('首页');
$info::user('小洪帽',25);
$info->info();

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

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

1.作用:

在程序运行过程中,通过它可以在对象的外部获取私有成员属性的值。

2.代码示例:

class Constructs
{

    
    private $name;

    private $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    /**
     * [__get 获得一个类的成员变量时调用]
     * @param  [type] $getName [调用名,在类外面调用 $info->age ; age 就是这个调用名]
     * @return [type]          [description]
     */
    public function __get($getName){

        if($getName == 'age'){
            if($this->age >= 16){
                return $this->age;
            }else{
                return '你还未成年!';
            }
        }else{
            return $this->$getName;
        }

    }

}

$info = new Constructs('小洪帽',15);
echo $info->name; //输出:小洪帽
echo $info->age; //输出:你还未成年!

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

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

代码示例:

class Constructs
{

    
    private $name;

    private $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }

    /**
     * [__set 设置一个类的成员变量时调用]
     * @param [type] $keys   [key]
     * @param [type] $values [value]
     */
    public function __set($keys,$values){

        $this->$keys = $values;

    }


    public function index(){
        echo $this->name.'今年'.$this->age.'岁了';
    }

}

$info = new Constructs('小洪帽',15);
$info->name = 'PHP之路';
$info->age = '20';
$info->index(); // 输出:PHP之路今年20岁了

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

在看这个方法之前我们看一下isset()函数的应用,isset()是测定变量是否设定用的函数,传入一个变量作为参数,如果传入的变量存在则传回true,否则传回false

那么如果在一个对象外面使用isset()这个函数去测定对象里面的成员是否被设定可不可以用它呢?

分两种情况,如果对象里面成员是公有的,我们就可以使用这个函数来测定成员属性,如果是私有的成员属性,这个函数就不起作用了,原因就是因为私有的被封装了,在外部不可见。那么我们就不可以在对象的外部使用isset()函数来测定私有成员属性是否被设定了呢?当然是可以的,但不是一成不变。你只要在类里面加上一个__isset()方法就可以了,当在类外部使用isset()函数来测定对象里面的私有成员是否被设定时,就会自动调用类里面的__isset()方法了帮我们完成这样的操作。

1.作用:

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

2.代码示例:

class Constructs
{

    
    private $name;

    private $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }

    
    /**
     * [__isset 当对不可访问属性调用isset()或empty()时调用]
     * @param  [type]  $content [调用名]
     * @return boolean          [description]
     */
    public function __isset($content){
        echo '当在类外部使用isset()函数测定私有成员{$content}时,自动调用<br>';
        echo isset($this->$content).'<br>';
    }

}

$info = new Constructs('小洪帽',15);
echo isset($info->age);
echo empty($info->name);

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

看这个方法之前呢,我们也先来看一下 unset() 函数,unset()这个函数的作用是删除指定的变量且传回true,参数为要删除的变量。

那么如果在一个对象外部去删除对象内部的成员属性用unset()函数可以吗?

这里自然也是分两种情况:

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

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

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

代码示例:

class Constructs
{

    
    private $name;

    private $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    /**
     * [__unset 当对不可访问属性调用unset()时被调用]
     * @param [type] $content [调用名]
     */
    public function __unset($content){

        echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>";
        
    }


}

$info = new Constructs('小洪帽',15); //初始值
unset($info->name);

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

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

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

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

1.注意:

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

2.作用:

__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。

3.代码示例:

class Constructs
{

    
    private $name;

    private $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    public function __sleep(){

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

    }

}

$info = new Constructs('小洪帽',15); //初始值
echo serialize($info);


 

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

如果说 __sleep() 是白的,那么 __wakeup() 就是黑的了。

那么为什么呢?因为:

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

1.作用:
__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。

2.代码示例:

class Constructs
{

    
    private $name;

    private $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    public function __sleep(){

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

    }


    public function __wakeup(){
        echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
        $this->name = '小洪帽';
        $this->age = 18;
        // 这里不需要返回数组
    }

}

$info = new Constructs('小洪帽',15); //初始值
echo serialize($info);
echo '<br><br>';
print_r(unserialize(serialize($info)));


 

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

1.作用:  

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


2.注意:

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

3警告:

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

4.代码示例:

class Constructs
{

    
    private $name;

    private $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    public function __toString(){

        return 'Hello World!';

    }

}

$info = new Constructs('小洪帽'); //初始值
echo $info;

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

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

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

1.作用:

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

2.注意:

本特性只在 PHP 5.3.0 及以上版本有效。

3.代码示例:

class Constructs
{

    
    private $name;

    private $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    public function __invoke(){

        echo '这是一个对象';

    }

}

$info = new Constructs('小洪帽'); //初始值
$info();


 

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

1.作用:

PHP 5.1.0 起,当调用 var_export() 导出类时,此静态方法会被自动调用。

2.参数:

本方法的唯一参数是一个数组,其中包含按 array('property' => value, ...) 格式排列的类属性。
 

3.下面我们先来看看在没有加 __set_state() 情况按下,代码及运行结果如何:

class Constructs
{

    
    public $name;

    public $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    

}

$info = new Constructs('小洪帽'); //初始值
var_export($info);

//输出结果:Constructs::__set_state(array( 'name' => '小洪帽', 'age' => 16, ))

4.加了 __set_state() 之后:

class Constructs
{

    
    public $name;

    public $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    public static function __set_state($an_array){

        $a = new Constructs();

        $a->name = $an_array['name'];

        return $a;

    }

}

$info = new Constructs('小洪帽'); //初始值
$info->name = 'PHP';
var_export($info);

//输出结果:Constructs::__set_state(array( 'name' => 'PHP', 'age' => 16, ))

__clone() 克隆方法,当对象复制完成时调用

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

1.作用:

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

2.语法:

$copy_of_object = clone $object;

3.注意:

当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。

当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。

4.代码示例:

class Constructs
{

    
    public $name;

    public $age;


    public function __construct($name='',$age=16){

        $this->name = $name;

        $this->age = $age;

    }


    public function __clone(){

        echo __METHOD__."你正在克隆对象<br>";

    }

}

$info = new Constructs('小洪帽'); //初始值

$clone = clone $info;

var_dump('info:');
var_dump($info);
echo '<br>';
var_dump('clone:');
var_dump($clone);


//输出:
//Constructs::__clone你正在克隆对象
//string(5) "info:" object(Constructs)#1 (2) { ["name"]=> string(9) "小洪帽" ["age"]=> int(16) }
//string(6) "clone:" object(Constructs)#2 (2) { ["name"]=> string(9) "小洪帽" ["age"]=> int(16) }



 

__autoload() 尝试加载未定义的类

在魔术函数 __autoload() 方法出现以前,如果你要在一个程序文件中实例化100个对象,那么你必须用include或者require包含进来100个类文件,或者你把这100个类定义在同一个类文件中 —— 相信这个文件一定会非常大,然后你就痛苦了。

但是有了 __autoload() 方法,以后就不必为此大伤脑筋了,这个类会在你实例化对象之前自动加载制定的文件。

1.作用:

你可以通过定义这个函数来启用类的自动加载。
 

2.看看以往的实现方式:


require_once('project/class/A.php');  
require_once('project/class/B.php');  
require_once('project/class/C.php');  
   
if (条件A) {  
    $a = new A();  
    $b = new B();  
    $c = new C();  
    // … 业务逻辑  
} else if (条件B) {  
    $a = new A();  
    $b = new B();  
    // … 业务逻辑  
}

看到了吗?不用100个,只是3个看起来就有点烦了。而且这样就会有一个问题:如果脚本执行“条件B”这个分支时,C.php这个文件其实没有必要包含。因为,任何一个被包含的文件,无论是否使用,均会被php引擎编译。如果不使用,却被编译,这样可以被视作一种资源浪费。更进一步,如果C.php包含了D.php,D.php包含了E.php。并且大部分情况都执行“条件B”分支,那么就会浪费一部分资源去编译C.php,D.php,E.php三个“无用”的文件。

3.使用 __autoload() 方式:

function  __autoload($className) {  
    $filePath = "project/class/".$className.".php"; //路径  $className  为类名
    if (is_readable($filePath)) {  
        require($filePath);  
    }  
}  
   

$a = new A();  
$b = new B();  
$c = new C();  



PHP7.2以上版本使用上面的__autoload()会报错

报错:Deprecated: __autoload() is deprecated, use spl_autoload_register() instead in

解决:把PHP版本降低到7.2以下或者使用下面方法



spl_autoload_register('autoload');

function autoload($className){  
    $filePath = "project/class/".$className.".php";  
    print_r($filePath);die;
    if (is_readable($filePath)) {  
        require($filePath);  
    }  
}
   

$a = new A();  
$b = new B();  
$c = new C(); 


__debugInfo() 打印所需调试信息

1.注意:

该方法在PHP 5.6.0及其以上版本才可以用,如果你发现使用无效或者报错,请查看啊你的版本。
 

2.代码示例

class Constructs
{

    
    private $prop;


    public function __construct($val){

        $this->prop = $val;

    }


    public function __debugInfo(){

        return ['propSquared'=>$this->prop ** 2];

        //注意:这里的 `**` 是乘方的意思,也是在PHP5.6.0及其以上才可以使用,详情请查看PHP手册

    }

}

$info = new Constructs(10); //初始值

var_dump($info);


//结果:object(Constructs)#1 (1) { ["propSquared"]=> int(100) }

总结:

以上就是PHP中我了解到的魔术方法了,常用的包括 __set() __get() __autoload() 等应该熟悉,其他的了解也没有关系,毕竟知识不怕多嘛。

好了,有兴趣的或者我这里没有说明白的,可以参考啊官方文档。

PHP在线手册地址:http://php.net/manual/zh/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值