POPO
, (*1)
POPO - "Plain Old Php Object" was inspired by "Plain Old Java Object" (POJO) concept., (*2)
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., (*3)
POPO Schema can be defined and extended on few levels, and it can be defined in multiple files., (*4)
Examples
Single schema file
Simple schema in YAML format, describing properties and relations of POPO objects., (*5)
#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., (*6)
#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., (*7)
In both cases, Foo
object uses Bar
object as its dependency, and they are both defined under Example
schema name., (*8)
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:, (*9)
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:, (*10)
[
'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., (*11)
Installation
composer require popo/generator --dev
Note: The installation can be skipped when using docker, see Docker support section., (*12)
Usage
You can either use it as composer dependency or as docker command., (*13)
-
Define schema file, see tests/fixtures for examples., (*14)
-
Generate POPO files, run:, (*15)
For example: bin/popo generate -s tests/fixtures/popo.yml
or docker-popo generate -s tests/fixtures/popo.yml
., (*17)
POPO Schema
POPO Schema can be defined and extended on few levels- and it can be defined in multiple files., (*18)
The schema supports key mapping- inheritance- collections and encapsulation of other POPO objects., (*19)
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., (*20)
config:
namespace: App\Example
...
outputPath
Defines output directory., (*21)
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., (*22)
config:
namespace: App\Example
outputPath: src/
namespaceRoot: App\
...
extend
Which class should the generated class extend from. Must start with \ or contain ::class
., (*23)
config:
extend: \App\Example\AbstractDto::class
...
implement
Which interface should the generated class implement. Must start with \ or contain ::class
., (*24)
config:
implement: \App\Example\DtoInterface::class
...
Class comment., (*25)
config:
comment: |
@Document(collection="events")
...
Generated PHP file comment., (*26)
config:
phpComment: |
Auto generated.
@SuppressWarnings(PHPMD)
@phpcs:ignoreFile
...
use
Import statements., (*27)
config:
use:
- Doctrine\ODM\MongoDB\Mapping\Annotations\Document
- Doctrine\ODM\MongoDB\Mapping\Annotations\Field
- Doctrine\ODM\MongoDB\Mapping\Annotations\Id
...
trait
Traits statements., (*28)
config:
trait:
- App\Example\MyTrait
...
attribute
Class attributes value., (*29)
config:
attribute: |
#[Doctrine\ORM\Mapping\Entity(repositoryClass: LogEventRepository::class)]
...
attributes
: array
Attribute value as collection. Supported values:, (*30)
config:
attributes:
- name: Doctrine\ORM\Mapping\Entity
value: {repositoryClass: LogEventRepository::class}
...
classPluginCollection
: array
Additional plugins used to generate methods., (*31)
config:
classPluginCollection:
- \App\Plugin\ExampleMethodPopoPlugin::class
...
namespacePluginCollection
: array
Additional plugins used to generate namespace block., (*32)
config:
namespacePluginCollection:
- \App\Plugin\ExampleNamespacePopoPlugin::class
...
propertyPluginCollection
: array
Additional plugins used to generate properties., (*33)
config:
propertyPluginCollection:
- \App\Plugin\ExamplePropertyPopoPlugin::class
...
mappingPolicyPluginCollection
: array
Set of plugins used to map property names, e.g. fooId
=> FOO_ID
., (*34)
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., (*35)
property:
- name: title
...
type
Property data type, supported are:, (*36)
array
bool
float
int
string
mixed
const
popo
datetime
Default property type is string
., (*37)
property:
- name: precision
type: float
...
Docblock value for property and methods., (*38)
property:
- name: title
comment: Lorem ipsum
...
default: mixed
Default value., (*39)
property:
- name: items
default: \App\ExampleInterface::TEST_BUZZ
...
Used by datetime
data type. Supported values:, (*40)
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., (*41)
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)
., (*42)
property:
- name: products
type: array
itemName: product
...
attribute
Attribute value., (*43)
property:
- name: price
attribute: |
#[Doctrine\ORM\Mapping\Column(type: Types::INTEGER)]
...
attributes
: array
Attribute value as collection. Supported values:, (*44)
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:, (*45)
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
., (*46)
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., (*47)
On top of that there is a global-config
that is defined when using --schemaConfigFilename
parameter., (*48)
, (*49)
schema-config
The configuration was defined as a Schema
property.
It will be used by all POPO objects in all files under given schema., (*50)
schema-file-config
The configuration was defined as a SchemaFile
property.
It will be used by all POPO objects in current file., (*51)
popo-config
The configuration was defined as a POPO
property.
It will be used by one specific POPO objects in current file., (*52)
See tests/fixtures for schema examples., (*53)
Property name remapping
POPO can remap property keys names, for example change foo_id
into fooId
., (*54)
See Property Name Remapping doc., (*55)
Pluggable architecture
New functionality can be provided on the command line, or via configuration., (*56)
See Plugins doc., (*57)
Doctrine Support
See Doctrine Support doc., (*58)
Command line options
See Command Line Options doc., (*59)
More Examples
See fixtures and tests for more usage examples., (*60)
Composer script
Add popo scrip to composer and run composer popo
in a project., (*61)
"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., (*62)
docker container run -it --rm oliwierptak/popo /app/bin/popo
You can either run the command directly, or create an alias, e.g.:, (*63)
alias docker-popo='docker container run -it --rm oliwierptak/popo /app/bin/popo ${@}'
For example:, (*64)
docker-popo generate -s tests/fixtures/popo.yml
docker-popo report -s tests/fixtures/popo.yml
See also: bin/docker-popo., (*65)
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., (*66)
$:
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}
]}}