魔术方法(魔术函数)

类内部使用的方法

__construct() :实例化对象时被调用;
__destuct():当删除一个对象或者对象销毁前是被执行;

__get():调用对象不存在的属性或没有声明的属性值被调用;

__set():给一个未定义的属性或没有声明的属性赋值时调用此方法;

__call():调用对象不存在方法时被调用;

这里的没有声明包括当使用对象调用时,访问控制修饰符为proteced或private的属性(即没有权限访问的属性)。

<?php

class Human {
    //定义属性
    public $name = "Jack";        //姓名属性
    public $sex = "female";        //性别属性
    public $age = 23;            //年龄属性
    var $addr;                    //通讯属性 PHP4里面声明属性
    public $data = array();        //定义data属性 用来存放数据
    //定义方法
    public function reading() {
        
    }
    
    public function talking() {
        echo "Hellow world!<br />";
    }
    //PHPdocuments
    function html() {//how to make love
        
    }
    /**
     * 魔术方法 当满足特定场景时 自动调用的方法
     * @param string $name    要设置的未定义属性名称
     * @param mixed $value    设置的值
     */
    public function __set($name, $value) {
//        echo "你正在尝试给一个未定义的属性{$name}赋值!{$value}";
        //将未定义的属性和值 以键值对 存入data属性中 在类的内部使用$this指代当前调用对象 访问属性和方法
        $this->data[$name] = $value;
    }
    
    //读取一个未定义的属性时 自动调用的魔术方法
    public function __get($name) {
//        echo "你正在读取未定义的属性{$name}";
        echo $this->data[$name];
//        return $this->data[$name];
    }
    
    //访问未定义的方法时 自动调用的魔术方法
    public function __call($name, $arguments) {
//        echo $name;
//        var_dump($arguments);
        echo "你调用了一个未定义的方法:{$name},并且携带参数:";
        var_dump($arguments);
    }
}
//实例化一个对象
$Jack = new Human();
//通过对象去获取属性和方法 通过"->"运算符 .
echo $Jack->name."的年龄是:".$Jack->age."<br />";
$Jack->talking();
//给一个未定义的属性赋值时  会调用魔术方法 __set();
$Jack->from = "USA";    
var_dump($Jack->data);
echo $Jack->from."<br />";
//访问未定义的方法
$Jack->abc("1", "a");

__callStatic()

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

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

_toString()

_toString():打印一个对象时被调用,比如echo $obj ,print($obj) __toString(),类被当成字符串时的回应方法此方法必须返回一个字符串,否则报错或500。不能在 __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;

__clone()

__clone():克隆对象时被调用,比如$t = new Test()$tt = clone $t;在单例模式中已经介绍

__sleep()

__sleep() :如过存在此方法serialize之前被调用,此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。

注意:


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

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

__wakeup()

__wakeup():unserialize之前被调用,__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。但不需要全部保存,这个功能就很好用。

与serialize相反,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;
    }

    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 = 2;
        $this->sex = '男';
        // 这里不需要返回数组
    }
}

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

__isset和__unset()

__isset($property):当对一个未定义的属性或者没有生命的属性调用isset()函数时调用此方法,如 isset($c->name);

__unset():当对一个未定义的属性或没有声明的属性调用unset()函数时调用此方法,如:unset($c->name);

这里的没有声明包括当使用对象调用时,访问控制修饰符为protecedprivate的属性(即没有权限访问的属性)。

<?php

class Human {

    public $name;
    private $sex;


    public function __isset($name) {
        echo "__isset被调用了:属性$name<br/>";
    }
    function __unset($name)
    {
        echo "__unset被调用了:属性$name<br/>";
    }
}
//实例化对象
$test = new Human();
isset($test->sex);//调用私有属性 被自动调用了
isset($test->age);//调用未定义的属性 被自动调用了
echo "<br/>";
unset($test->sex);//删除私有属性 被自动调用了
unset($test->age);//输出未定义的属性 被自动调用了

__invoke()

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

__set_state()

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

<?php
class Person
{
    protected $sex;
    public    $name;
    private   $age;

    public function __construct($name="", $age=25, $sex='男')
    {
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }
    public function t(){}
    public static  function __set_state($an_array){
        $a = new self();
        $a->name = $an_array['name'];
        return $a;
    }
}
$person = new Person('小明'); // 初始赋值
$person->name = '小红';
$a=var_export($person,true);
var_dump($a);

