PHP 反射原理在Laravel中的应用
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");
就可以了!我注入什么样的对象,就使用什么样的驱动,而不是每次去修改类,这样解耦的目的就达到了!
- 原文作者:Looper
- 原文链接:http://www.cachesx.com/post/reflection/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。