一、简述
- 这里使用反射类
ReflectionClass
,主要是解析类属性注释中的@var
,支持l类和类数组形式,满足了大部分的需求 - 目前只对
@var
进行解析,在抽象类Attribute
中预留对其他注释的解析,只需继承Attribute
类,实现对应的initialize
和invoke
方法。
二、代码
Attribute
类:抽象类,所有注释解析都需继承这个类,实现对应的方法
<?php
namespace App\Services\Reflection;
abstract class Attribute
{
/**
* 对不同注释进行解析,例如@var,@auth等
* @param $value
* @return mixed
*/
public abstract function initialize($value);
/**
* 执行某个的对象的目标方法或者返回类属性对应的修饰类
* @return mixed
*/
public abstract function invoke();
/**
* 工厂返回不同的解析类
* @param AttributeInfo $info
* @return VariableAttribute|null
*/
public static function createFromAttributeInfo(AttributeInfo $info)
{
//目前先解析@var的注释作
switch (strtolower($info->type)) {
case 'var':
$attribute = new VariableAttribute();
break;
}
if (isset($attribute)) {
$attribute->initialize($info->value);
return $attribute;
}
return null;
}
}
AttributeInfo
类:解析注释类,正则表达式拆分,拆分成如[['type' => 'var', 'value' => 'class']]
,里面是对象AttributeInfo
对象
<?php
namespace App\Services\Reflection;
class AttributeInfo
{
public $type;
public $value;
public function __construct($type, $value=null)
{
$this->type = $type;
$this->value = $value;
}
/**
* 解析注释
* @param $comment
* @return array|null
*/
public static function create($comment)
{
// 正则表达式拆分,拆分成如[['type' => 'var', 'value' => 'class']],里面是对象AttributeInfo对象
$regex = '#@(?<type>[a-zA-Z][a-zA-Z0-9\_]+)([ ]+(?<value>.*))?#i';
if (preg_match_all($regex, $comment, $matches, PREG_SET_ORDER)) {
$attributes = [];
foreach ($matches as $match) {
$type = isset($match['type']) ? $match['type'] : null;
if (isset($type)) {
$value = isset($match['value']) ? $match['value'] : null;
$attributes[] = new AttributeInfo($type, trim($value));
}
}
return $attributes;
}
return null;
}
}
AttributeCollection
类:类属性集合
<?php
namespace App\Services\Reflection;
class AttributeCollection
{
private $attributes;
private function __construct(){}
public static function create($comment)
{
$attributes = AttributeInfo::create($comment);
$collection = new AttributeCollection();
if (isset($attributes)) {
foreach ($attributes as $info) {
$attribute = Attribute::createFromAttributeInfo($info);
if (isset($attribute)) {
$collection->attributes[] = $attribute;
}
}
}
return $collection;
}
public function invoke()
{
if (!empty($this->attributes)) {
/** @var Attribute $attribute */
foreach ($this->attributes as $attribute) {
$attribute->invoke();
}
}
}
public function exists($class)
{
if (!empty($this->attributes)) {
foreach ($this->attributes as $attribute) {
if ($class === get_class($attribute)) {
return true;
}
}
}
return false;
}
public function attributes($class = null)
{
if (empty($this->attributes)) {
return null;
}
$attributes = array();
foreach ($this->attributes as $attribute) {
if ($class === get_class($attribute)) {
$attributes[] = $attribute;
}
}
return $this->attributes;
}
}
VariableAttribute
类:@var
注释解析
<?php
namespace App\Services\Reflection;
class VariableAttribute extends Attribute
{
private $class;
private $isArray = false;
public function initialize($value)
{
// 这里有两种情况:
// ①类,即@var className
// ②类数组, 即@var className[]
$regex = '#^(?<class>[\\\\\/]?[a-zA-Z][a-zA-Z\_0-9\\\\\/]*)(?<digit>\[\])?#i';
if (preg_match($regex, $value, $match)) {
$this->class = $match['class'];
$this->isArray = array_key_exists('digit', $match);
}
}
public function isArray()
{
return $this->isArray;
}
public function isClass()
{
// 这里排除变量类型
$classes = ["string", "double", "float", "long", "int", "integer", "bool", "boolean", "resource", "array"];
return isset($this->class) && false === array_search(strtolower($this->class), $classes);
}
public function invoke()
{
return $this->class;
}
}
Json
类:将json字符串转成类
<?php
namespace App\Services\Reflection;
class Json
{
/**
* json转类
* @param $arg
* @param $class
* @return null
*/
public static function toObject($arg, $class)
{
try {
if (is_string($class)) {
$class = new \ReflectionClass($class);
}
$params = $arg;
if (is_string($arg) && !is_array($arg)) {
$params = self::toArray($arg);
}
if (!empty($params)) {
$object = new $class->name;
$properties = $class->getProperties();
if (!empty($properties)) {
foreach ($properties as $property) {
if (array_key_exists($property->name, $params)) {
$value = $params[$property->name];
$attributes = AttributeCollection::create($property->getDocComment())
->attributes(VariableAttribute::class);
if (isset($attributes[0]) && !empty($attributes[0])) {
/** @var VariableAttribute $attribute */
$attribute = $attributes[0];
$class = $attribute->invoke();
if ($attribute->isClass() && isset($class)) {
if ($attribute->isArray()) {
$value = self::toArray($value, $class);
} else {
$value = self::toObject($value, $class);
}
}
}
$property->setValue($object, $value);
}
}
}
return $object;
}
return null;
} catch (\ReflectionException $exception) {
return null;
}
}
/**
* json转数据
* @param $arg
* @param null $class
* @return array|mixed|string
*/
public static function toArray($arg, $class=null)
{
if (is_string($arg) && !is_array($arg)) {
$list = json_decode($arg, true);
} else {
$list = $arg;
}
if (isset($class)) {
$result = array();
if (is_array($list) && !empty($list)) {
foreach ($list as $item) {
$result[] = self::toObject($item, $class);
}
}
return $result;
}
return $list;
}
}
三、测试
<?php
namespace App\Services\Test;
class A
{
public $a1;
public $a2;
}
<?php
namespace App\Services\Test;
class B
{
public $b1;
public $b2;
/**
* @var \App\Services\Test\A
*/
public $a;
}
<?php
namespace App\Services\Test;
class C
{
public $c1;
public $c2;
/**
* @var \App\Services\Test\A[]
*/
public $a;
/** @var \App\Services\Test\B */
public $b;
}
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\ApiController;
use App\Services\Reflection\Json;
use App\Services\Test\C;
class TestController extends ApiController
{
public function index()
{
$data = [
'c1' => 1,
'c2' => 2,
'a' => [
['a1' => 3, 'a2' => 4],
['a1' => 5, 'a2' => 6],
],
'b' => [
'b1' => 7,
'b2' => 8,
'a' => ['a1' => 9, 'a2' => 10]
],
];
$dataJson = json_encode($data, JSON_UNESCAPED_UNICODE);
$parse = Json::toObject($dataJson, C::class);
dd($parse);die;
}
}
执行结果如下: