popo/generator

Plain Old PHP Object (POPO) / 数据结构 / 数据传输对象 (DTO) 生成器。将 YAML 架构转换为 PHP 类。


README

Build and run tests

POPO - "Plain Old Php Object" 是受 "Plain Old Java Object" (POJO) 概念启发的。

POPO 生成器还可以定位、加载、验证和合并架构以创建 PHP 源代码文件,代表数组/数据结构/数据传输对象/Doctrine ORM 实体/MongoDB ODM 文档。

POPO 架构可以在多个级别上定义和扩展,并且可以定义在多个文件中。

示例

单个架构文件

简单的 YAML 格式架构,描述 POPO 对象的属性和关系。

#example.popo.yml
$:
  config:
    namespace: App\Example\Readme
    outputPath: tests/

Example:
  Foo:
    property: [
      {name: title}
      {name: bar, type: popo, default: Bar::class}
    ]

  Bar:
    property: [
      {name: title}
    ]

多个架构文件

相同的示例可以拆分为多个文件。但是这次,是 Bar 修改了 Foo 的定义。

#foo.popo.yml
Example:
  Foo:
    property: [
      {name: title}
    ]
#bar.popo.yml
Example:
  Foo:
    property: [
      {name: bar, type: popo, default: Bar::class}
    ]

  Bar:
    property: [
      {name: title}
    ]

生成的代码相同,但架构依赖项已反转。

在两种情况下,Foo 对象都使用 Bar 对象作为其依赖项,并且它们都定义在 Example 架构名称下。

生成的代码使用示例

从数组实例化数据结构。

use App\Example\Readme\Foo;

$data = [
    'title' => 'A title',
    'bar' => [
        'title' => 'Bar lorem ipsum',
    ],
];

$foo = (new Foo)->fromArray($data);

echo $foo->getTitle();
echo $foo->requireBar()->getTitle();

输出

A title
Bar lorem ipsum

显示对象的层次结构作为数组。

use App\Example\Readme\Foo;

$foo = (new Foo);
$foo->requireBar()->setTitle('new value');

print_r($foo->toArray());

输出

[
    'title' => null,
    'bar' => [
        'title' => 'new value',
    ],
];

运行 bin/popo generate -s tests/fixtures/popo-readme.ymldocker-popo generate -s tests/fixtures/popo-readme.yml 以生成此示例的文件。

安装

composer require popo/generator --dev

注意:当使用 docker 时,可以跳过安装,请参阅 Docker 支持部分

用法

您可以使用它作为 composer 依赖项或作为 docker 命令。

  1. 定义架构文件,请参阅 tests/fixtures 以获取示例。

  2. 生成 POPO 文件,运行

    • 使用 composer

      vendor/bin/popo generate -s <schema-path> -o <output-path>
    • 使用 docker

      docker-popo generate -s <schema-path> -o <output-path>     

例如: bin/popo generate -s tests/fixtures/popo.ymldocker-popo generate -s tests/fixtures/popo.yml

POPO 架构

POPO 架构可以在多个级别上定义和扩展,并且可以定义在多个文件中。

该架构支持键映射、继承、集合和其他 POPO 对象的封装。

架构定义

$: # file-config, shared configuration for all POPO objects in current schema file
  config:
    namespace: string
    outputPath: string
    namespaceRoot: string|null # if set remaps namespace and outputPath
    extend: string|null # which class POPO objects should extend from
    implement: string|null # which interface POPO objects should implement
    comment: string|null # Class docblock comment
    phpComment: string|null # Generated PHP File docblock comment
    use: array<string>|[] # Import block in generated PHP class
    trait: array<string>|[] # Traits to be used with generated class
    attribute: string|null # Class attributes as string
    attributes: array<key, value>|[] # Class attributes as key value pairs
    classPluginCollection: array<string>|[]
    phpFilePluginCollection: array<string>|[]
    namespacePluginCollection: array<string>|[]
    propertyPluginCollection: array<string>|[]
    mappingPolicyPluginCollection: array<string>|[]
  default: array # default values
  property: array # shared properties

