PHP 反射

什么是反射?

反射是一种在运行时检查类和方法的能力,通俗点讲就是根据类名获取类的任何信息,比如该类有什么方法,参数,变量等等,我们先简单练习一下:

class Cache{
    protected $client;
    public function __construct($client){
        $this->client = $client;
    }
}
// 初始化 ReflectionClass 类
$reflectionClass = new ReflectionClass('Cache');
// 反射Cache类的构造函数
$reflectionConstruct = $reflectionClass->getConstructor();
// 获取构造函数所有依赖的参数
$reflectionParams = $reflectionConstruct->getParameters();
// 创建类的新的实例。给出的参数将会传递到类的构造函数
$newInstance = $reflectionClass->newInstance($reflectionParams);

我们接着之前控制反转容器的例子修改一下

interface CacheDriver{
}

class RedisDriver implements CacheDriver{
    // redis Driver
    function connect(){
        echo 'Redis Driver';
    }
}
class MemcacheDriver implements CacheDriver{
    // memecache Driver
    function connect(){
        echo 'Memcache Driver';
    }
}
class MongoDriver implements CacheDriver{
    // mongo Driver
    function connect(){
        echo 'Mongo Driver';
    }
}
class Container{
    protected $bindings=[];
    //绑定接口和生成相应实例的回调函数
    public function bind($abstract, $concrete=null, $shared=false){
        if (!$concrete instanceof Closure) {
            //如果提供的参数不是回调函数,则产生默认的回调函数
            $concrete = $this->getClosure($abstract, $concrete);
        }
        // 将匿名函数注册到bindings成员变量里,bindings有两个元素concrete, shared
        $this->bindings[$abstract] = compact('concrete', 'shared');
    }
    //获取建立类型时要使用的闭包。
    protected function getClosure($abstract,$concrete){
        //生成实例的回调函数,$c为Ioc容器对象,在调用回调生成实例时提供
        return function($c) use ($abstract, $concrete){
            $method= ($abstract == $concrete) ? 'build' : 'make';
            //调用的是容器的build或make方法生成实例
            return $c->$method($concrete);
        };
    }
    //生成实例对象,首先解决接口和要实例化类之间的依赖关系
    public function make($abstract){
        //获取具体的实例
        $concrete = $this->getConcrete($abstract);
        //如果抽象实例和具体实例相同,或者抽象实例是抽象类的实例
        if($this->isBuildable($concrete, $abstract)){
            $object = $this->build($concrete);
        }else{
            $object = $this->make($concrete);
        }
        return $object;
    }

    protected function isBuildable($concrete, $abstract){
        return $concrete === $abstract || $concrete instanceof Closure;
    }
    //获取绑定的回调函数
    protected function getConcrete($abstract){
        if(!isset($this->bindings[$abstract])){
            return $abstract;
        }
        return $this->bindings[$abstract]['concrete'];
    }
    //实例化对象
    public function build($concrete){
        if($concrete instanceof Closure){
            return $concrete($this);
        }
        //反射类
        $reflector = new ReflectionClass($concrete);

        //是否可实例化
        if (!$reflector->isInstantiable()) {
            echo $message="Traget [$concrete] is not instantiable";
        }
        //反射构造函数,如果无构造函数,直接实例化该类
        $constructor=$reflector->getConstructor();
        if(is_null($constructor)){
            return new $concrete;
        }
        //获取依赖参数
        $dependencies = $constructor->getParameters();

        $instances=$this->getDependencies($dependencies);
        return $reflector->newInstanceArgs($instances);
    }
    //解决通过反射机制实例化对象时的依赖
    protected function getDependencies($parameters){
        $dependencies=[];
        foreach($parameters as $parameter){
            // 获取对象的类名
            $dependency = $parameter->getClass();
            if (is_null($dependency)) {
                $dependencies[] = null;
            } else {
                $dependencies[] = $this->resolveClass($parameter);
            }

        }
        return (array) $dependencies;
    }
    protected function resolveClass(ReflectionParameter $parameter){
        return $this->make($parameter->getClass()->name);
    }
}

class FileCache{
    protected $cacheDriver;
    public function __construct(CacheDriver $cacheDriver){
        $this->cacheDriver = $cacheDriver;
    }
    public function cacheHtml(){
        $this->cacheDriver->connect();
    }
}
//实例化Ioc容器
$app = new Container();
//完成容器的填充
$app->bind("CacheDriver", "MemcacheDriver");;
$app->bind("FileCache", "FileCache");
//通过容器实例依赖注入,完成类的实例化
$file_cache = $app->make("FileCache");
$file_cache->cacheHtml();
$redis_driver = $app->make("MemcacheDriver");
$redis_driver->connect();

通过容器,我们就不需要使用new去实例化对象了,使用make就可以实例化一个类,但是FileCache的函数是需要一个构造参数的,可是我们并没有提供这样的参数,这就是IoC强大之处了, 调用make实例化对象的时候,容器会使用反射功能,去分析我们要实例化对象的构造函数,获取构造函数所需的每个参数,然后分别去实例化这些参数,如果实例化这些参数也要参数,那么就再去实例化参数的参数,到最后实例化我们的 MemcacheDriver,如果我们想使用Redis作为驱动的话$app->bind("CacheDriver", "RedisDriver");就可以了!我注入什么样的对象,就使用什么样的驱动,而不是每次去修改类,这样解耦的目的就达到了!