PHP에서 리플렉션 사용에 대한 자세한 설명

coldplay.xixi
풀어 주다: 2023-04-08 22:20:01
앞으로
2574명이 탐색했습니다.

PHP에서 리플렉션 사용에 대한 자세한 설명

실제 개발에 반영을 적용하는 방법에 대해 이야기해보겠습니다.

  • 자동으로 문서 생성
  • MVC 아키텍처 구현
  • 단위 테스트 구현
  • DI 컨테이너와 협력하여 종속성 해결
  • ...

문서 자동 생성

클래스, 인터페이스, 함수의 내부 구조와 메소드 분석 리플렉션 기반 메소드 문서는 함수 매개변수는 물론 클래스 속성 및 메소드에 대해 자동으로 생성될 수 있습니다.

/**
 * 学生类
 *
 * 描述信息
 */
class Student
{
    const NORMAL = 1;
    const FORBIDDEN = 2;
    /**
     * 用户ID
     * @var 类型
     */
    public $id;
    /**
     * 获取id
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }
    public function setId($id = 1)
    {
        $this->id = $id;
    }
}
$ref = new ReflectionClass('Student');
$doc = $ref->getDocComment();
echo $ref->getName() . ':' . getComment($ref) , "\n";
echo "属性列表:\n";
printf("%-15s%-10s%-40s\n", 'Name', 'Access', 'Comment');
$attr = $ref->getProperties();
foreach ($attr as $row) {
    printf("%-15s%-10s%-40s\n", $row->getName(), getAccess($row), getComment($row));
}
echo "常量列表:\n";
printf("%-15s%-10s\n", 'Name', 'Value');
$const = $ref->getConstants();
foreach ($const as $key => $val) {
    printf("%-15s%-10s\n", $key, $val);
}
echo "\n\n";
echo "方法列表\n";
printf("%-15s%-10s%-30s%-40s\n", 'Name', 'Access', 'Params', 'Comment');
$methods = $ref->getMethods();
foreach ($methods as $row) {
    printf("%-15s%-10s%-30s%-40s\n", $row->getName(), getAccess($row), getParams($row), getComment($row));
}
// 获取权限
function getAccess($method)
{
    if ($method->isPublic()) {
        return 'Public';
    }
    if ($method->isProtected()) {
        return 'Protected';
    }
    if ($method->isPrivate()) {
        return 'Private';
    }
}
// 获取方法参数信息
function getParams($method)
{
    $str = '';
    $parameters = $method->getParameters();
    foreach ($parameters as $row) {
        $str .= $row->getName() . ',';
        if ($row->isDefaultValueAvailable()) {
            $str .= "Default: {$row->getDefaultValue()}";
        }
    }
    return $str ? $str : '';
}
// 获取注释
function getComment($var)
{
    $comment = $var->getDocComment();
    // 简单的获取了第一行的信息,这里可以自行扩展
    preg_match('/\* (.*) *?/', $comment, $res);
    return isset($res[1]) ? $res[1] : '';
}
로그인 후 복사
​ ​

php file.php를 실행하면 해당 문서 정보를 확인할 수 있습니다. php file.php 就可以看到相应的文档信息。

实现 MVC 架构

现在好多框架都是 MVC

MVC 아키텍처 구현
이제 많은 프레임워크는 라우팅 정보를 기반으로 컨트롤러를 찾는 MVC 아키텍처를 사용합니다( $controller) 및 메서드 이름($method)을 지정한 다음 리플렉션을 사용하여 자동 호출을 구현합니다.

PHP에서 리플렉션 사용에 대한 자세한 설명

$class = new ReflectionClass(ucfirst($controller) . 'Controller');
$controller = $class->newInstance();
if ($class->hasMethod($method)) {
    $method = $class->getMethod($method);
    $method->invokeArgs($controller, $arguments);
} else {
    throw new Exception("{$controller} controller method {$method} not exists!");
}
로그인 후 복사

단위 테스트 구현

일반적으로 함수와 클래스를 테스트하여 예상대로 결과를 반환할 수 있는지 확인합니다. 리플렉션을 사용하여 간단하고 일반적인 클래스 테스트 사례를 구현할 수 있습니다.


class Calc
{
    public function plus($a, $b)
    {
        return $a + $b;
    }
    public function minus($a, $b)
    {
        return $a - $b;
    }
}
function testEqual($method, $assert, $data)
{
    $arr = explode('@', $method);
    $class = $arr[0];
    $method = $arr[1];
    $ref = new ReflectionClass($class);
    if ($ref->hasMethod($method)) {
        $method = $ref->getMethod($method);
        $res = $method->invokeArgs(new $class, $data);
        var_dump($res === $assert);
    }
}
testEqual('Calc@plus', 3, [1, 2]);
testEqual('Calc@minus', -1, [1, 2]);
로그인 후 복사
​ ​ PHPUnit 单元测试框架很大程度上依赖了 Reflection 的特性,可以了解下。

配合 DI 容器解决依赖

Laravel 等许多框架都是使用 Reflection 解决依赖注入问题,具体可查看 Laravel 源码进行分析。
下面我们代码简单实现一个 DI 容器演示 Reflection클래스에 대한 테스트 방법이며, 리플렉션을 사용하여 함수 테스트 방법을 구현할 수도 있습니다.
이것은 제가 간단히 작성한 테스트 사례입니다. PHPUnit 단위 테스트 프레임워크는 Reflection의 기능에 크게 의존합니다.

DI 컨테이너와 협력하여 종속성을 해결

calcdemo 的顺序,不能颠倒,不然的话会报错,原因是由于 Demo 依赖 Calc,首先要定义依赖关系。
Demo 实例化的时候,会用到 Calc 类,也就是说 Demo 依赖于 Calc,但是在 $data 上面找不到的话,会抛出错误,所以首先要定义            $di->calc = 'Calc'Laravel 및 기타 여러 프레임워크에서는 Reflection은 종속성 주입 문제를 해결합니다. 자세한 내용은 분석을 위한 <code>Laravel 소스 코드를 참조하세요.

다음 코드는 종속성 주입 문제를 해결하기 위해 DI 컨테이너 데모 Reflection을 구현합니다.

class DI
{
    protected static $data = [];
    public function __set($k, $v)
    {
        self::$data[$k] = $v;
    }
    public function __get($k)
    {
        return $this->bulid(self::$data[$k]);
    }
    // 获取实例
    public function bulid($className)
    {
        // 如果是匿名函数,直接执行,并返回结果
        if ($className instanceof Closure) {
            return $className($this);
        }
        
        // 已经是实例化对象的话,直接返回
        if(is_object($className)) {
            return $className;
        }
        // 如果是类的话,使用反射加载
        $ref = new ReflectionClass($className);
        // 监测类是否可实例化
        if (!$ref->isInstantiable()) {
            throw new Exception('class' . $className . ' not find');
        }
        // 获取构造函数
        $construtor = $ref->getConstructor();
        // 无构造函数,直接实例化返回
        if (is_null($construtor)) {
            return new $className;
        }
        // 获取构造函数参数
        $params = $construtor->getParameters();
        // 解析构造函数
        $dependencies = $this->getDependecies($params);
        // 创建新实例
        return $ref->newInstanceArgs($dependencies);
    }
    // 分析参数,如果参数中出现依赖类,递归实例化
    public function getDependecies($params)
    {
        $data = [];
        foreach($params as $param)
        {
            $tmp = $param->getClass();
            if (is_null($tmp)) {
                $data[] = $this->setDefault($param);
            } else {
                $data[] = $this->bulid($tmp->name);
            }
        }
        return $data;
    }
    
    // 设置默认值
    public function setDefault($param)
    {
        if ($param->isDefaultValueAvailable()) {
            return $param->getDefaultValue();
        }
        throw new Exception('no default value!');
    }
}
class Demo
{
    public function __construct(Calc $calc)
    {
        echo $calc->plus(1, 2);
    }
}
$di = new DI();
$di->calc = 'Calc'; // 加载单元测试用例中 Calc 类
$di->demo = 'Demo';
$di->demo;
로그인 후 복사

위의 calcdemo의 순서는 바뀔 수 없습니다. 그렇지 않으면 오류가 보고됩니다. 이유는 Demo가 다음에 달려 있기 때문입니다. Calc code>, 먼저 종속성을 정의합니다. <strong><code>Demo가 인스턴스화되면 Calc 클래스가 사용됩니다. 이는 DemoCalc에 종속된다는 의미입니다. $data에 없으면 오류가 발생하므로 먼저 $di->calc = 'Calc'를 정의해야 합니다.

Reflection은 매우 멋진 기능입니다. 사용하되 남용하지 마세요.

End

원천 기술 공유를 고집하세요. 여러분의 지원이 제가 계속할 수 있도록 격려해 줄 것입니다.
추천 튜토리얼: "🎜php tutorial🎜"🎜🎜🎜

위 내용은 PHP에서 리플렉션 사용에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
php
원천:juejin.im
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