SchemaName: # schema-config
  $: # shared configuration for all POPO objects in SchemaName, in all schema files
    config:
      namespace: string
      outputPath: string
      namespaceRoot: string|null
      extend: string|null
      implement: string|null
      comment: string|null
      phpComment: string|null
      use: array<string>|[]
      trait: array<string>|[]
      attribute: string|null,
      attributes: array<key, value>|[]
      classPluginCollection: array<string>|[]
      phpFilePluginCollection: array<string>|[]
      namespacePluginCollection: array<string>|[]
      propertyPluginCollection: array<string>|[]
      mappingPolicyPluginCollection: array<string>|[]
    default: array
    property: [{
      name: string,
      type:
        type: string
        default: string
        supportedTypes: ['array','bool','float','int','string','mixed','const','popo', 'datetime'],
      comment: string|null,
      default: mixed,
      itemType: string|null,
      itemName: string|null,
      extra: {timezone: ..., format: ...},
      attribute: string|null,
      attributes: array<key, value>|[]
      mappingPolicy: ['none', 'lower', 'upper', 'camel-to-snake', 'snake-to-camel'],
      mappingPolicyValue: string|null
    }]

  PopoName: # popo-config
    config:
      namespace: string
      outputPath: string
      namespaceRoot: string|null
      extend: string|null
      implement: string|null
      comment: string|null
      phpComment: string|null
      use: array<string>|[]
      trait: array<string>|[]
      attribute: string|null,
      attributes: array<key, value>|[]
      classPluginCollection: array<string>|[]
      phpFilePluginCollection: array<string>|[]
      namespacePluginCollection: array<string>|[]
      propertyPluginCollection: array<string>|[]
      mappingPolicyPluginCollection: array<string>|[]
    default: array
    property: [{
      name: string,
      type:
        type: string
        default: string
        supportedTypes: ['array','bool','float','int','string','mixed','const','popo', 'datetime'],
      comment: string|null,
      default: mixed,
      itemType: string|null,
      itemName: string|null,
      extra: {timezone: ..., format: ...},
      attribute: string|null,
      attributes: array<key, value>|[]
      mappingPolicy: ['none', 'lower', 'upper', 'camel-to-snake', 'snake-to-camel'],
      mappingPolicyValue: string|null
    }]

架构配置选项

namespace

定义生成的类命名空间。

config:
  namespace: App\Example
  ...

outputPath

定义输出目录。

config:
  outputPath: src/
  ...

namespaceRoot

定义应从 outputPath 中删除的起始位置。例如,要生成位于 src/Example 下的文件,使用 App\Example 命名空间。

config:
  namespace: App\Example
  outputPath: src/
  namespaceRoot: App\
  ...

extend

生成的类应从哪个类扩展。必须以 \ 开头或包含 ::class

config:
  extend: \App\Example\AbstractDto::class
  ...

implement

生成的类应实现哪个接口。必须以 \ 开头或包含 ::class

config:
  implement: \App\Example\DtoInterface::class
   ...

comment

类注释。

config:
  comment: |
    @Document(collection="events")
...

phpComment

生成的 PHP 文件注释。

config:
  phpComment: |
    Auto generated.
    @SuppressWarnings(PHPMD)
    @phpcs:ignoreFile
...

use

导入语句。

  config:
    use:
      - Doctrine\ODM\MongoDB\Mapping\Annotations\Document
      - Doctrine\ODM\MongoDB\Mapping\Annotations\Field
      - Doctrine\ODM\MongoDB\Mapping\Annotations\Id
    ...

trait

特质语句。

  config:
    trait:
      - App\Example\MyTrait
    ...

attribute

类属性值。

  config:
    attribute: |
      #[Doctrine\ORM\Mapping\Entity(repositoryClass: LogEventRepository::class)]
    ...

attributes: array

属性值作为集合。支持值

  • name
  • value: mixed
  config:
    attributes:
      - name: Doctrine\ORM\Mapping\Entity
        value: {repositoryClass: LogEventRepository::class}
    ...

classPluginCollection: array

用于生成方法的附加插件。

  config:
    classPluginCollection:
      - \App\Plugin\ExampleMethodPopoPlugin::class
    ...

namespacePluginCollection: array

用于生成命名空间块的附加插件。

  config:
    namespacePluginCollection:
      - \App\Plugin\ExampleNamespacePopoPlugin::class
    ...

propertyPluginCollection: array

用于生成属性的附加插件。

  config:
    propertyPluginCollection:
      - \App\Plugin\ExamplePropertyPopoPlugin::class
    ...

mappingPolicyPluginCollection: 数组

用于映射属性名称的插件集合,例如 fooId => FOO_ID

  config:
    mappingPolicyPluginCollection:
      - \App\Plugin\SpecialCaseMappingPopoPlugin::class
    ...

