类反射

什么是反射?

PHP5以及之后具有完整的反射API,动态获取类的方法、属性、参数,注释 等 信息以及动态调用对象的方法的功能称为反射API。
反射是操纵面向对象范型中元模型的API,其功能十分强大,可帮助我们构建复杂,可扩展的应用。比如 Laravel 的容器

功能?

可以获取类的一切信息,包括:
1. 类基本信息(类名、是否是抽象类、是否可实例化、类是否为final或者abstract)
2. 类的方法、方法是否存在、方法返回值、方法的注释、Method Names
3. 类的属性,静态属性,常量
4. 所在命名空间 Namespace

 

通过ReflectionClass,我们可以得到Person类的以下信息:
     1)常量 Contants
     2)属性 Property Names
     3)方法 Method Names静态
     4)属性 Static Properties
     5)命名空间 Namespace
     6)Person类是否为final或者abstract

简单例子:

<?php

class HandsonBoy
{
    public $name = 'Leon';
    public $age = 25;

    public function __set($name, $value)
    {
        echo '您正在设置私有属性' . $name . '<br >值为' . $value . '<br>';
        $this->$name = $value;
    }

    public function __get($name)
    {
        if (!isset($this->$name)) {
            echo '未设置' . $name;
            $this->$name = "正在为你设置默认值" . '<br>';
        }
        return $this->$name;
    }


    public function seeMe()
    {

    }
}

$boy = new HandsonBoy();
echo $boy->name . '<br />';
$boy->hair = 'short';

# 用反射来获取类的属性
$reflect = new ReflectionObject($boy);
$props = $reflect->getProperties();

//反射遍历属性
foreach ($props as $prop) {
    var_dump($prop->getName() . PHP_EOL);
}

//获取对象方法列表

$methods = $reflect->getMethods();
foreach ($methods as $method) {
    var_dump($method->getName() . PHP_EOL);
}

# 当然也可以直接用 get_object_vars 等函数来实现获取类的属性和方法
//对象
echo '<br>';
//var_dump(get_object_vars($boy));
//var_dump(get_class_vars(get_class($boy)));
//返回由类的属性的方法名组成的数组
//var_dump(get_class_methods(get_class($boy)));
<?php

class mysql
{
    function connect($db)
    {
        echo "连接到数据库{$db[0]}".PHP_EOL;
    }
}
class sqlproxy
{
    private $target;
    public function __construct($tar)
    {
        $this->target[] = new $tar;
    }
    public function __call($name,$args)
    {
//        var_dump('name:'.$name);connect
//        var_dump($args);siwei
        foreach($this->target as $obj)
        {
            $r = new ReflectionClass($obj);//获取 mysql_Class 的反射类
            if($method = $r->getMethod($name))
            {
                if($method->isPublic() && !$method->isAbstract())
                {
                    echo "方法前拦截记录LOG".PHP_EOL;
                    $method->invoke($obj,$args);//调用 obj = 实例化之后的mysql类对象的  args =siwei
                    echo "方法后拦截".PHP_EOL;
                }
            }
        }
    }
}
$obj = new sqlproxy('mysql');//实例化sqlproxy 类,初始化 $this->target 赋值 = new mysql()
$obj->connect('siwei'); //调用了魔术方法 __call  name是方法名:connect ||  args 为 参数数组,siwei
// 遍历初始化传过来的 mysql 的反射类  ,然后获取 connect 的方法,存到method 变量中
// 判断 method 是否是抽象方法,如果不是,调用反射的method 的 invoke(new mysql(),siwei)

基于反射实现的容器,依赖注入DI 

<?php

/**
 *
 * 工具类,使用该类来实现自动依赖注入。
 *
 * 使用php的反射函数,创建了一个容器类,使用该类来实现其他类的依赖注入功能。
 *
 * 依赖注入分为两种,
 * 一种是构造函数的依赖注入,
 * 一种是方法的依赖注入。
 * 我们使用下面三个类来做下测试。
 */
class Ioc
{

    // 获得类的对象实例
    public static function getInstance($className)
    {

        $paramArr = self::getMethodParams($className);

        return (new ReflectionClass($className))->newInstanceArgs($paramArr);
    }

    /**
     * 执行类的方法
     * @param  [type] $className  [类名]
     * @param  [type] $methodName [方法名称]
     * @param  [type] $params     [额外的参数]
     * @return [type]             [description]
     */
    public static function make($className, $methodName, $params = [])
    {

        // 获取类的实例
        $instance = self::getInstance($className);

        // 获取该方法所需要依赖注入的参数
        $paramArr = self::getMethodParams($className, $methodName);

        return $instance->{$methodName}(...array_merge($paramArr, $params)); #将 合并之后的数组作为函数参数
    }

    /**
     * 获得类的方法参数,只获得有类型的参数
     * @param  [type] $className   [description]
     * @param  [type] $methodsName [description]
     * @return [type]              [description]
     */
    protected static function getMethodParams($className, $methodsName = '__construct')
    {

        // 通过反射获得该类
        $class = new ReflectionClass($className);
        $paramArr = []; // 记录参数,和参数类型

        // 判断该类是否有构造函数
        if ($class->hasMethod($methodsName)) {
            // 获得构造函数
            $construct = $class->getMethod($methodsName);

            // 判断构造函数是否有参数
            $params = $construct->getParameters();

            if (count($params) > 0) {

                // 判断参数类型
                foreach ($params as $key => $param) {

                    if ($paramClass = $param->getClass()) {

                        // 获得参数类型名称
                        $paramClassName = $paramClass->getName();

                        // 获得参数类型
                        $args = self::getMethodParams($paramClassName);
                        $paramArr[] = (new ReflectionClass($paramClass->getName()))->newInstanceArgs($args);
                    }
                }
            }
        }

        return $paramArr;
    }
}

class A
{

    protected $cObj;

    /**
     * 用于测试多级依赖注入 B依赖A,A依赖C
     * @param C $c [description]
     */
    public function __construct(C $c)
    {

        $this->cObj = $c;
    }

    public function aa()
    {

        echo 'this is A->test';
    }

    public function aac()
    {

        $this->cObj->cc();
    }
}

class B
{

    protected $aObj;

    /**
     * 测试构造函数依赖注入
     * @param A $a [使用引来注入A]
     */
    public function __construct(A $a)
    {

        $this->aObj = $a;
    }

    /**
     * [测试方法调用依赖注入]
     * @param C $c [依赖注入C]
     * @param string $b [这个是自己手动填写的参数]
     * @return [type]    [description]
     */
    public function bb(C $c, $b)
    {

        $c->cc();
        echo "\r\n";

        echo 'params:' . $b;
    }

    /**
     * 验证依赖注入是否成功
     * @return [type] [description]
     */
    public function bbb()
    {

        $this->aObj->aac();
    }
}

class C
{

    public function cc()
    {

        echo 'this is C->cc';
    }
}


// 使用Ioc反射 来创建B类的实例,B的构造函数依赖A类,A的构造函数依赖C类。最终调用 C的 cc方法 this is C->cc , 说明依赖注入成功。
$bObj = Ioc::getInstance('B');
$bObj->bbb();
var_dump($bObj);


# 打印结果

this is C->cc

object(B)#3 (1) {
["aObj":protected]=>
  object(A)#7 (1) {
  ["cObj":protected]=>
    object(C)#10 (0) {
    }
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值