代码改变世界

深入 Laravel 内核之 PHP 反射机制和依赖注入

2020-06-19 17:51  小伍2013  阅读(696)  评论(0编辑  收藏  举报

结论:

  • PHP中提供了反射类来解析类的结构;
  • 通过反射类可以获取到类的构造函数及其参数和依赖;
  • 给构造函数的参数递归设置默认值后,即可使用这些带默认值的参数通过 newInstanceArgs 实例化出类对象;
  • 在实例化的过程中,依赖的类也会被实例化,从而实现了依赖注入。

PHP中反射类的常用方法:

// 通过类名 Circle 实例化反射类
$reflectionClass = new reflectionClass(Circle::class);
// 获取类常量
$reflectionClass->getConstants();
// 获取类属性
$reflectionClass->getProperties();
// 获取类方法
$reflectionClass->getMethods();
// 获取类的构造函数
$constructor = $reflectionClass->getConstructor();
// 获取方法的参数
$parameters = $constructor->getParameters();
// 通过参数默认值数组实例化类创建对象
$class = $reflectionClass->newInstanceArgs($dependencies);

创建两个具有依赖关系的类:

class Point
{
    public $x;
    public $y;

    public function __construct($x = 0, $y = 0)
    {
        $this->x = $x;
        $this->y = $y;
    }
}

class Circle
{
    // 圆的半径
    public $radius;

    // 圆心位置
    public $center;

    const PI = 3.14;

    public function __construct(Point $point, $radius = 1)
    {
        $this->center = $point;
        $this->radius = $radius;
    }

    // 打印圆心位置的坐标
    public function printCenter()
    {
        printf('圆心的位置是:(%d, %d)', $this->center->x, $this->center->y);
    }

    //计算圆形的面积
    public function area()
    {
        return 3.14 * pow($this->radius, 2);
    }
}

创建两个方法:

make 方法负责解析类和构建类的对象;

getDependencies 负责依赖解析并初始化参数默认值;

//构建类的对象
function make($className)
{
    try {
        $reflectionClass = new ReflectionClass($className);
        $constructor = $reflectionClass->getConstructor();
        $parameters  = $constructor->getParameters();
        $dependencies = getDependencies($parameters);

        // 用指定的参数创建一个新的类实例
        return $reflectionClass->newInstanceArgs($dependencies);
    } catch (ReflectionException $e) {
        throw new Exception("{$className} not found.");
    }
}

//依赖解析并初始化参数默认值
function getDependencies($parameters)
{
    $dependencies = [];
    // Circle 类的参数为 point 和 radius
    // Point 类的参数为 x 和 y
    foreach($parameters as $parameter) {
        $dependency = $parameter->getClass();
        // 如果参数不是类
        if (is_null($dependency)) {
            // 可选参数,有默认值
            if($parameter->isDefaultValueAvailable()) {
                $dependencies[] = $parameter->getDefaultValue();
            }
            // 必传参数暂时先给一个默认值
            else {
                $dependencies[] = '0';
            }
        }
        // 如果参数是类
        else {
            // 递归进行依赖解析
            $dependencies[] = make($parameter->getClass()->name);
        }
    }

    return $dependencies;
}

测试:

// 实例化对象
$circle = make('Circle');
// 调用对象的方法
$area = $circle->area();
var_dump($circle, $area);

过程说明:

make('Circle')
	getDependencies(Point, int)
		make('Point')
			getDependencies(int, int)
			new Point(int, int);
		return Point;
	new Circle(Point, int);
return Circle;