실제 개발에 반영을 적용하는 방법에 대해 이야기해보겠습니다.
- 자동으로 문서 생성
- 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)을 지정한 다음 리플렉션을 사용하여 자동 호출을 구현합니다.
$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 컨테이너와 협력하여 종속성을 해결
calc
和 demo
的顺序,不能颠倒,不然的话会报错,原因是由于 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;
위의 calc
와 demo
의 순서는 바뀔 수 없습니다. 그렇지 않으면 오류가 보고됩니다. 이유는 Demo
가 다음에 달려 있기 때문입니다. Calc code>, 먼저 종속성을 정의합니다. <strong><code>Demo
가 인스턴스화되면 Calc
클래스가 사용됩니다. 이는 Demo
가 Calc
에 종속된다는 의미입니다. $data
에 없으면 오류가 발생하므로 먼저 $di->calc = 'Calc'
를 정의해야 합니다.