PHP实现注解功能
在Java中,注解能控制程序的执行,这是天生自带的特性,给方法一些其他的标识,实现一些看起来很妙的操作。那么我们PHP天生不自带,有没有什么办法能实现呢?答案肯定是有的,你可以读取PHP文件为字符串,再利用正则来匹配,将注解与方法绑定在一起,肯定是能实现的。当然了,这种方法实现太硬核,代码执行起来相对来说也比较低效,本文要说的是使用正则匹配注释实现注解这个功能。
反射机制是PHP重要组成部分,虽然反射内容较多,但是PHP文档关于反射写得很清楚。今天会用到有ReflectionClass、ReflectionMethod两个反射类来获取到类和方法的注释。
创建一个Demo类到Demo.php
/**
* Class Demo
*/
class Demo
{
/**
* method1
*/
public function method1()
{
}
/**
* method2
*/
public function method2()
{
}
}
先来获取一下注释,创建一个测试脚本main.php
$reflection = new ReflectionClass(Demo::class);
print_r($reflection->getDocComment());
打印结果如下
/**
* Class Demo
*/
可以看到,我们获取到了类的注释,接下来我们来获取方法的注释
$reflection = new ReflectionClass(Demo::class);
print_r($reflection->getDocComment());
echo PHP_EOL;
foreach ($reflection->getMethods() as $method) {
print_r($method->getDocComment());
echo PHP_EOL;
}
打印结果如下
/**
* Class Demo
*/
/**
* method1
*/
/**
* method2
*/
我们已经获取到了这个类以及方法的注释,假设我们将注解的格式定义为"@字母 值"的形式,然后我们试着增加几个这样格式的自定义注释
/**
* Class Demo
* @className Demo
*/
class Demo
{
/**
* method1
* @requestMethod POST
*/
public function method1()
{
}
/**
* method2
* @requestMethod GET
*/
public function method2()
{
}
}
我们给类增加了注释className,给每个方法增加了methodName。前面我们已经试过,能通过反射拿到这些注释,接下来用正则解析
$reflection = new ReflectionClass(Demo::class);
$annotation = [];
preg_match_all('/@([a-zA-Z]+)\s+(.*)/', $reflection->getDocComment(), $matches);
for ($i = 0; $i < count($matches[0]); $i++) {
$annotation['_class'][$matches[1][$i]] = $matches[2][$i];
}
$i = $matches = null;
foreach ($reflection->getMethods() as $method) {
preg_match_all('/@([a-zA-Z]+)\s+(.*)/', $method->getDocComment(), $matches);
for ($i = 0; $i < count($matches[0]); $i++) {
$annotation['_methods'][$method->getName()][$matches[1][$i]] = $matches[2][$i];
}
$i = $matches = null;
}
print_r($annotation);
打印测试,得到如下结果
Array
(
[_class] => Array
(
[className] => Demo
)
[_methods] => Array
(
[method1] => Array
(
[requestMethod] => POST
)
[method2] => Array
(
[requestMethod] => GET
)
)
)
接下来将这些代码进行一下封装
class Annotation
{
/**
* 获取解析到的注解
* @param $className
* @param null $method
* @return array
* @throws \ReflectionException
*/
public static function parse($className, $method = null)
{
$annotation = [];
$ref = new \ReflectionClass($className);
$annotation['_class'] = self::_parseAnnotation($ref->getDocComment());
if ($method) {
$annotation['_method'][$method] = self::_parseAnnotation($ref->getMethod($method)->getDocComment());
} else {
foreach ($ref->getMethods() as $method) {
$methodName = $method->getName();
$annotation['_methods'][$methodName] = self::_parseAnnotation($ref->getMethod($methodName)->getDocComment());
$methodName = null;
}
}
return $annotation;
}
/**
* 解析出注解
* @param $doc
* @return array
*/
private static function _parseAnnotation($doc)
{
$result = [];
preg_match_all('/@([a-zA-Z]+)\s+(.*)/', $doc, $matches);
if (!empty($matches)) {
for ($i = 0; $i < count($matches[0]); $i++) {
$result[$matches[1][$i]] = $matches[2][$i];
}
}
$matches = null;
return $result;
}
}
测试一下
// 如果不传入方法名称则解析全部方法的注释
$annotation = Annotation::parse(Demo::class);
print_r($annotation);
最终打印结果同上。至此,已经获取到了我们想得到的信息,可以利用这些数据对程序进行一些控制了。
注:1、反射相关内容在https://www.php.net/manual/zh/book.reflection.php