属性配置选项

name

属性的名称。相关方法将基于此值生成。例如 getFooBar()。这是一个必需参数。

property: 
  - name: title
    ...

类型

属性数据类型,支持的类型有

  • 数组
  • 布尔型
  • 浮点型
  • 整型
  • 字符串
  • 混合型
  • 常量
  • popo
  • 日期时间

默认属性类型是 字符串

property: 
  - name: precision
    type: float
    ...

comment

属性和方法的 Docblock 值。

property: 
  - name: title
    comment: Lorem ipsum
    ...

默认: 混合型

默认值。

property: 
  - name: items
    default: \App\ExampleInterface::TEST_BUZZ
    ...

额外: 数组

用于 日期时间 数据类型。支持的值

  • 格式
  • 时区
property: 
    - name: created
      type: datetime
      extra: 
          timezone: Europe/Paris
          format: D, d M y H:i:s O
    ...

itemType

用于 数组 数据类型,与 itemName 元素一起。描述单个数组元素的类型。

property:
    - name: products
      type: array
      itemType: Product::class
    ...

itemName

用于 数组 数据类型。描述单个数组元素的名称。例如:setProducts(array $products)addProduct(Product $item)

property:
    - name: products
      type: array
      itemName: product
    ...

attribute

属性值。

property:
    - name: price
      attribute: |
      #[Doctrine\ORM\Mapping\Column(type: Types::INTEGER)]
    ...

attributes: array

属性值作为集合。支持值

  • name
  • value: mixed
property:
    - name: id
      attributes:
        - name: Doctrine\ORM\Mapping\Column
          value: ['length: 255']
    ...

mappingPolicy: 数组

动态重映射属性名称,例如,fooId => FOO_ID。支持的值

  • 小写
  • 大写
  • 驼峰转蛇形
  • 蛇形转驼峰
property:
  - name: fooId
    mappingPolicy:
      - camel-to-snake
      - upper
    ...

mappingPolicyValue

静态重映射属性名称,例如,fooId => FOO_ID

property:
  - name: fooId
    mappingPolicyValue: FOO_ID
  ...

Schema 继承

popo-config 的值覆盖 schema-file-config 的值,而 schema-file-config 的值覆盖 schema-config 的值。

此外,还有一个在设置 --schemaConfigFilename 参数时定义的 global-config

POPO Schema

schema-config

配置被定义为 Schema 属性。它将在给定 schema 下的所有文件中的所有 POPO 对象中使用。

schema-file-config

配置被定义为 SchemaFile 属性。它将在当前文件中的所有 POPO 对象中使用。

popo-config

配置被定义为 POPO 属性。它将在当前文件中的一个特定 POPO 对象中使用。

有关 schema 示例,请参阅 tests/fixtures

属性名称重映射

POPO 可以重映射属性键名,例如将 foo_id 改为 fooId

请参阅 属性名称重映射 文档。

插件架构

可以通过命令行或配置提供新功能。

请参阅 插件 文档。

Doctrine 支持

请参阅 Doctrine 支持 文档。

命令行选项

请参阅 命令行选项 文档。

更多示例

请参阅 fixturestests 以获取更多使用示例。

Composer 脚本

将 popo 脚本添加到 composer 中,并在项目中运行 composer popo

    "scripts": {
        "popo": [
            "bin/popo generate -s <schema-path>"
        ]
    },
    "scripts-descriptions": {
        "popo": "Generate POPO files"
    }

Docker 支持

使用 docker,您可以在项目中不安装 POPO 依赖项的情况下生成文件。

docker container run -it --rm oliwierptak/popo /app/bin/popo

您可以直接运行命令,也可以创建别名,例如。

alias docker-popo='docker container run -it --rm oliwierptak/popo /app/bin/popo ${@}'

例如

docker-popo generate -s tests/fixtures/popo.yml
docker-popo report -s tests/fixtures/popo.yml

另请参阅: bin/docker-popo

PHP 版本兼容性

  • POPO v1.x - PHP 7.2+
  • POPO v2.x - PHP 7.2+
  • POPO v3.x - PHP 7.4+
  • POPO v4.x - PHP 7.4+
  • POPO v5.x - PHP 7.4+
  • POPO v6.x - PHP 8+

POPO schema 示例

生成 PopoConfigurator 类的示例模式。

$:
  config:
    namespace: Popo
    outputPath: src/
    phpComment: |
      @SuppressWarnings(PHPMD)
      @phpcs:ignoreFile

