SkillAgentSearch skills...

Popo

POPO - plain old PHP object. Generate Data Structures / Data Transfer Objects from a schema.

Install / Use

/learn @oliwierptak/Popo

README

POPO

Build and run tests

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.

  1. Define schema file, see tests/fixtures for examples.

  2. 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
   
View on GitHub
GitHub Stars20
CategoryDevelopment
Updated1mo ago
Forks0

Languages

PHP

Security Score

95/100

Audited on Mar 6, 2026

No findings