_debugInfo()

_debugInfo(),打印所需调试信息、自动调用 PHP5.6以后出的魔术方法

<?php
class C {
    private $prop;
    public function __construct($val) {
        $this->prop = $val;
    }
    public function __debugInfo() {
        return [
            'propSquared' => $this->prop ** 2,
        ];
    }
}
//这里的 `**` 是乘方的意思,也是在PHP5.6.0及其以上才可以使用,详情请查看PHP手册
var_dump(new C(42));

类外部使用的方法

__autoload():实例化一个对象时,如果对应的类不存在,在该方法被调用。

类的自动加载

在系统开发过程中,不可能把所有的类都写在一个PHP文件中,当在一个PHP文件中需要调用另一个文件中声明的类时,就需要通过include或者require把这个文件引入。

在类文件众多的项目中,要将所有类的文件都逐个引入,是一个很让人头痛的工作。那么我们能不能再用到什么类的时候,在把这个类所在的PHP文件引入呢?

__autoload()魔术方法实现

在PHP5中,可以定义个__autoload函数,它会在试图使用尚未被定义的类时自动调用,通过调用此函数,脚本引擎在PHP出错失败前有了最后一个机会加载所需的类。

__autoload($className)函数接受的一个参数,就是你想加载类的类名,所以你做项目时,在组织定义类的文件名时,需要按照一定的规则,最好以类名为中心,也可以加上同意的前缀或后缀形成文件名。

set_include_path(),是在脚本里动态地对PHP.iniinclude_path进行修改的。动态设置包含目录(预定义include和require的路径范围)。

加载lib目录下的类

set_include_path(get_include_path().PATH_SEPAPATOR."lib/");

示例图

<?php
function __autoload($class){
  var_dump($class);
  include $class.".php";
}
//set_include_path(get_include_path().PATH_SEPARATOR."lib/");
$a=new a();
$a->say();

$b= new b();
$b->say();

其实这个__autuload 并不是最好的选择,用spl_autoload_register()函数

spl_autoload_register()

spl_autoload_register()此函数的功能就是把函数注册至spl的autoload函数栈中,并移除系统默认的__autolad()函数。

spl_autoload_register()可以调用多次。它实际上创建了autoload函数的队列,按定义时的顺序逐个执行。而__autoload()只可以定义一次

<?php

class test {
 public static function loadprint( $class ) {
 $file = $class . '.class.php';
 if (is_file($file)) {
  require_once($file);
 }
 }
}
spl_autoload_register( array('test','loadprint') );
//另一种写法:spl_autoload_register( "test::loadprint" );
$obj = new PRINTIT();
$obj->doPrint();?>

printit.class.php文件

<?php
class PRINTIT {
 function doPrint() {
 echo 'hello world2';
 }
}
?>

spl_autoload_register()多次调用示例

<?php
set_include_path(get_include_path().PATH_SEPARATOR."lib/");
function __autoload($class){
  echo "__autoload".$class,"<br/>";
  include $class.".php";
}
function __myautoload($class){
    echo "__myautoload".$class,"<br/>";
    include $class.".php";
}
function __toload($class){
    echo "__toload中".$class,"<br/>";

}
spl_autoload_register("__myautoload");
spl_autoload_register("__toload");//这个函数是多次执行的
// 如果上一个函数没找到 会执行下一个spl的注册函数进行查找 会进入__toload 
//以 “栈 ”的形式进行查找
//
$a=new a();
$a->say();
$b= new b();
$b->say();
$c= new c(); //new 一个没有的类 对象测试 spl_autoload_regiter的测试结果

命名空间引入类的原理

在当前文件夹在新建dong然后在里面新建个Text.php

<?php
namespace dongge;
use dong\Text;
use function var_dump;

class test {
    public static function loadprint( $class ) {
        $file = $class . '.php';
        $file=str_replace("\\",'/',$file);
        if(is_file($file)){
            include $file;
        }
    }
}
//另一种写法: spl_autoload_register( array('test','loadprint') ,true,true);
spl_autoload_register( "dongge\\test::loadprint" );
$obj = new Text();
$obj->doPrint();

dong文件夹的Text.php

<?php
namespace  dong;
class Text{
 function doPrint() {
 echo 'hello world2';
 }
}
?>

示例图2

Last modification:January 6, 2020
如果觉得我的文章对你有用,请随意赞赏