Popo:
  PopoConfigurator:
    default:
      phpFilePluginCollection:
        - \Popo\Plugin\PhpFilePlugin\StrictTypesPhpFilePlugin::class
        - \Popo\Plugin\PhpFilePlugin\CommentPhpFilePlugin::class
      namespacePluginCollection:
        - \Popo\Plugin\NamespacePlugin\UseStatementPlugin::class
      classPluginCollection:
        - \Popo\Plugin\ClassPlugin\ClassAttributePlugin::class
        - \Popo\Plugin\ClassPlugin\ClassCommentPlugin::class
        - \Popo\Plugin\ClassPlugin\ConstPropertyClassPlugin::class
        - \Popo\Plugin\ClassPlugin\DateTimeMethodClassPlugin::class
        - \Popo\Plugin\ClassPlugin\ExtendClassPlugin::class
        - \Popo\Plugin\ClassPlugin\ImplementClassPlugin::class
        - \Popo\Plugin\ClassPlugin\IsNewClassPlugin::class
        - \Popo\Plugin\ClassPlugin\ListModifiedPropertiesClassPlugin::class
        - \Popo\Plugin\ClassPlugin\MetadataClassPlugin::class
        - \Popo\Plugin\ClassPlugin\ModifiedToArrayClassPlugin::class
        - \Popo\Plugin\ClassPlugin\PopoMethodClassPlugin::class
        - \Popo\Plugin\ClassPlugin\RequireAllClassPlugin::class
        - \Popo\Plugin\ClassPlugin\UpdateMapClassPlugin::class
        - \Popo\Plugin\ClassPlugin\FromArrayClassPlugin::class
        - \Popo\Plugin\ClassPlugin\FromMappedArrayClassPlugin::class
        - \Popo\Plugin\ClassPlugin\ToArrayClassPlugin::class
        - \Popo\Plugin\ClassPlugin\ToMappedArrayClassPlugin::class
        - \Popo\Plugin\ClassPlugin\MappingPolicyMethod\ToArrayLowercasePlugin::class
        - \Popo\Plugin\ClassPlugin\MappingPolicyMethod\ToArrayUppercasePlugin::class
        - \Popo\Plugin\ClassPlugin\MappingPolicyMethod\ToArraySnakeToCamelPlugin::class
        - \Popo\Plugin\ClassPlugin\MappingPolicyMethod\ToArrayCamelToSnakePlugin::class
      propertyPluginCollection:
        - \Popo\Plugin\PropertyPlugin\AddItemPropertyMethodPlugin::class
        - \Popo\Plugin\PropertyPlugin\DefinePropertyPlugin::class
        - \Popo\Plugin\PropertyPlugin\GetPropertyMethodPlugin::class
        - \Popo\Plugin\PropertyPlugin\HasPropertyMethodPlugin::class
        - \Popo\Plugin\PropertyPlugin\RequirePropertyMethodPlugin::class
        - \Popo\Plugin\PropertyPlugin\SetPropertyMethodPlugin::class
      mappingPolicyPluginCollection:
        none: \Popo\Plugin\MappingPolicy\NoneMappingPolicyPlugin::class
        lower: \Popo\Plugin\MappingPolicy\LowerMappingPolicyPlugin::class
        upper: \Popo\Plugin\MappingPolicy\UpperMappingPolicyPlugin::class
        snake-to-camel: \Popo\Plugin\MappingPolicy\SnakeToCamelMappingPolicyPlugin::class
        camel-to-snake: \Popo\Plugin\MappingPolicy\CamelToSnakeMappingPolicyPlugin::class
    property: [
      {name: schemaPath}
      {name: namespace}
      {name: namespaceRoot}
      {name: outputPath}
      {name: phpFilePluginCollection, type: array, itemType: string, itemName: phpFilePluginClass}
      {name: namespacePluginCollection, type: array, itemType: string, itemName: namespacePluginClass}
      {name: classPluginCollection, type: array, itemType: string, itemName: classPluginClass}
      {name: propertyPluginCollection, type: array, itemType: string, itemName: propertyPluginClass}
      {name: mappingPolicyPluginCollection, type: array, itemType: string, itemName: mappingPolicyPluginClass}
      {name: schemaConfigFilename}
      {name: schemaPathFilter}
      {name: schemaFilenameMask, default: '*.popo.yml'}
      {name: shouldIgnoreNonExistingSchemaFolder, type: bool}
    ]}}