php之基础深入—类与对象篇

有些东西如果不是经常使用,很容易忘记,比如魔术方法和魔术常量。

PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用。
魔术方法包括:

1、类的自动加载:spl_autoload_register()函数可以注册任意数量的自动加载器,当使用尚未被定义的类和接口(interface)时自动去加载,这样可以避免include一大堆文件。

魔术方法(Magic methods)

PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用。
魔术方法包括:

  • __construct(),类的构造函数
  • __destruct(),类的析构函数
  • __call(),在对象中调用一个不可访问方法时调用
  • __callStatic(),用静态方式中调用一个不可访问方法时调用
  • __get(),获得一个类的成员变量时调用
  • __set(),设置一个类的成员变量时调用
  • __isset(),当对不可访问属性调用isset()empty()时调用
  • __unset(),当对不可访问属性调用unset()时被调用。
  • __sleep(),执行serialize()时,先会调用这个函数
  • __wakeup(),执行unserialize()时,先会调用这个函数
  • __toString(),类被当成字符串时的回应方法
  • __invoke(),调用函数的方式调用一个对象时的回应方法
  • __set_state(),调用var_export()导出类时,此静态方法会被调用。
  • __clone(),当对象复制完成时调用

__construct,类的析构函数
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset或empty,当对不可访问属性调用unset,执行serialize()时,先会调用这个函数
__wakeup时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用 __autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息

比如你有一个需要引用的类文件Example.php,它是这样子的

__construct()__destruct()

构造函数和析构函数应该不陌生,他们在对象创建和消亡时被调用。例如我们需要打开一个文件,在对象创建时打开,对象消亡时关闭

<?php 
class FileRead
{
    protected $handle = NULL;

    function __construct(){
        $this->handle = fopen(...);
    }

    function __destruct(){
        fclose($this->handle);
    }
}
?>

这两个方法在继承时可以扩展,例如:

<?php 
class TmpFileRead extends FileRead
{
    function __construct(){
        parent::__construct();
    }

    function __destruct(){
        parent::__destruct();
    }
}
?>

范例

<?phpclass Example{    public function __construct() {        echo "I am zhylioooo";    }}

__call()__callStatic()

在对象中调用一个不可访问方法时会调用这两个方法,后者为静态方法。这两个方法我们在可变方法(Variable
functions)调用中可能会用到。

<?php
class MethodTest 
{
    public function __call ($name, $arguments) {
        echo "Calling object method '$name' ". implode(', ', $arguments). "n";
    }

