popo / generator
Plain Old PHP Object (POPO) / Data Structure / Data Transfer Object (DTO) Generator. Convert YAML schema to PHP class.
Installs: 35 695
Dependents: 9
Suggesters: 0
Security: 0
Stars: 18
Watchers: 1
Forks: 0
Open Issues: 0
Requires
- php: ^8
- ext-ctype: *
- nette/php-generator: ^4
- symfony/config: ^5|^6
- symfony/console: ^5|^6
- symfony/dependency-injection: ^5|^6
- symfony/finder: ^5|^6
- symfony/yaml: ^5|^6
Requires (Dev)
- bvanhoekelen/performance: ^2.5
- everon/coding-standard: ^3.0
- phpmd/phpmd: ^2.13
- phpstan/phpstan: ^1.4
- phpunit/phpunit: ^10
- squizlabs/php_codesniffer: ^3.7
- symfony/var-dumper: ^5|^6
- dev-main
- 6.5.0
- 6.4.5
- 6.4.4
- 6.4.3
- 6.4.2
- 6.4.1
- 6.4.0
- 6.3.8
- 6.3.7
- 6.3.6
- 6.3.5
- 6.3.4
- 6.3.3
- 6.3.2
- 6.3.1
- 6.3.0
- 6.2.2
- 6.2.1
- 6.2.0
- 6.1.2
- 6.1.1
- 6.1.0
- 6.0.4
- 6.0.3
- 6.0.2
- 6.0.1
- 6.0.0
- 5.10.1
- 5.9.6
- 5.9.5
- 5.9.4
- 5.9.3
- 5.9.2
- 5.9.1
- 5.9.0
- 5.8.0
- 5.7.0
- 5.6.0
- 5.5.0
- 5.4.0
- 5.3.1
- 5.3.0
- 5.2.2
- 5.2.1
- 5.2.0
- 5.1.2
- 5.1.1
- 5.1.0
- 5.0.2
- 5.0.1
- 5.0.0
- 4.0.0
- 3.1.0
- 3.0.1
- 3.0.0
- 2.6.3
- 2.6.2
- 2.6.1
- 2.6.0
- 2.5.1
- 2.5.0
- 2.4.1
- 2.4.0
- 2.3.0
- 2.2.2
- 2.2.1
- 2.2.0
- 2.1.0
- 2.0.0
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.0
- dev-feature-key-mapping
- dev-php74
- dev-cleanups
- dev-yaml-support-5.0
This package is auto-updated.
Last update: 2024-12-03 17:56:26 UTC
README
POPO - "Plain Old Php Object" was inspired by "Plain Old Java Object" (POJO) concept.
POPO generator can also locate, load, validate, and combine schemas to create PHP source code files, representing Arrays / Data Structures / Data Transfer Objects / Doctrine ORM Entities / MongoDB ODM Documents.
POPO Schema can be defined and extended on few levels, and it can be defined in multiple files.
Examples
Single schema file
Simple schema in YAML format, describing properties and relations of POPO objects.
#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} ]
Multiple schema files
The same example can be split into multiple files. However, this time, it's the Bar
that modifies the Foo
definition.
#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} ]
The generated code is the same, but the schema dependencies are inverted.
In both cases, Foo
object uses Bar
object as its dependency, and they are both defined under Example
schema name.
Generated code usage examples
Instantiate data structure from an array.
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();
Output:
A title
Bar lorem ipsum
Display hierarchy of objects as an array.
use App\Example\Readme\Foo; $foo = (new Foo); $foo->requireBar()->setTitle('new value'); print_r($foo->toArray());
Output:
[
'title' => null,
'bar' => [
'title' => 'new value',
],
];
Run bin/popo generate -s tests/fixtures/popo-readme.yml
or docker-popo generate -s tests/fixtures/popo-readme.yml
to generate files from this example.
Installation
composer require popo/generator --dev
Note: The installation can be skipped when using docker, see Docker support section.
Usage
You can either use it as composer dependency or as docker command.
-
Define schema file, see tests/fixtures for examples.
-
Generate POPO files, run:
-
with composer
vendor/bin/popo generate -s <schema-path> -o <output-path>
-
with docker
docker-popo generate -s <schema-path> -o <output-path>
-
For example: bin/popo generate -s tests/fixtures/popo.yml
or docker-popo generate -s tests/fixtures/popo.yml
.
POPO Schema
POPO Schema can be defined and extended on few levels- and it can be defined in multiple files.
The schema supports key mapping- inheritance- collections and encapsulation of other POPO objects.
Schema Definition
$: # 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 }]
Schema configuration options
namespace
Defines generated class namespace.
config: namespace: App\Example ...
outputPath
Defines output directory.
config: outputPath: src/ ...
namespaceRoot
Defines the begging of outputPath
that should be removed.
For example to generated files under src/Example
with App\Example
namespace.
config: namespace: App\Example outputPath: src/ namespaceRoot: App\ ...
extend
Which class should the generated class extend from. Must start with \ or contain ::class
.
config: extend: \App\Example\AbstractDto::class ...
implement
Which interface should the generated class implement. Must start with \ or contain ::class
.
config: implement: \App\Example\DtoInterface::class ...
comment
Class comment.
config: comment: | @Document(collection="events") ...
phpComment
Generated PHP file comment.
config: phpComment: | Auto generated. @SuppressWarnings(PHPMD) @phpcs:ignoreFile ...
use
Import statements.
config: use: - Doctrine\ODM\MongoDB\Mapping\Annotations\Document - Doctrine\ODM\MongoDB\Mapping\Annotations\Field - Doctrine\ODM\MongoDB\Mapping\Annotations\Id ...
trait
Traits statements.
config: trait: - App\Example\MyTrait ...
attribute
Class attributes value.
config: attribute: | #[Doctrine\ORM\Mapping\Entity(repositoryClass: LogEventRepository::class)] ...
attributes
: array
Attribute value as collection. Supported values:
name
value
:mixed
config: attributes: - name: Doctrine\ORM\Mapping\Entity value: {repositoryClass: LogEventRepository::class} ...
classPluginCollection
: array
Additional plugins used to generate methods.
config: classPluginCollection: - \App\Plugin\ExampleMethodPopoPlugin::class ...
namespacePluginCollection
: array
Additional plugins used to generate namespace block.
config: namespacePluginCollection: - \App\Plugin\ExampleNamespacePopoPlugin::class ...
propertyPluginCollection
: array
Additional plugins used to generate properties.
config: propertyPluginCollection: - \App\Plugin\ExamplePropertyPopoPlugin::class ...
mappingPolicyPluginCollection
: array
Set of plugins used to map property names, e.g. fooId
=> FOO_ID
.
config: mappingPolicyPluginCollection: - \App\Plugin\SpecialCaseMappingPopoPlugin::class ...
Property configuration options
name
The name of the property. The property related methods will be generated based on this value. For example getFooBar()
.
This is required parameter.
property: - name: title ...
type
Property data type, supported are:
array
bool
float
int
string
mixed
const
popo
datetime
Default property type is string
.
property: - name: precision type: float ...
comment
Docblock value for property and methods.
property: - name: title comment: Lorem ipsum ...
default: mixed
Default value.
property: - name: items default: \App\ExampleInterface::TEST_BUZZ ...
extra: array
Used by datetime
data type. Supported values:
format
timezone
property: - name: created type: datetime extra: timezone: Europe/Paris format: D, d M y H:i:s O ...
itemType
Used by array
data type together with itemName
element. Describes type of single array element.
property: - name: products type: array itemType: Product::class ...
itemName
Used by array
data type. Describes name of single array element. For example: setProducts(array $products)
, addProduct(Product $item)
.
property: - name: products type: array itemName: product ...
attribute
Attribute value.
property: - name: price attribute: | #[Doctrine\ORM\Mapping\Column(type: Types::INTEGER)] ...
attributes
: array
Attribute value as collection. Supported values:
name
value
:mixed
property: - name: id attributes: - name: Doctrine\ORM\Mapping\Column value: ['length: 255'] ...
mappingPolicy: array
Dynamically remaps property names, for example, fooId
=> FOO_ID
. Supported values:
none
lower
upper
camel-to-snake
snake-to-camel
property: - name: fooId mappingPolicy: - camel-to-snake - upper ...
mappingPolicyValue
Statically remaps property names, for example, fooId
=> FOO_ID
.
property: - name: fooId mappingPolicyValue: FOO_ID ...
Schema Inheritance
The popo-config
values override schema-file-config
values- and schema-file-config
values overwrite schema-config
values.
On top of that there is a global-config
that is defined when using --schemaConfigFilename
parameter.
schema-config
The configuration was defined as a Schema
property.
It will be used by all POPO objects in all files under given schema.
schema-file-config
The configuration was defined as a SchemaFile
property.
It will be used by all POPO objects in current file.
popo-config
The configuration was defined as a POPO
property.
It will be used by one specific POPO objects in current file.
See tests/fixtures for schema examples.
Property name remapping
POPO can remap property keys names, for example change foo_id
into fooId
.
See Property Name Remapping doc.
Pluggable architecture
New functionality can be provided on the command line, or via configuration.
See Plugins doc.
Doctrine Support
See Doctrine Support doc.
Command line options
See Command Line Options doc.
More Examples
See fixtures and tests for more usage examples.
Composer script
Add popo scrip to composer and run composer popo
in a project.
"scripts": {
"popo": [
"bin/popo generate -s <schema-path>"
]
},
"scripts-descriptions": {
"popo": "Generate POPO files"
}
Docker support
With docker you can generate files without installing POPO
as dependency in the project.
docker container run -it --rm oliwierptak/popo /app/bin/popo
You can either run the command directly, or create an alias, e.g.:
alias docker-popo='docker container run -it --rm oliwierptak/popo /app/bin/popo ${@}'
For example:
docker-popo generate -s tests/fixtures/popo.yml
docker-popo report -s tests/fixtures/popo.yml
See also: bin/docker-popo.
PHP version compatibility
- 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 example
Schema example that produces generated PopoConfigurator class.
$: 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} ]}}