搜索
首页后端开发php教程PHP基础教程十三之反射、对象序列化

本节讲解的内容

  • 对象的克隆

  • 对象的遍历

  • 对象的序列化和反序列化

  • 内置标准类的使用

  • traits的使用

  • 类和对象的相关函数

  • PHP反射机制

前言

PHP的面向对象是一个重要的知识点,它的思想贯穿着我们开发的整个流程。在面向对象中还有一些知识点是需要我们去了解的,对象克隆的特点以及对象的遍历,对象的序列化和反序列化,如果你想写一个PHP的框架,那么你对PHP的反射也是要掌握的。

对象的克隆

当我们创建一个对象后,就会在内存中分配一个空间,对象名指向这个空间,前面我们讲过对象的赋值,当一个对象把里面的数据修改了,另一个对象的数据也会跟着变化,因为赋值是相当于把对象标识符赋值了一份,而克隆并不是这样的。

<?php

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

    public function getName(){
        return $this -> name;
    }

    public function getAge(){
        return $this -> age;
    }

    public function getSex(){
        return $this -> sex;
    }
}

$a = new Person(&#39;宋江&#39;,&#39;45&#39;,&#39;男&#39;);
echo &#39;<pre class="brush:php;toolbar:false">&#39;;

$b = clone $a;
$b -> name = &#39;武松&#39;;
var_dump($a);
var_dump($b);

结果

对象克隆的语法:

$新对象 = clone $就对象;

从上面的结果中可以看出来,一个对象的数据变化,并不会影响到另外一个对象的数据。对象克隆会生成一个全新的对象。可以理解如下:

在PHP的魔术方法中有一个魔术方法和PHP的克隆有关的__clone(),在复制完成时,如果在类里面定义了魔术方法__clone()方法,则新创建也就是复制生成的对象会调用这个__clone()方法,在这个方法中如果有需要可以对属性做一些变动。

如果不想一个对象进行克隆可以在内部把魔术方法__clone()方法定义成私有的,这时如果在进行克隆,会提示

Call to private Peoson::__clone() from context &#39;&#39;

对象的遍历

在PHP中对象也是可以遍历的,对象的遍历可以理解成把某个对象的属性和值,遍历显示出来。遍历使用到foreach这个循环体。基本语法:

foreach(对象名 as $key => $val){
    //$key是属性名,$val是属性值
}

<?php

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

$person = new Person(&#39;孙悟空&#39;,256,&#39;男&#39;);

foreach ($person as $key => $value) {
    echo $key . &#39; = &#39; . $value . &#39;<br>&#39;;
}
.......结果........
name = 孙悟空
age = 256

对象的遍历只能把属性的修饰符是public的遍历出来,protected和private不能再类外访问,所以在类外进行对象的遍历,用这两个修饰符修饰的属性是取不出来的,就像上面代码的sex属性一样。

对象的序列化和反序列化

在PHP中当程序执行完一个文件,就会自动的释放内存,我们在文件中创建的对象,变量等,都会消失,如果我们在一个文件创建了一个对象,在另外一个对象中想要使用这个对象,就可以使用到对象的序列化(serialize())和反序列化(unserialize())。

对象的序列化和反序列化可以理解成把对象转换成字符串保存在一个文件中,在用到对象时把文件进行反序列化得到对象,对象是不能直接保存在文件中的。

示意图:

a.php文件,创建一个对象并保存:

<?php

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

$person = new Person(&#39;孙悟空&#39;,256,&#39;男&#39;);
//使用file_put_contents()函数把将一个字符串写入文件 
file_put_contents(&#39;D:person.txt&#39;, serialize($person));

上面的代码file_put_contents()函数是把一个字符串写入到一个文件中。
上面的代码执行完可以看到在D盘有一个person.txt文件,里面是一个转换成字符串的对象。

b.php文件,使用到a.php文件中的对象person

<?php

    class Person{
        public $name;
        public $age;
        private $sex;
        public function __construct($name,$age,$sex){
            $this -> name = $name;
            $this -> age = $age;
            $this -> sex = $sex;
        }
    }
    //通过file_get_contents这个方法把一个文件读取到字符串。
    $str = file_get_contents(&#39;D:person.txt&#39;);
    //进行反序列化。
    $person = unserialize($str);
    echo &#39;<pre class="brush:php;toolbar:false">&#39;;
    var_dump($person);
    ......结果......
    object(Person)#1 (3) {
      ["name"]=>
      string(9) "孙悟空"
      ["age"]=>
      int(256)
      ["sex":"Person":private]=>
      string(3) "男"
    }

在b.php中通过file_get_contents()这个方法把一个文件读取到字符串,然后通过unserialize()反序列化,但是这样反序列化得到的对象不是person对象,所以在文件中把person类的声明粘贴复制过来,自动转换成person对象。

对象的序列化和反序列化可以让多个文件共享一个对象。

PHP内置标准类

有时我们希望把一些数据,以对象的属性的方式存储,同时我们又不想定义一个类,可以考虑使用PHP内置标准类 stdClass,这是用系统提供的一个虚拟的类,并不需要我们定义就可以直接使用。

<?php

    $person = new StdClass();
    $person -> name = &#39;孙悟空&#39;;
    $person -> age = 524;
    $person -> sex = &#39;男&#39;;
    echo &#39;<pre class="brush:php;toolbar:false">&#39;;
    var_dump($person);
    ......结果......
    object(stdClass)#1 (3) {
      ["name"]=>
      string(9) "孙悟空"
      ["age"]=>
      int(524)
      ["sex"]=>
      string(3) "男"
    }

在上面代码中我们可以看出来,并没有定义stdClass这个类就能使用。

Traits的使用

Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用方法集。可以理解为在traits中定义一段代码块,可以在不同的类中使用。

图解:

traits使用语法:

trait 自定义名称{
    //额外定义的代码
}
在类中使用格式
use 自定义的名称;

代码:

<?php
    //使用trait,自定义名称
    trait my_code{
        public function minus($num1,$num2){
            return $num1 - $num2;
        }
    }

    class A{
        public function plus($num1,$num2){
            return $num1 + $num2;
        }
    }

    class B extends A{
        //使用trait定义的代码块
        use my_code;
    }

    class C extends A{
        use my_code;
    }

    class D extends A{

    }

    $b = new B();
    $c = new C();
    $d = new D();
    echo $b -> plus(1,2);
    echo &#39;<br>&#39;;
    echo $b -> minus(2,1);
    echo &#39;<br>&#39;;

    echo $d -> plus(1,2);
    echo &#39;<br>&#39;;
    echo $d -> minus(2,1);
    ......结果......
    3
    1
    3
    Fatal error: Call to undefined method D::minus() in D:\mywamp\Apache24\htdocs\zendstudio\yunsuanfu\staits.php on line 36

在上面代码中类D没有使用trait代码,所以在使用minus方法时,出现错误。

当我们在trait中写的函数和父类的函数一样时,以trait代码为准,即trait代码的优先级高于继承的类。

类与对象相关函数

在PHP中系统提供了一系列的函数来判断类和对象的。从帮助文档中可以看到好多函数:

在这里我们只对里面的几个函数进行了解。

<?php

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

        public function showInfo(){
            echo &#39;名字是:&#39; . $this -> name . &#39;,年龄是:&#39; . $this -> age . &#39;,性别是:&#39; . $this -> sex . &#39;<br>&#39;; 
        }
    }

    //判断是否创建了对象,没有创建返回true,创建返回false。
    if(class_exists(&#39;Person&#39;)){
        $person = new Person(&#39;唐僧&#39;,25,&#39;男&#39;);
        //返回对象的类名
        echo &#39;类名是: &#39; . get_class($person) . &#39;<br>&#39;;
        //判断方法是否存在。
        if(method_exists($person, &#39;showInfo&#39;)){
            $person -> showInfo();
        }else{
            echo &#39;该方法不存在&#39;;
        }
        //判断属性是否存在
        if(property_exists($person,&#39;name&#39;)){
            echo $person -> name;
        }else{
            echo &#39;属性不存在&#39;;
        }
    }else{
        echo &#39;类不存在&#39;;
    }
    ......结果......
    类名是: Person
    名字是:唐僧,年龄是:25,性别是:男
    唐僧

使用类和对象函数,可以保证我们代码的完整性,对出错信息进行及时的捕获输出。

PHP反射机制

在很多编程语言中都有反射这种概念,反射简单理解就是通过类,获取里面的属性,方法,甚至注释也可以,不管属性和方法的访问修饰符是什么类型都可以获取到。

在PHP 5中具有完整的反射API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。而我们在开发中一般是使用不到反射的,但是在某些情况下使用反射可以更好的处理问题,比如我们需要我们写框架底层,扩展功能,管理大量的未知类。

定义一个类,通过反射创建对象和调用里面的方法:

<?php

    class Dog{

        public $name;
        protected $age;
        private $food;

        public function __construct($name, $age, $food){

            $this->name = $name;
            $this->age = $age;
            $this->food = $food;
        }

        public function cry($sound){

            echo &#39;<br> &#39; . $this->name . &#39; 叫声是.&#39; . $sound;
        }

    }

    //使用反射完成对象的创建和方法调用
    //1. 创建一个反射对象
    $reflect_obj = new ReflectionClass(&#39;Dog&#39;);

    //2. 通过反射对象创建Dog对象实例
    $dog = $reflect_obj->newInstance(&#39;大黄狗&#39;, 4, &#39;排骨&#39;);
    echo &#39;<pre class="brush:php;toolbar:false">&#39;;
    var_dump($dog);

    //3. 调用方法-使用代理方式.
    $reflect_method_cry = $reflect_obj->getMethod(&#39;cry&#39;);
    echo &#39;<pre class="brush:php;toolbar:false">&#39;;
    var_dump($reflect_method_cry);
    //4. 代理调用cry
    $reflect_method_cry->invoke($dog, &#39;汪汪&#39;);

结果:


在上面代码中,我们通过new创建了一个反射的对象,在反射对象里面通过newInstance()方法得到类的对象。获取里面的方法可以使用反射对象的getMethod()方法,返回来的是一个方法对象ReflectionMethod类,通过里面的invoke()方法执行该方法。这里只是基本的介绍,可以查找帮助文档了解更多的反射对象和方法。

反射实现TP控制器调度

需求:

有一个类IndexAction,其中的方法和访问控制修饰符是不确定的,
1. 如果index 方法是public,可以执行 _before_index.
2. 如果存在_before_index 方法,并且是public的,执行该方法
3. 执行test方法
4. 再判断有没有_after_index方法,并且是public的,执行该方法

代码:

<?php

    class IndexAction{
        public function index(){
            echo &#39;index<br>&#39;;
        }

        public function _before_index(){
            echo &#39;_before_index方法执行 <br>&#39;;
        }

        public function test($data){
            echo &#39;data : &#39;  . $data . &#39;<br>&#39;;
        }

        public  function _after_index(){
            echo &#39;_after_index方法执行<br>&#39;;
        }
    }

    if(class_exists(&#39;IndexAction&#39;)){
        //创建对象
        $reflectionClass = new ReflectionClass(&#39;IndexAction&#39;);
        //判断index是否存在
        if($reflectionClass -> hasMethod(&#39;index&#39;)){

            //获取index方法对象
            $reflec_index_method = $reflectionClass -> getMethod(&#39;index&#39;);
            //判断修饰符是否是public
            if($reflec_index_method -> isPublic()){
                //判断是否有_before_index方法
                if($reflectionClass -> hasMethod(&#39;_before_index&#39;)){
                    $reflec_before_method = $reflectionClass -> getMethod(&#39;_before_index&#39;);
                    //判断是否是public
                    if($reflec_before_method -> isPublic()){
                        $reflec_before_method -> invoke($reflectionClass -> newInstance());
                        //调用test()方法
                        $reflectionClass -> getMethod(&#39;test&#39;) -> invoke($reflectionClass -> newInstance(),&#39;这是test的数据&#39;);
                        //判断是否有_after_index方法
                        if($reflectionClass -> hasMethod(&#39;_after_index&#39;)){
                            $reflec_after_method = $reflectionClass -> getMethod(&#39;_after_index&#39;);
                            //判断是否是public
                            if($reflec_after_method -> isPublic()){
                                //执行_after_index方法
                                $reflec_after_method -> invoke($reflectionClass -> newInstance());

                            }else{
                                echo &#39;_after_index不是public修饰的&#39;;
                            }

                        }else{
                            echo &#39;没有_after_index方法&#39;;
                        }


                    }else{
                        echo &#39;_before_index修饰符不是public&#39;;
                    }

                }else{
                    echo &#39;没有_before_index方法&#39;;
                }


            }else{
                echo &#39;index方法不是public修饰&#39;;
            }


        }else{
            echo &#39;index方法不存在&#39;;
        }

    }else{
        echo &#39;类名不存在&#39;;
    }
    ......结果.......
    _before_index方法执行 
    data : 这是test的数据
    _after_index方法执行

在上面的代码中可以看到我们不停地在判断类中有没有某个方法,是不是public修饰,然后执行,我们可以利用封装的思想,把一些共性的特征抽取出来写成一个函数。从而对我们的代码进行优化。

优化的代码:

<?php

    class IndexAction{
        public function index(){
            echo &#39;index<br>&#39;;
        }

        public function _before_index(){
            echo &#39;_before_index方法执行 <br>&#39;;
        }

        public function test($data){
            echo &#39;data : &#39;  . $data . &#39;<br>&#39;;
        }

        public  function _after_index(){
            echo &#39;_after_index方法执行<br>&#39;;
        }
    }

    if(class_exists(&#39;IndexAction&#39;)){
        $reflectionClass = new ReflectionClass(&#39;IndexAction&#39;);
        //创建IndexAction对象。
        $indexAction = $reflectionClass -> newInstance();

        execute($reflectionClass,$indexAction,&#39;index&#39;);
        execute($reflectionClass,$indexAction,&#39;_after_index&#39;);
        execute($reflectionClass,$indexAction,&#39;test&#39;,&#39;test使用的数据&#39;);
        execute($reflectionClass,$indexAction,&#39;_after_index&#39;);
    }else{
        echo &#39;没有IndexAction方法&#39;;
    }

    //封装的函数
    /**
     * [execute description]对反射的封装。
     * @param  [type]  $reflect_obj [description]反射对象
     * @param  [type]  $worker      [description]类对象
     * @param  [type]  $name        [description]方法的名字
     * @param  [type]  $canshu      [description]方法的参数
     * @return boolean              [description]
     */
    function execute($reflect_obj,$indexAction,$name,$param = &#39;&#39;){
        if($reflect_obj-> hasMethod($name)){
            $method = $reflect_obj->getMethod($name);
        if($method->isPublic()){
            $method->invoke($indexAction,$param); 
        }else{
            echo $name . &#39;不是public&#39;;
        }
        }else{
            echo $name . &#39;方法不存在&#39;;
        }
    }
    ......结果.....
    index
    _after_index方法执行
    data : test使用的数据
    _after_index方法执行

可以看到进行功能的封装,可以简化我们的代码,同时代码看起来更加的清晰。

总结

PHP的面向对象的内容到这里算是讲完了,在开发中利用面向对象的思想进行开发是一定要掌握的技能。同时也要对面向对象进行深度的了解。

 以上就是PHP,反射、对象序列化的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
继续使用PHP:耐力的原因继续使用PHP:耐力的原因Apr 19, 2025 am 12:23 AM

PHP仍然流行的原因是其易用性、灵活性和强大的生态系统。1)易用性和简单语法使其成为初学者的首选。2)与web开发紧密结合,处理HTTP请求和数据库交互出色。3)庞大的生态系统提供了丰富的工具和库。4)活跃的社区和开源性质使其适应新需求和技术趋势。

PHP和Python:探索他们的相似性和差异PHP和Python:探索他们的相似性和差异Apr 19, 2025 am 12:21 AM

PHP和Python都是高层次的编程语言,广泛应用于Web开发、数据处理和自动化任务。1.PHP常用于构建动态网站和内容管理系统,而Python常用于构建Web框架和数据科学。2.PHP使用echo输出内容,Python使用print。3.两者都支持面向对象编程,但语法和关键字不同。4.PHP支持弱类型转换,Python则更严格。5.PHP性能优化包括使用OPcache和异步编程,Python则使用cProfile和异步编程。

PHP和Python:解释了不同的范例PHP和Python:解释了不同的范例Apr 18, 2025 am 12:26 AM

PHP主要是过程式编程,但也支持面向对象编程(OOP);Python支持多种范式,包括OOP、函数式和过程式编程。PHP适合web开发,Python适用于多种应用,如数据分析和机器学习。

PHP和Python:深入了解他们的历史PHP和Python:深入了解他们的历史Apr 18, 2025 am 12:25 AM

PHP起源于1994年,由RasmusLerdorf开发,最初用于跟踪网站访问者,逐渐演变为服务器端脚本语言,广泛应用于网页开发。Python由GuidovanRossum于1980年代末开发,1991年首次发布,强调代码可读性和简洁性,适用于科学计算、数据分析等领域。

在PHP和Python之间进行选择:指南在PHP和Python之间进行选择:指南Apr 18, 2025 am 12:24 AM

PHP适合网页开发和快速原型开发,Python适用于数据科学和机器学习。1.PHP用于动态网页开发,语法简单,适合快速开发。2.Python语法简洁,适用于多领域,库生态系统强大。

PHP和框架:现代化语言PHP和框架:现代化语言Apr 18, 2025 am 12:14 AM

PHP在现代化进程中仍然重要,因为它支持大量网站和应用,并通过框架适应开发需求。1.PHP7提升了性能并引入了新功能。2.现代框架如Laravel、Symfony和CodeIgniter简化开发,提高代码质量。3.性能优化和最佳实践进一步提升应用效率。

PHP的影响:网络开发及以后PHP的影响:网络开发及以后Apr 18, 2025 am 12:10 AM

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP类型提示如何起作用,包括标量类型,返回类型,联合类型和无效类型?PHP类型提示如何起作用,包括标量类型,返回类型,联合类型和无效类型?Apr 17, 2025 am 12:25 AM

PHP类型提示提升代码质量和可读性。1)标量类型提示:自PHP7.0起,允许在函数参数中指定基本数据类型,如int、float等。2)返回类型提示:确保函数返回值类型的一致性。3)联合类型提示:自PHP8.0起,允许在函数参数或返回值中指定多个类型。4)可空类型提示:允许包含null值,处理可能返回空值的函数。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境