    public static function __callStatic ($name, $arguments) {
        echo "Calling static method '$name' ". implode(', ', $arguments). "n";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context');
?>

下面让我们以实例的形式向大家讲解下这几个魔术方法时如何使用的。

你还有一个父文件,或者父类,或者说是公共文件,总之是你必须要用到的文件,它是这样子的

__get()__set()__isset()__unset()

当get/set一个类的成员变量时调用这两个函数。例如我们将对象变量保存在另外一个数组中,而不是对象本身的成员变量

<?php 
class MethodTest
{
    private $data = array();

    public function __set($name, $value){
        $this->data[$name] = $value;
    }

    public function __get($name){
        if(array_key_exists($name, $this->data))
            return $this->data[$name];
        return NULL;
    }

    public function __isset($name){
        return isset($this->data[$name])
    }

    public function unset($name){
        unset($this->data[$name]);
    }
}
?>

一、 __construct(),类的构造函数

<?phpfunction getClass($class) {    $file = $class . '.php';    if (is_file($file)) {        require_once($file);    }}spl_autoload_register('getClass');//实例化一个本文件没有的类new Example();    //当你在这个公共文件上面或者包含它的文件下面实例化不在同一个文件上的类时,就会自动查找并把那个类文件加载进来,就可以实例化了

__sleep()__wakeup()

当我们在执行serialize()unserialize()时,会先调用这两个函数。例如我们在序列化一个对象时,这个对象有一个数据库链接,想要在反序列化中恢复链接状态,则可以通过重构这两个函数来实现链接的恢复。例子如下:

<?php
class Connection 
{
    protected $link;
    private $server, $username, $password, $db;

    public function __construct($server, $username, $password, $db)
    {
        $this->server = $server;
        $this->username = $username;
        $this->password = $password;
        $this->db = $db;
        $this->connect();
    }

    private function connect()
    {
        $this->link = mysql_connect($this->server, $this->username, $this->password);
        mysql_select_db($this->db, $this->link);
    }

    public function __sleep()
    {
        return array('server', 'username', 'password', 'db');
    }

    public function __wakeup()
    {
        $this->connect();
    }
}
?>

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

当然,这只是最简单的例子,但是当需要引用的类文件比较多的时候,就很能体现出spl_autoload_register()的价值与灵活性了。

__toString()

对象当成字符串时的回应方法。例如使用echo $obj;来输出一个对象

<?php
// Declare a simple class
class TestClass
{
    public function __toString() {
        return 'this is a object';
    }
}

$class = new TestClass();
echo $class;
?>

这个方法只能返回字符串,而且不可以在这个方法中抛出异常,否则会出现致命错误。

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

2、代码的复用:trait定义和class相似,但是无法实例化自身,通常用use来实现代码复用,相当于把trait的内容复制到use的地方,use多个trait类时用逗号隔开

__invoke()

调用函数的方式调用一个对象时的回应方法。如下

<?php
class CallableClass 
{
    function __invoke() {
        echo 'this is a object';
    }
}
$obj = new CallableClass;
var_dump(is_callable($obj));
?>

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

<?phptrait A{    function say(){        echo "hello";    }}class B{    use A;}$obj=new B();$obj->say();

__set_state()

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

<?php
class A
{
    public $var1;
    public $var2;

    public static function __set_state ($an_array) {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
var_dump(var_export($a));
?>
function __constrct{ 方法体 //通常用来对成员属性进行初始化赋值}

从基类继承的成员会被 trait
插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而
trait 则覆盖了被继承的方法。

__clone()

当对象复制完成时调用。例如在设计模式详解及PHP实现:单例模式一文中提到的单例模式实现方式,利用这个函数来防止对象被克隆。

<?php 
public class Singleton {
    private static $_instance = NULL;

    // 私有构造方法 
    private function __construct() {}

    public static function getInstance() {
        if (is_null(self::$_instance)) {
            self::$_instance = new Singleton();
        }
        return self::$_instance;
    }

    // 防止克隆实例
    public function __clone(){
        die('Clone is not allowed.' . E_USER_ERROR);
    }
}
?>

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

<?phptrait A{    function say(){        echo "hello";    }    function listen(){        echo "Wow!";    }}class B{    function say(){        echo "zhylioooo";    }}class C extends B{    use A;    function listen(){        echo "boom!";    }}$obj=new C();$obj->say();    //输出"hello",trait类A覆盖了父类B的相同方法$obj->listen(); //输出"boom",当前类C覆盖了trait类A的相同方法

魔术常量(Magic constants)

PHP中的常量大部分都是不变的,但是有8个常量会随着他们所在代码位置的变化而变化,这8个常量被称为魔术常量。

  • __LINE__,文件中的当前行号
  • __FILE__,文件的完整路径和文件名
  • __DIR__,文件所在的目录
  • __FUNCTION__,函数名称
  • __CLASS__,类的名称
  • __TRAIT__,Trait的名字
  • __METHOD__,类的方法名
  • __NAMESPACE__,当前命名空间的名称

这些魔术常量常常被用于获得当前环境信息或者记录日志。

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

3、匿名类的创建和使用:PHP 7 开始支持匿名类。
匿名类可以创建一次性的简单对象,可以传递参数到匿名类的构造器,也可以扩展其他类、实现接口(implement
interface),以及像其他普通的类一样使用 trait,

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

<?phpclass SomeClass {}interface SomeInterface {}trait SomeTrait {}var_dump(new class extends SomeClass implements SomeInterface {    private $num;    public function __construct($num)    {        $this->num = $num;    }    use SomeTrait;});/* *以上代码会输出: *object(class@anonymous)#1  { *["Command line code0x104c5b612":"class@anonymous":private]=> *int *  } */
name = $name; $this->sex = $sex; $this->age = $age; } /** * say 方法 */ public function say() { echo "我叫:" . $this->name . ",性别:" . $this->sex . ",年龄:" . $this->age; } }

4、重载overloading:当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用,所有的重载方法都必须被声明为public,这些魔术方法的参数都不能通过引用传递

创建对象$Person1且不带任参数

属性重载

$Person1 = new Person();echo $Person1->say(); //输出:我叫:,性别:男,年龄:27
<?phpclass A{    private $name="zhylioooo";    private $data=array();       //保存被重载的数据    //在给不可访问属性赋值时,__set() 会被调用    public function __set($name, $value) {        $this->data[$name]=$value;    }    //读取不可访问属性的值时,__get() 会被调用    public function __get($name) {        if (array_key_exists($name, $this->data)) {            return $this->data[$name];        }else{            return "Undefined property ".$name;        }    }    //当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用    public function __isset($name) {        return isset($this->data[$name]);    }    //当对不可访问属性调用 unset() 时,__unset() 会被调用    public function __unset($name){        echo $name."has unset.";        unset($this->data[$name]);    }    //调用存在的属性    public function getName(){        return $this->name;    }}$obj=new A();echo $obj->getName();   //输出"zhylioooo"echo $obj->age;         //输出"Undefined property age"var_dump(isset($obj->age)); //输出 ...03.php:34:boolean falseunset($obj->age);       //输出"age has unset"$obj->age=24;echo $obj->age;         //输出24

创建对象$Person2且带参数“小明”

方法重载

$Person2 = new Person;echo $Person2->say(); //输出:我叫:张三,性别:男,年龄:27
<?phpclass A{    private function say1(){        echo "hello";    }    private static function say2(){        echo "world";    }    //在对象中调用一个不可访问方法时,__call() 会被调用    //$name 参数是要调用的方法名称。$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数    public function __call($name, $arguments) {        echo "call the ".$name.":";        $this->say1();    }    public static function __callStatic($name, $arguments){        echo "call the static method ".$name;        self::say2();    }}$obj=new A();$obj->say1();   //输出call the say1:helloA::say2();      //输出call the static method say2world

创建对象$Person3且带三个参数

5、魔术方法:PHP 将所有以 __开头的类方法保留为魔术方法,包括

$Person3 = new Person;echo $Person3->say(); //输出:我叫:李四,性别:男,年龄:25
  • __construct(),类的构造函数(类实例化时自动调用)
  • __destruct(),类的析构函数(销毁一个类之前调用)
  • __澳门新浦京电子游戏,call(),在对象中调用一个不可访问方法时调用
  • __callStatic(),用静态方式中调用一个不可访问方法时调用
  • __get(),获得一个类的成员变量时调用
  • __set(),设置一个类的成员变量时调用
  • __isset(),当对不可访问属性调用isset时调用
  • __unset(),当对不可访问属性调用unset()时被调用。
  • __sleep(),执行serialize()时,先会调用这个函数
  • __wakeup(),执行unserialize()时,先会调用这个函数
  • __toString(),类被当成字符串时的回应方法
  • __invoke(),调用函数的方式调用一个对象时的回应方法
  • __set_state(),调用var_export()导出类时,此静态方法会被调用(var_export和var_dump类似)
  • __clone(),当对象复制完成时调用
  • __autoload(),尝试加载未定义的类(建议使用spl_autoload_register
  • __debugInfo(),打印所需调试信息

二、__destruct(),类的析构函数

其中__sleep和__wakeup

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

<?php//serialize()函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,则该方法会优先被调用,然后才执行序列化操作。//unserialize()会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,再进行反序列化操作。//序列化函数serialize()的作用是把不方便存储和传递的php变量序列化为可存储的字符串,而反序列化函数unserialize()则是把相应字符串反序列化为php变量。//serialize()可处理除了 resource 之外的任何类型$arr=array(1,2,3);$data= serialize($arr);var_dump($data);   //输出..3.php:6:string 'a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}' (length=30)$new_arr= unserialize($data);var_dump($new_arr); /*输出C:wamp64wwwtest3.php:8:array   0 => int 1  1 => int 2  2 => int 3 * */

<?phpclass A{    private $name,$pswd;    public function __construct($name,$pswd) {        $this->name=$name;        $this->pswd=$pswd;        $this->login();    }    public function login(){        echo "hello ".$this->name."!your pswd is ".$this->pswd;    }    public function __sleep() {        return array("name","pswd");    }    public function __wakeup() {        $this->login();    }}$obj=new A("zhylioooo","123456");   //输出hello zhylioooo!your pswd is 123456$str=serialize($obj);unserialize($str);      //再次输出hello zhylioooo!your pswd is 123456/**序列化操作对象时,__sleep()用于提交未提交的数据,即保持对象现场状态*__wakeup()则是恢复现场 */

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

6、 类与对象的几个重要函数和关键字

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

class_exists() 判断某个类是否存在interface_exists() 判断接口是否存在class_implements() 返回指定类实现的所有接口class_parents() 返回指定类的父类get_class() __CLASS__ 获取某个对象所处的类名get_parent_class() 获取某个对象所属父类的类名get_class_methods() 获取一个类所有方法,返回索引数组get_class_vars() 获取一个类所有属性,下标为属性名get_declared_classes() 获取所有声明过的类is_object() 判断是否对象get_object_vars() 获得对象所有属性,返回数组,下标为属性名property_exists() 判断对象中是否存在该属性get_called_class() 获取当前主调类的类名this 是指向当前对象的指针(姑且用C里面的指针来看吧)self 是指向当前类的指针parent 是指向父类的指针static 是指向最初调用的类

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

1、 析构方法的声明格式

function __destruct(){ //方法体}

注意:析构函数不能带有任何参数。

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

name = $name; $this->sex = $sex; $this->age = $age; } /** * say 说话方法 */ public function say() { echo "我叫:".$this->name.",性别:".$this->sex.",年龄:".$this->age; } /** * 声明一个析构方法 */ public function __destruct() { echo "我觉得我还可以再抢救一下,我的名字叫".$this->name; }}$Person = new Person; //销毁上面创建的对象$Person

上面的程序运行时输出:

我觉得我还可以再抢救一下,我的名字叫小明

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

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

1、 __call() 方法的格式:

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

2、 __call() 方法的作用:

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

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

"; } /** * 声明此方法用来处理调用对象中不存在的方法 */ function __call { echo "你所调用的函数:" . $funName . "(参数:" ; // 输出调用不存在的方法名 print_r; // 输出调用不存在的方法时的参数列表 echo ")不存在!
n"; // 结束换行 } }$Person = new Person(); $Person->run; // 调用对象中不存在的方法,则自动调用了对象中的__call()方法$Person->eat; $Person->say(); 

你所调用的函数:run(参数:Array 不存在!

你所调用的函数:eat(参数:Array ( [0] => 小明 [1] => 苹果 )
)不存在!

Hello, world!

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

此方法与上面所说的 __call 是未静态方法准备的之外,其它都是一样的。

"; } /** * 声明此方法用来处理调用对象中不存在的方法 */ public static function __callStatic { echo "你所调用的静态方法:" . $funName . "(参数:" ; // 输出调用不存在的方法名 print_r; // 输出调用不存在的方法时的参数列表 echo ")不存在!
n"; // 结束换行 }}$Person = new Person();$Person::run; // 调用对象中不存在的方法,则自动调用了对象中的__call()方法$Person::eat;$Person->say();

你所调用的静态方法:run(参数:Array
不存在!你所调用的静态方法:eat(参数:Array ( [0] => 小明 [1]
=> 苹果 ) )不存在!Hello, world!

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

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

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

我们通过下面的 __get() 的实例来更进一步的连接它吧:

name = $name; $this->age = $age; } /** * 在类中添加__get()方法,在直接获取属性值时自动调用一次,以属性名作为参数传入并处理 * @param $propertyName * * @return int */ public function __get { if ($propertyName == "age") { if  { return $this->age - 10; } else { return $this->$propertyName; } } else { return $this->$propertyName; } }}$Person = new Person; // 通过Person类实例化的对象,并通过构造方法为属性赋初值echo "姓名:" . $Person->name . "
"; // 直接访问私有属性name,自动调用了__get()方法可以间接获取echo "年龄:" . $Person->age . "
"; // 自动调用了__get()方法,根据对象本身的情况会返回不同的值

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

__set() 的作用:

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

name = $name; $this->age = $age; } /** * 声明魔术方法需要两个参数,真接为私有属性赋值时自动调用,并可以屏蔽一些非法赋值 * @param $property * @param $value */ public function __set { if  { if ($value > 150 || $value < 0) { return; } } $this->$property = $value; } /** * 在类中声明说话的方法,将所有的私有属性说出 */ public function say(){ echo "我叫".$this->name.",今年".$this->age."岁了"; }}$Person=new Person; //注意,初始值将被下面所改变//自动调用了__set()函数,将属性名name传给第一个参数,将属性值”李四”传给第二个参数$Person->name = "小红"; //赋值成功。如果没有__set函数,将属性名age传给第一个参数,将属性值26传给第二个参数$Person->age = 16; //赋值成功$Person->age = 160; //160是一个非法值,赋值失效$Person->say(); //输出:我叫小红,今年16岁了

我叫小红,今年16岁了

七、 __isset或empty()时调用

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

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

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

__isset()的作用:当对不可访问属性调用 isset 时,__isset() 会被调用。

name = $name; $this->age = $age; $this->sex = $sex; } /** * @param $content * * @return bool */ public function __isset { echo "当在类外部使用isset()函数测定私有成员{$content}时,自动调用
"; echo isset; }}$person = new Person; // 初始赋值echo isset,"
";echo isset,"
";echo isset,"
";

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

八、 __unset时被调用。

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

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

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

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

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

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

name = $name; $this->age = $age; $this->sex = $sex; } /** * @param $content * * @return bool */ public function __unset { echo "当在类外部使用unset()函数来删除私有成员时自动调用的
"; echo isset; }}$person = new Person; // 初始赋值unset;unset;unset;

当在类外部使用unset()函数来删除私有成员时自动调用的1当在类外部使用unset()函数来删除私有成员时自动调用的

九、 __sleep时,先会调用这个函数

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

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

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

注意:

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

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

name = $name; $this->age = $age; $this->sex = $sex; } /** * @return array */ public function __sleep() { echo "当在类外部使用serialize方法
"; $this->name = base64_encode; return array; // 这里必须返回一个数值,里边的元素表示返回的属性名称 }}$person = new Person; // 初始赋值echo serialize;echo '
';

当在类外部使用serialize方法O:6:”Person”:2:{s:4:”name”;s:8:”5bCP5piO”;s:3:”age”;i:25;}

十、 __wakeup时,先会调用这个函数

如果说 __sleep 就是黑的了。

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

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

name = $name; $this->age = $age; $this->sex = $sex; } /** * @return array */ public function __sleep() { echo "当在类外部使用serialize方法
"; $this->name = base64_encode; return array; // 这里必须返回一个数值,里边的元素表示返回的属性名称 } /** * __wakeup */ public function __wakeup() { echo "当在类外部使用unserialize方法
"; $this->name = 2; $this->sex = '男'; // 这里不需要返回数组 }}$person = new Person; // 初始赋值var_dump;var_dump(unserialize;

当在类外部使用serialize方法string
“O:6:”Person”:2:{s:4:”name”;s:8:”5bCP5piO”;s:3:”age”;i:25;}”
当在类外部使用serialize方法当在类外部使用unserialize方法object {
[“sex”]=> string “男” [“name”]=> int }

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

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

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

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

name = $name; $this->age = $age; $this->sex = $sex; } public function __toString() { return 'go go go'; }}$person = new Person; // 初始赋值echo $person;

那么如果类中没有 __toString()
这个魔术方法运行会发生什么呢?让我们来测试下:

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 D:phpStudyWWWtestindex.php on line
18很明显,页面报了一个致命错误,这是语法所不允许的。

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

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

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

name = $name; $this->age = $age; $this->sex = $sex; } public function __invoke() { echo '这可是一个对象哦'; }}$person = new Person; // 初始赋值$person();

