Skip to content

Commit

Permalink
Added more plugin types to cover all code generation process
Browse files Browse the repository at this point in the history
  • Loading branch information
oliwierptak committed Feb 25, 2022
1 parent 368ac12 commit 5854465
Show file tree
Hide file tree
Showing 40 changed files with 699 additions and 209 deletions.
79 changes: 59 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,12 @@ See [tests/fixtures](tests/fixtures/) for schema examples.
- `popo`
- `datetime`

## Collection support

Use property's `itemType` and `itemName` to create properties with collection item type support. For example
using `Buzz::class` as itemType and `buzz` for the itemName, would generate: `addBuzz(Buzz $item)`.


## Additional functionality with plugins

Apart from the typical setters and getters POPO objects have additional helper methods which ease access to, and offer
Expand All @@ -342,6 +348,7 @@ The following methods are supported by `class` plugins:
- `isNew`
- `fromArray`
- `toArray`
- `modifiedToArray`
- `requireAll`
- `listModifiedProperties`

Expand All @@ -361,40 +368,72 @@ $configurator = (new \Popo\PopoConfigurator)
->setPropertyPluginCollection([]);
```

## Plugins
## Generating Code with Plugins

POPO generation process is split into four parts:

- PHP file header code generation
- Namespace code generation
- Class methods code generation
- Properties and property methods code generation

Each part has corresponding set of plugins.

- #### Popo\Plugin\PhpFilePluginInterface
Generates code related to PHP header, e.g. strict type, file comments.
```php
interface PhpFilePluginInterface
{
public function run(PhpFile $file, Schema $schema): PhpFile;
}
```

- #### Popo\Plugin\NamespacePluginInterface
Generates code related to namespace, e.g. aliases, use statements.

```php
interface NamespacePluginInterface
{
public function run(PhpNamespace $namespace): PhpNamespace;
}
```

- #### Popo\Plugin\ClassPluginInterface
Generates code related to class methods, e.g. `toArray()`, `isNew()`, `extends` or `implements` keywords.

```php
interface ClassPluginInterface
{
public function run(BuilderPluginInterface $builder): void;
}
```

- #### Popo\Plugin\PropertyPluginInterface
Generates code related to properties and their methods, e.g. `hasFoo()`, `getFoo()`, `requireFoo()`.

```php
interface PropertyPluginInterface
{
public function run(BuilderPluginInterface $builder, Property $property): void;
}
```

Adding new behaviour can be achieved with plugins, for example:
#### Example of plugin setup:

```php
$configurator = (new \Popo\PopoConfigurator)
->addPhpFilePluginClass(PhpFilePluginClass::class)
->addNamespacePluginClass(NamespacePluginClass::class)
->addClassPluginClass(PluginClass1:class)
->addClassPluginClass(PluginClass2:class)
->addPropertyPluginClass(PluginProperty1::class)
->addPropertyPluginClass(PluginProperty2::class)
...
```

```php
interface ClassPluginInterface
{
public function run(BuilderPluginInterface $builder): void;
}
```

```php
interface PropertyPluginInterface
{
public function run(BuilderPluginInterface $builder, Property $property): void;
}
```

See [src/Popo/Plugin](src/Popo/Plugin).

## Collection support

Use property's `itemType` and `itemName` to create properties with collection item type support. For example
using `Buzz::class` as itemType and `buzz` for the itemName, would generate: `addBuzz(Buzz $item)`.

## More Examples

See [fixtures](tests/fixtures/popo.yml) and [tests](tests/suite/App/PopoTest.php) for more usage examples.
Expand Down
14 changes: 12 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
},
"require-dev": {
"phpunit/phpunit": "^9",
"symfony/var-dumper": "^5|^6"
"symfony/var-dumper": "^5|^6",
"phpstan/phpstan": "^1.4",
"phpmd/phpmd": "^2.11",
"everon/coding-standard": "^2.0"
},
"autoload": {
"psr-4": {
Expand Down Expand Up @@ -59,11 +62,18 @@
"bin/popo generate -s tests/fixtures/popo-namespace-root.yml",
"bin/popo generate -s tests/fixtures/popo-readme.yml",
"bin/popo generate -s tests/fixtures/popo-datetime.yml",
"bin/popo generate -s tests/fixtures/popo-extend-1.yml",
"bin/popo generate -s tests/fixtures/popo-extend-2.yml",
"bin/popo generate -s tests/fixtures/popo-extend-3.yml",
"bin/popo generate -s tests/fixtures/bundles/ -c tests/fixtures/bundles/project.config.yml"
],
"phpstan": [
"vendor/bin/phpstan analyse -c phpstan.neon"
]
},
"scripts-descriptions": {
"popo": "Generate POPO files"
"popo": "Generate POPO files",
"phpstan": "Run phpstan"
},
"bin": [
"bin/popo",
Expand Down
9 changes: 9 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
parameters:
level: 8
paths:
- src
- tests
bootstrapFiles:
- tests/bootstrap.php
excludePaths:
- tests/*
131 changes: 66 additions & 65 deletions src/Popo/Builder/AbstractBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
use Nette\PhpGenerator\PhpFile;
use Nette\PhpGenerator\PhpNamespace;
use Popo\Plugin\BuilderPluginInterface;
use Popo\Plugin\PluginContainerInterface;
use Popo\Schema\Generator\SchemaGeneratorInterface;
use Popo\Schema\Inspector\SchemaInspectorInterface;
use Popo\Schema\Property\Property;
use Popo\Schema\Schema;

abstract class AbstractBuilder implements BuilderPluginInterface
Expand All @@ -23,107 +23,108 @@ abstract class AbstractBuilder implements BuilderPluginInterface
protected Method $method;
protected SchemaInspectorInterface $schemaInspector;
protected SchemaGeneratorInterface $schemaGenerator;
protected FileWriter $fileWriter;
/**
* @var \Popo\Plugin\ClassPluginInterface[]
*/
protected array $classPluginCollection = [];
/**
* @var \Popo\Plugin\PropertyPluginInterface[]
*/
protected array $propertyMethodPluginCollection = [];

/**
* @throws \Throwable
*/
abstract public function build(Schema $schema): string;
protected PluginContainerInterface $pluginContainer;

public function __construct(
SchemaInspectorInterface $schemaInspector,
SchemaGeneratorInterface $schemaGenerator,
FileWriter $fileWriter,
array $classPluginCollection,
array $propertyMethodPluginCollection
PluginContainerInterface $pluginContainer
) {
$this->schemaInspector = $schemaInspector;
$this->schemaGenerator = $schemaGenerator;
$this->fileWriter = $fileWriter;
$this->classPluginCollection = $classPluginCollection;
$this->propertyMethodPluginCollection = $propertyMethodPluginCollection;
$this->pluginContainer = $pluginContainer;
}

public function getSchema(): Schema
{
return $this->schema;
}
abstract protected function buildPhpFile(): self;

public function getFile(): PhpFile
{
return $this->file;
}
abstract protected function buildNamespace(): self;

public function getNamespace(): PhpNamespace
{
return $this->namespace;
}
abstract protected function buildProperties(): self;

public function getClass(): ClassType
{
return $this->class;
}
abstract protected function buildClass(): self;

public function getSchemaInspector(): SchemaInspectorInterface
public function build(Schema $schema): BuilderPluginInterface
{
return $this->schemaInspector;
$this->schema = $schema;

$this
->buildPhpFile()
->runPhpFilePlugins()
->buildNamespace()
->runNamespacePlugins()
->buildClass()
->runClassPlugins()
->buildProperties()
->runPropertyPlugins();

return $this;
}

public function getSchemaGenerator(): SchemaGeneratorInterface
protected function runPhpFilePlugins(): self
{
return $this->schemaGenerator;
foreach ($this->pluginContainer->createPhpFilePlugin() as $plugin) {
$this->file = $plugin->run($this->file, $this->schema);
}

return $this;
}

protected function buildSchema(Schema $schema): self
protected function runNamespacePlugins(): self
{
$this->schema = $schema;
foreach ($this->pluginContainer->createNamespacePlugin() as $plugin) {
$this->namespace = $plugin->run($this->namespace);
}

$this->file = new PhpFile();
$this->file->setStrictTypes();
return $this;
}

if ($schema->getConfig()->getComment() !== null) {
$this->file->addComment($schema->getConfig()->getComment());
protected function runClassPlugins(): self
{
foreach ($this->pluginContainer->createClassPlugins() as $plugin) {
$plugin->run($this);
}

$this->namespace = $this->file->addNamespace(
new PhpNamespace(
$schema->getConfig()->getNamespace()
)
);
return $this;
}

$this->buildClass();
protected function runPropertyPlugins(): self
{
foreach ($this->schema->getPropertyCollection() as $property) {
foreach ($this->pluginContainer->createPropertyPlugins() as $plugin) {
$plugin->run($this, $property);
}
}

return $this;
}

protected function buildClass(): self
public function getSchema(): Schema
{
$this->namespace->addUse('UnexpectedValueException');
return $this->schema;
}

$this->class = $this->namespace->addClass($this->schema->getName());
public function getFile(): PhpFile
{
return $this->file;
}

return $this;
public function getNamespace(): PhpNamespace
{
return $this->namespace;
}

protected function runClassPlugins(): void
public function getClass(): ClassType
{
foreach ($this->classPluginCollection as $plugin) {
$plugin->run($this);
}
return $this->class;
}

protected function runPropertyMethodPlugins(Property $property): void
public function getSchemaInspector(): SchemaInspectorInterface
{
foreach ($this->propertyMethodPluginCollection as $plugin) {
$plugin->run($this, $property);
}
return $this->schemaInspector;
}

public function getSchemaGenerator(): SchemaGeneratorInterface
{
return $this->schemaGenerator;
}
}
3 changes: 0 additions & 3 deletions src/Popo/Builder/FileWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@

class FileWriter
{
/**
* @throws \Throwable
*/
public function save(PhpFile $file, Schema $schema): string
{
$handle = null;
Expand Down
37 changes: 26 additions & 11 deletions src/Popo/Builder/PopoBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,38 @@

namespace Popo\Builder;

use Popo\Schema\Schema;
use Nette\PhpGenerator\PhpFile;
use Nette\PhpGenerator\PhpNamespace;

class PopoBuilder extends AbstractBuilder
{
/**
* @throws \Throwable
*/
public function build(Schema $schema): string
protected function buildPhpFile(): self
{
$this->buildSchema($schema);
$this->file = new PhpFile();

foreach ($schema->getPropertyCollection() as $property) {
$this->runPropertyMethodPlugins($property);
}
return $this;
}

protected function buildNamespace(): self
{
$this->namespace = new PhpNamespace(
$this->schema->getConfig()->getNamespace()
);

$this->file->addNamespace($this->namespace);

return $this;
}

$this->runClassPlugins();
protected function buildProperties(): self
{
return $this;
}

protected function buildClass(): self
{
$this->class = $this->namespace->addClass($this->schema->getName());

return $this->fileWriter->save($this->file, $schema);
return $this;
}
}
Loading

0 comments on commit 5854465

Please sign in to comment.