liip / metadata-parser
元数据解析器
Requires
- php: ^7.4 || ^8.0
- ext-json: *
- doctrine/annotations: ^1.13 || ^2.0.1
- psr/log: ^1|^2|^3
Requires (Dev)
- doctrine/collections: ^1.6
- friendsofphp/php-cs-fixer: v3.17.0
- jms/serializer: ^2.3 || ^3
- phpstan/phpstan: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^8.5.15 || ^9.5
Suggests
- jms/serializer: ^2.3 || ^3
Conflicts
- doctrine/annotations: < 1.11
- jms/serializer: < 2.3
README
该项目基于我们在Liip最初作为闭源项目所做的项目开源,可能缺少一些文档。如果您有任何需要或有任何问题,我们非常愿意看到您提出问题! :)
这是一个从PHP类构建模型元数据的解析器。然后可以使用元数据模型生成代码或配置。例如,为类型生成序列化器或ElasticSearch模式。
元数据是针对这种用法构建的。表示相同信息的PHP级别构造被分组在一起:表示虚拟属性和具有相同序列化名称但适用于不同版本的字段的方法。
这个可扩展的解析器可以处理PHP代码和注释或其他元数据。您可以编写自己的解析器,但这个库提供了对以下内容的支持
- 反射
- PhpDoc
- JMSSerializer注释
贡献
如果您想为该项目做出贡献(太棒了!!),请阅读贡献指南并遵守我们的行为准则
我该去哪里寻求帮助?
如果您需要,请提出问题。
设置
use Doctrine\Common\Annotations\AnnotationReader; use Liip\MetadataParser\Builder; use Liip\MetadataParser\Parser; use Liip\MetadataParser\RecursionChecker; use Liip\MetadataParser\ModelParser\JMSParser; use Liip\MetadataParser\ModelParser\LiipMetadataAnnotationParser; use Liip\MetadataParser\ModelParser\PhpDocParser; use Liip\MetadataParser\ModelParser\ReflectionParser; use Liip\MetadataParser\ModelParser\VisibilityAwarePropertyAccessGuesser; $parser = new Parser( new ReflectionParser(), new PhpDocParser(), new JMSParser(new AnnotationReader()), new VisibilityAwarePropertyAccessGuesser(), new LiipMetadataAnnotationParser(new AnnotationReader()), ); $recursionChecker = new RecursionChecker(new NullLogger()); $builder = new Builder($parser, $recursionChecker);
用法
Builder::build
方法是获取ClassMetadata
对象的主要入口点。构建器接受一个Reducer
数组,以选择在存在多个选项时使用哪个属性。一个reducer可能导致删除不可接受的任何变体中的属性。单个字段的多个选项主要来自JMSSerializer的@SerializedName
和@VirtualProperty
注释
- GroupReducer:根据是否属于指定的任何组来选择属性;
- VersionReducer:根据是否包含在指定的版本中来选择属性;
- TakeBestReducer:确保我们最终得到与序列化名称相同的属性,如果在其他reducer之后我们仍然有多个选项。
use Liip\MetadataParser\Reducer\GroupReducer; use Liip\MetadataParser\Reducer\PreferredReducer; use Liip\MetadataParser\Reducer\TakeBestReducer; use Liip\MetadataParser\Reducer\VersionReducer; $reducers = [ new VersionReducer('2'), new GroupReducer(['api', 'detail']), new PreferredReducer(), new TakeBestReducer(), ]; $metadata = $builder->build(MyClass::class, $reducers);
ClassMetadata
提供了有关类的所有信息。属性具有PropertyType
以告诉它们是什么类型的属性。包含另一个类的属性是类型为PropertyTypeClass
的属性,该类型具有getClassMetadata()
方法来获取嵌套类的元数据。这个结构被验证为不包含任何无限递归。
属性命名策略
默认情况下,属性名称将从一个camelCased转换为小写和snake_cased名称(例如,myProperty
变为my_property
)。如果您想保持属性名称不变,可以通过以下代码将策略更改为identical
\Liip\MetadataParser\ModelParser\RawMetadata\PropertyCollection::useIdenticalNamingStrategy();
使用@Preferred处理边缘情况
该库在Liip\MetadataParser\Annotation\Preferred
中提供了自己的注释,用于在存在多个选项时指定要使用的属性。例如,当序列化没有指定版本的模型时,它们在不同的版本中使用不同的虚拟属性时,这可能很有用。
use JMS\Serializer\Annotation as JMS; use Liip\MetadataParser\Annotation as Liip; class Product { /** * @JMS\Since("2") * @JMS\Type("string") */ public $name; /** * @JMS\Until("1") * @JMS\SerializedName("name") * @JMS\Type("string") * @Liip\Preferred */ public $legacyName; }
预期的递归:与有缺陷的模型一起工作
JMS注释
如果您正在使用JMS注释,您可以向可能递归的属性添加MaxDepth
注释。
以下示例将告诉元数据解析器递归预期最多可达3
层。
use JMS\Serializer\Annotation as JMS; class RecursionModel { /** * @JMS\MaxDepth(3) * @JMS\Type("RecursionModel") */ public $recursion; }
纯PHP
RecursionChecker 接受第二个参数来指定递归终止的位置。如果您的模型树看起来有递归但实际没有,这非常有用。JMSSerializer 总是作用于实际数据,因此只要不是无限递归,就不会注意到递归。
例如,假设您有一个 Product
,它有一个字段 variants
,该字段又是 Product
类型的列表。这些变体产品使用相同的类,因此也有 variants
字段。然而,在实际数据中,变体永远不会包含进一步的变体。为了避免此示例中的递归异常,您需要指定
$expectedRecursions = [ ['variants', 'variants'], ]; $recursionChecker = new RecursionChecker(new NullLogger(), $expectedRecursions);
使用此配置,在最终模型的变体属性类型中找到的 ClassMetadata
将没有 variants
字段,因此处理元数据的代码不需要担心无限递归。
扩展元数据解析器
这个库包含了一些解析器,但您也可以编写自己的来处理项目特定的自定义信息。使用 PropertyVariationMetadata::setCustomInformation
方法添加自定义数据,并使用 PropertyMetadata::getCustomInformation
在元数据消费者中读取它。