搜索
首页后端开发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, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php怎么判断有没有小数点php怎么判断有没有小数点Apr 20, 2022 pm 08:12 PM

php判断有没有小数点的方法:1、使用“strpos(数字字符串,'.')”语法,如果返回小数点在字符串中第一次出现的位置,则有小数点;2、使用“strrpos(数字字符串,'.')”语句,如果返回小数点在字符串中最后一次出现的位置,则有。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace("&nbsp;","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么设置implode没有分隔符php怎么设置implode没有分隔符Apr 18, 2022 pm 05:39 PM

在PHP中,可以利用implode()函数的第一个参数来设置没有分隔符,该函数的第一个参数用于规定数组元素之间放置的内容,默认是空字符串,也可将第一个参数设置为空,语法为“implode(数组)”或者“implode("",数组)”。

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无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前By尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

PhpStorm Mac 版本

PhpStorm Mac 版本

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

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

SecLists

SecLists

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

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。