当然,如果你执意要将对象当函数方法使用,那么会得到下面结果:

Fatal error: Function name must be a string in
D:phpStudyWWWtestindex.php on line 18

十三、 __set_state导出类时,此静态方法会被调用。

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

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

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

name = $name; $this->age = $age; $this->sex = $sex; }}$person = new Person; // 初始赋值var_export;

Person::__set_state(array( ‘sex’ => ‘男’, ‘name’ => ‘小明’,
‘age’ => 25, ))

很明显,将对象中的属性都打印出来了

加了 __set_state() 之后:

name = $name; $this->age = $age; $this->sex = $sex; } public static function __set_state { $a = new Person(); $a->name = $an_array['name']; return $a; }}$person = new Person; // 初始赋值$person->name = '小红';var_export;

Person::__set_state(array( ‘sex’ => ‘男’, ‘name’ => ‘小红’,
‘age’ => 25, ))

十四、 __clone(),当对象复制完成时调用

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

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

$copy_of_object = clone $object;

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

当复制完成时,如果定义了 __clone中的 __clone()
方法会被调用,可用于修改属性的值。看代码:

name = $name; $this->age = $age; $this->sex = $sex; } public function __clone() { echo __METHOD__."你正在克隆对象
"; }}$person = new Person; // 初始赋值$person2 = clone $person;var_dump;var_dump;echo '
';var_dump;var_dump;

Person::__clone你正在克隆对象string “persion1:” object {
[“sex”]=> string “男” [“name”]=> string “小明” [“age”]=>
int “persion2:” object { [“sex”]=> string “男” [“name”]=>
string “小明” [“age”]=> int }

十五、__autoload(),尝试加载未定义的类

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

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

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

还是通过例子来看看吧:

/** * 文件non_autoload.php */ require_once('project/class/A.php'); require_once('project/class/B.php'); require_once('project/class/C.php'); if ; $b = new B; // … 业务逻辑 } else if ; $b = new B(); // … 业务逻辑 }

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

那么如果使用 __autoload() 方式呢?

/** * 文件autoload_demo.php */ function __autoload { $filePath = “project/class/{$className}.php”; if (is_readable { require; } } if ; $b = new B; // … 业务逻辑 } else if ; $b = new B(); // … 业务逻辑 }

ok,不论效率怎么用,最起码界面看起来舒服多了,没有太多冗余的代。

再来看看这里的效率如何,我们分析下:

当php引擎第一次使用类A,但是找不到时,会自动调用 __autoload
方法,并将类名“A”作为参数传入。所以,我们在 __autoload()
中需要的做的就是根据类名,找到相应的文件,并包含进来,如果我们的方法也找不到,那么php引擎就会报错了。

这里可以只用require,因为一旦包含进来后,php引擎再遇到类A时,将不会调用__autoload,而是直接使用内存中的类A,不会导致多次包含。

其实php发展到今天,已经有将 `spl_autoload_register` —
注册给定的函数作为 __autoload
的实现了,但是这个不在啊本文讲解之内,有兴趣可以自行看手册。

十六、__debugInfo(),打印所需调试信息

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

prop = $val; } /** * @return array */ public function __debugInfo() { return [ 'propSquared' => $this->prop ** 2, ]; }}var_dump;

object { [“propSquared”]=> int }

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

总结

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

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

PHP在线手册地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

发表评论

电子邮件地址不会被公开。 必填项已用*标注