详解 PHP 反射的基本使用

PHP反射

今天我要给大家讲解的是PHP当中使用非常普遍的高级操作:反射。反射在当今几乎所有的PHP框架或者工具中都占用非常重要的角色,就比如Laravel的容器,容器对于Laravel架构来说极其重要,Laravel的核心类Illuminate\Foundation\Application就是继承自Illuminate\Container\Container类。为了让大家彻底的理解反射,我这里写这篇博文,希望大家能够仔细阅读,我还写了一个简单的容器,这个容器,是我今天上午实现的,它也是基于反射实现的,代码量不大,但也不是那么容易理解,代码已经上传到了码云php-base-container

预备知识

大家可以参考php的官方地址,这里有对反射详细的论述,php反射
对于反射,我们会接触到四个类,分别是:

  1. ReflectionClass
  2. ReflectionFunction
  3. ReflectionMethod
  4. ReflectionParameter

为了让大家明白这个这四个类的作用,我给大家写了一个下面的例子。

class  Printer
{
}

class  Student
{
    private $name;
    private $year;

    public function __construct($name, $year)
    {
        $this->name = $name;
        $this->year = $year;
    }

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

    public function setBase(Printer $printer, $name, $year = 10)
    {
        $this->name = $name;
        $this->year = $year;
    }
}

上面我们声明了2个类Printer和Student,测试如下:

$refl_class = new ReflectionClass(Student::class);
$object = $refl_class->newInstanceArgs(["obama", 100]);
echo get_class($object) . "\n";
echo $object->getValue();

控制台打印结果如下:

如何实现Laravel的容器

首先打印的是Student这个类的类名,然后打印了obama,我来解释下上面的代码, new ReflectionClass(Student::class)创建了一个代表Student类的ReflectionClass对象,这个类的对象有一个方法newInstanceArgs,这个方法可以创建Student::class类的对象,它的参数为Student类构造函数所需要的参数,我们的参数为obama和100,紧接着调用get_class方法获取刚才创建对象的类,也就是我们打印的第一行Student,印证了我们上面所说的。因为$object就是Student类的对象,所以我们可以调用getValue方法,返回值就是name,结果为obama。

上面我们分析了ReflectionClass,下面我们再来分析下面的这个:

$refl_method = $refl_class->getMethod("setBase");
echo get_class($refl_method) . "\n";
$parameters = $refl_method->getParameters();
foreach ($parameters as $parameter) {
    echo $parameter->getName() . "\n";
    if ($parameter->getClass() != null) {
         echo $parameter->getClass()->getName() . "\n";
    }
    if ($parameter->isDefaultValueAvailable()) {
        echo $parameter->getDefaultValue() . "\n";
    }
}

上面的代码运行结果如下

如何实现Laravel的容器

之前我们创建了ReflectionClass类的对象为$refl_class,这里我们调用它的方法getMethod,这个getMethod会返回当前Student类的setBase,但是它是一个ReflectionMethod类的实例,这里的get_class($refl_method)就会打印出$refl_method的类为ReflectionMethod,也就是控制台的第一行结果,ReflectionMethod类有一个方法叫做getParameters,这个方法会返回ReflectionMethod所对应方法(这里就是setBase)的所有参数构成的数组,这个数组的每一个元素都是ReflectionParameter 类的对象,接下来的foreach遍历所有的参数,首先检查$parameter->getClass()的返回值,ReflectionParameter类的getClass方法返回的是这个参数所对应的类,也就是说getClass返回的是ReflectionClass类的对象,ReflectionClass类的对象有一个getName方法,这个方法返回类的名字,ReflectionParameter 类还有一个比较常用的方法,就是isDefaultValueAvailable,他检查这个参数是否有默认的值,如果这个参数有默认的值的话,那么getDefaultValue方法就可以获取到这个默认值。

上面分析了ReflectionMethod和ReflectionParameter两个类,还有一个要讲,如下:

function display($a, $b, Printer $printer)
{
    echo "called" . "\n";
}

$refl_function = new ReflectionFunction("display");
$parameters = $refl_function->getParameters();
foreach ($parameters as $parameter) {
    echo $parameter->getName() . "\n";
    if ($parameter->getClass() != null) {
        echo $parameter->getClass()->getName() . "\n";
    }
    if ($parameter->isDefaultValueAvailable()) {
        echo $parameter->getDefaultValue() . "\n";
    }
}

代码运行结果如下:

如何实现Laravel的容器

上面的代码很简单,首先我定义了名为display的方法,它有三个参数,紧接着看,我们创建了一个ReflectionFunction类的对象,它的参数为函数名,也就是说ReflectionFunction是对函数的封装。
ReflectionFunction类也有一个名为getParameters的方法,他返回的值和ReflectionMethod的getParameters方法的返回值是一样的,都是
ReflectionMethod类的数组,接下来的foreach便利操作和上面讲解ReflectionMethod类的操作的时候是一模一样的,不再详述。

总结

上面基本讲到了PHP反射的基本使用,希望大家仔细的理解我上面所说的内容,如果大家理解了我上面所说的,可以看我实现的php容器:php-base-container,如果大家有啥不懂的,可以联系我,也可以加下面的qq群,可以互相学习。

本作品采用《CC 协议》,转载必须注明作者和本文链接
微信:okayGoHome
本帖由系统于 4年前 自动加精
Dennis_Ritchie
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 6
cnguu

Nice!有没有实际应用的例子?伪代码也可以。

4年前 评论
Dennis_Ritchie (楼主) 4年前
cnguu (作者) 4年前
Krisji 4年前
zpers 4年前

很喜欢你写的文章,点赞!

4年前 评论

支持大佬 :+1:

4年前 评论

关于 php-base-container 代码中对 class container 的理解。
filelaravel_container_1.png

4年前 评论
lovecn 4年前
yxhsea (作者) 4年前
Sam_Edgar 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!