Skip to content

Commit 2121ea9

Browse files
authored
Merge pull request #73 from wol-soft/enumPostProcessor
Enum post processor
2 parents 85aa9b5 + 61c0283 commit 2121ea9

File tree

82 files changed

+1562
-205
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1562
-205
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
runs-on: ubuntu-latest
88
strategy:
99
matrix:
10-
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
10+
php: ['8.0', '8.1', '8.2']
1111

1212
name: PHP ${{ matrix.php }} tests
1313
steps:
@@ -25,7 +25,7 @@ jobs:
2525
run: composer install
2626

2727
- name: Prepare codeclimate test reporter
28-
if: ${{ matrix.php == '7.4' }}
28+
if: ${{ matrix.php == '8.2' }}
2929
run: |
3030
curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
3131
chmod +x ./cc-test-reporter
@@ -35,15 +35,15 @@ jobs:
3535
run: XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover=build/logs/clover.xml --testdox
3636

3737
- name: Upload the reports to coveralls.io
38-
if: ${{ matrix.php == '7.4' }}
38+
if: ${{ matrix.php == '8.2' }}
3939
run: |
4040
composer global require php-coveralls/php-coveralls
4141
php-coveralls -v
4242
env:
4343
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4444

4545
- name: Upload the reports to codeclimate
46-
if: ${{ matrix.php == '7.4' }}
46+
if: ${{ matrix.php == '8.2' }}
4747
run: sudo ./cc-test-reporter after-build -r $CC_TEST_REPORTER_ID
4848
env:
4949
CC_TEST_REPORTER_ID: 5e32818628fac9eb11d34e2b35289f88169610cc4a98c6f170c74923342284f1

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[![Latest Version](https://img.shields.io/packagist/v/wol-soft/php-json-schema-model-generator.svg)](https://packagist.org/packages/wol-soft/php-json-schema-model-generator)
2-
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.2-8892BF.svg)](https://php.net/)
2+
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%208.0-8892BF.svg)](https://php.net/)
33
[![Maintainability](https://api.codeclimate.com/v1/badges/7eb29e7366dc3d6a5f44/maintainability)](https://codeclimate.com/github/wol-soft/php-json-schema-model-generator/maintainability)
44
[![Build Status](https://github.com/wol-soft/php-json-schema-model-generator/actions/workflows/main.yml/badge.svg)](https://github.com/wol-soft/php-json-schema-model-generator/actions/workflows/main.yml)
55
[![Coverage Status](https://coveralls.io/repos/github/wol-soft/php-json-schema-model-generator/badge.svg?branch=master)](https://coveralls.io/github/wol-soft/php-json-schema-model-generator?branch=master)
@@ -27,7 +27,7 @@ Simple example from a PHP application: you define and document an API with swagg
2727

2828
## Requirements ##
2929

30-
- Requires at least PHP 7.2
30+
- Requires at least PHP 8.0
3131
- Requires the PHP extensions ext-json and ext-mbstring
3232

3333
## Installation ##

composer.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111
}
1212
],
1313
"require": {
14-
"wol-soft/php-json-schema-model-generator-production": "^0.18.1",
15-
"wol-soft/php-micro-template": "^1.3.2",
14+
"symfony/polyfill-php81": "^1.28",
15+
"wol-soft/php-json-schema-model-generator-production": "^0.19.0",
16+
"wol-soft/php-micro-template": "^1.9.0",
1617

17-
"php": ">=7.2",
18+
"php": ">=8.0",
1819
"ext-json": "*",
1920
"ext-mbstring": "*"
2021
},
2122
"require-dev": {
22-
"phpunit/phpunit": "^8.5 || ^9.4"
23+
"phpunit/phpunit": "^9.4"
2324
},
2425
"autoload": {
2526
"psr-4": {

docs/source/complexTypes/enum.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ Enum
33

44
Enums can be used to define a set of constant values a property must accept.
55

6+
.. hint::
7+
8+
If you define constraints via `enum` you may want to use the `EnumPostProcessor <../generator/postProcessor.html#enumpostprocessor>`__ to generate PHP enums.
9+
610
.. code-block:: json
711
812
{

docs/source/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
# -- Project information -----------------------------------------------------
2121

2222
project = u'php-json-schema-model-generator'
23-
copyright = u'2020, Enno Woortmann'
23+
copyright = u'2023, Enno Woortmann'
2424
author = u'Enno Woortmann'
2525

2626
# The short X.Y version
27-
version = u'0.19'
27+
version = u'0.24'
2828
# The full version, including alpha/beta/rc tags
29-
release = u'0.19.0'
29+
release = u'0.24.0'
3030

3131

3232
# -- General configuration ---------------------------------------------------

docs/source/generator/postProcessor.rst

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,102 @@ The added method **getPatternProperties** can be used to fetch a list of all pro
204204
205205
.. note::
206206

207-
If you want to add or remove pattern properties to your object after the object instantiation you can use the `AdditionalPropertiesAccessorPostProcessor <generator/postProcessor.html#additionalpropertiesaccessorpostprocessor>`__ or the `PopulatePostProcessor <generator/postProcessor.html#populatepostprocessor>`__
207+
If you want to modify your object by adding or removing pattern properties after the object instantiation you can use the `AdditionalPropertiesAccessorPostProcessor <postProcessor.html#additionalpropertiesaccessorpostprocessor>`__ or the `PopulatePostProcessor <postProcessor.html#populatepostprocessor>`__
208+
209+
EnumPostProcessor
210+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
211+
212+
.. warning::
213+
214+
Requires at least PHP 8.1
215+
216+
.. code-block:: php
217+
218+
$generator = new ModelGenerator();
219+
$generator->addPostProcessor(new EnumPostProcessor(__DIR__ . '/generated/enum/', '\\MyApp\\Enum'));
220+
221+
The **EnumPostProcessor** generates a `PHP enum <https://www.php.net/manual/en/language.enumerations.basics.php>`_ for each `enum <../complexTypes/enum.html>`__ found in the processed schemas.
222+
Enums which contain only integer values or only string values will be rendered into a `backed enum <https://www.php.net/manual/en/language.enumerations.backed.php>`_.
223+
Other enums will provide the following interface similar to the capabilities of a backed enum:
224+
225+
.. code-block:: php
226+
227+
public static function from(mixed $value): self;
228+
public static function tryFrom(mixed $value): ?self;
229+
230+
public function value(): mixed;
231+
232+
Let's have a look at the most simple case of a string-only enum:
233+
234+
.. code-block:: json
235+
236+
{
237+
"$id": "offer",
238+
"type": "object",
239+
"properties": {
240+
"state": {
241+
"enum": ["open", "sold", "cancelled"]
242+
}
243+
}
244+
}
245+
246+
The provided schema will generate the following enum:
247+
248+
.. code-block:: php
249+
250+
enum OfferState: string {
251+
case Open = 'open';
252+
case Sold = 'sold';
253+
case Cancelled = 'cancelled';
254+
}
255+
256+
The type hints and annotations of the generated class will be changed to match the generated enum:
257+
258+
.. code-block:: php
259+
260+
/**
261+
* @param OfferState|string|null $state
262+
*/
263+
public function setState($state): self;
264+
public function getState(): ?OfferState;
265+
266+
Mapping
267+
~~~~~~~
268+
269+
Each enum which is not a string-only enum must provide a mapping in the **enum-map** property, for example an integer-only enum:
270+
271+
.. code-block:: json
272+
273+
{
274+
"$id": "offer",
275+
"type": "object",
276+
"properties": {
277+
"state": {
278+
"enum": [0, 1, 2],
279+
"enum-map": {
280+
"open": 0,
281+
"sold": 1,
282+
"cancelled": 2
283+
}
284+
}
285+
}
286+
}
287+
288+
The provided schema will generate the following enum:
289+
290+
.. code-block:: php
291+
292+
enum OfferState: int {
293+
case Open = 0;
294+
case Sold = 1;
295+
case Cancelled = 2;
296+
}
297+
298+
If an enum which requires a mapping is found a **SchemaException** will be thrown.
299+
300+
.. note::
301+
302+
By enabling the *$skipNonMappedEnums* option of the **EnumPostProcessor** you can skip enums which require a mapping but don't provide a mapping. Those enums will provide the default `enum <../complexTypes/enum.html>`__ behaviour.
208303

209304
Custom Post Processors
210305
----------------------

docs/source/nonStandardExtensions/filter.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Filters may change the type of the property. For example the builtin filter **da
9191

9292
As the required check is executed before the filter a filter may transform a required value into a null value. Be aware when writing custom filters which transform values to not break your validation rules by adding filters to a property.
9393

94-
Only one transforming filter per property is allowed. may be positioned anywhere in the filter chain of a single property. If multiple filters are applied and a transforming filter is among them you have to make sure the property types are compatible. If you use a custom filter after the dateTime filter for example the custom filter has to accept a DateTime value. Filters used before a transforming filter must accept the base type of the property the filter is applied to defined in the schema. If the transformation of a property fails (the transforming filter throws an exception), subsequent filters won't be executed as their execution would add another error due to incompatible types which is irrelevant for the currently provided value.
94+
Only one transforming filter per property is allowed. The filter may be positioned anywhere in the filter chain of a single property. If multiple filters are applied and a transforming filter is among them you have to make sure the property types are compatible. If you use a custom filter after the dateTime filter for example the custom filter has to accept a DateTime value. Filters used before a transforming filter must accept the base type of the property the filter is applied to defined in the schema. If the transformation of a property fails (the transforming filter throws an exception), subsequent filters won't be executed as their execution would add another error due to incompatible types which is irrelevant for the currently provided value.
9595

9696
If you write a custom transforming filter you must define the return type of your filter function as the implementation uses Reflection methods to determine to which type a value is transformed by a filter.
9797

src/Model/GeneratorConfiguration.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PHPModelGenerator\Model;
66

7+
use Exception;
78
use PHPModelGenerator\Exception\InvalidFilterException;
89
use PHPModelGenerator\Filter\FilterInterface;
910
use PHPModelGenerator\Filter\TransformingFilterInterface;
@@ -68,6 +69,7 @@ public function __construct()
6869
*
6970
* @return $this
7071
*
72+
* @throws Exception
7173
* @throws InvalidFilterException
7274
*/
7375
public function addFilter(FilterInterface ...$additionalFilter): self

src/Model/Property/AbstractProperty.php

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPModelGenerator\Exception\SchemaException;
88
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
99
use PHPModelGenerator\Model\SchemaDefinition\JsonSchemaTrait;
10+
use PHPModelGenerator\Utils\NormalizedName;
1011
use PHPModelGenerator\Utils\ResolvableTrait;
1112

1213
/**
@@ -70,33 +71,6 @@ public function getAttribute(bool $variableName = false): string
7071
*/
7172
protected function processAttributeName(string $name): string
7273
{
73-
$attributeName = preg_replace_callback(
74-
'/([a-z][a-z0-9]*)([A-Z])/',
75-
static function (array $matches): string {
76-
return "{$matches[1]}-{$matches[2]}";
77-
},
78-
$name
79-
);
80-
81-
$elements = array_map(
82-
static function (string $element): string {
83-
return ucfirst(strtolower($element));
84-
},
85-
preg_split('/[^a-z0-9]/i', $attributeName)
86-
);
87-
88-
$attributeName = lcfirst(join('', $elements));
89-
90-
if (empty($attributeName)) {
91-
throw new SchemaException(
92-
sprintf(
93-
"Property name '%s' results in an empty attribute name in file %s",
94-
$name,
95-
$this->jsonSchema->getFile()
96-
)
97-
);
98-
}
99-
100-
return $attributeName;
74+
return lcfirst(NormalizedName::from($name, $this->jsonSchema));
10175
}
10276
}

src/Model/Property/Property.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,15 @@ public function getType(bool $outputType = false): ?PropertyType
8989
/**
9090
* @inheritdoc
9191
*/
92-
public function setType(PropertyType $type = null, PropertyType $outputType = null): PropertyInterface
93-
{
92+
public function setType(
93+
PropertyType $type = null,
94+
PropertyType $outputType = null,
95+
bool $reset = false
96+
): PropertyInterface {
97+
if ($reset) {
98+
$this->typeHintDecorators = [];
99+
}
100+
94101
$this->type = $type;
95102
$this->outputType = $outputType;
96103

@@ -277,9 +284,9 @@ public function setReadOnly(bool $isPropertyReadOnly): PropertyInterface
277284
/**
278285
* @inheritdoc
279286
*/
280-
public function setDefaultValue($defaultValue): PropertyInterface
287+
public function setDefaultValue($defaultValue, bool $raw = false): PropertyInterface
281288
{
282-
$this->defaultValue = $defaultValue;
289+
$this->defaultValue = $defaultValue !== null && !$raw ? var_export($defaultValue, true) : $defaultValue;
283290

284291
return $this;
285292
}
@@ -289,7 +296,7 @@ public function setDefaultValue($defaultValue): PropertyInterface
289296
*/
290297
public function getDefaultValue(): ?string
291298
{
292-
return $this->defaultValue !== null ? var_export($this->defaultValue, true) : null;
299+
return $this->defaultValue;
293300
}
294301

295302
/**

src/Model/Property/PropertyInterface.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,15 @@ public function getType(bool $outputType = false): ?PropertyType;
4343
* @param PropertyType|null $type
4444
* @param PropertyType|null $outputType By default the output type will be equal to the base type but due to applied
4545
* filters the output type may change
46+
* @param bool $reset set to true for a full type reset (including type hint decorators like array, ...)
4647
*
4748
* @return PropertyInterface
4849
*/
49-
public function setType(PropertyType $type = null, PropertyType $outputType = null): PropertyInterface;
50+
public function setType(
51+
PropertyType $type = null,
52+
PropertyType $outputType = null,
53+
bool $reset = false
54+
): PropertyInterface;
5055

5156
/**
5257
* @param bool $outputType If set to true the output type hint will be returned (may differ from the base type)
@@ -158,10 +163,12 @@ public function setInternal(bool $isPropertyInternal): PropertyInterface;
158163

159164
/**
160165
* @param mixed $defaultValue
166+
* @param bool $raw By default, the provided value will be added to the generated code via var_export. If the raw
167+
* option is enabled the value provided in $defaultValue will not be changed.
161168
*
162169
* @return PropertyInterface
163170
*/
164-
public function setDefaultValue($defaultValue): PropertyInterface;
171+
public function setDefaultValue($defaultValue, bool $raw = false): PropertyInterface;
165172

166173
/**
167174
* @return string|null

src/Model/Property/PropertyProxy.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,11 @@ public function getType(bool $outputType = false): ?PropertyType
6969
/**
7070
* @inheritdoc
7171
*/
72-
public function setType(PropertyType $type = null, PropertyType $outputType = null): PropertyInterface
73-
{
72+
public function setType(
73+
PropertyType $type = null,
74+
PropertyType $outputType = null,
75+
bool $reset = false
76+
): PropertyInterface {
7477
return $this->getProperty()->setType($type, $outputType);
7578
}
7679

@@ -198,9 +201,9 @@ public function isReadOnly(): bool
198201
/**
199202
* @inheritdoc
200203
*/
201-
public function setDefaultValue($defaultValue): PropertyInterface
204+
public function setDefaultValue($defaultValue, bool $raw = false): PropertyInterface
202205
{
203-
return $this->getProperty()->setDefaultValue($defaultValue);
206+
return $this->getProperty()->setDefaultValue($defaultValue, $raw);
204207
}
205208

206209
/**

src/Model/SchemaDefinition/JsonSchema.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace PHPModelGenerator\Model\SchemaDefinition;
66

7+
use PHPModelGenerator\Utils\ArrayHash;
8+
79
/**
810
* Class JsonSchema
911
*
@@ -62,11 +64,7 @@ public function getJson(): array
6264
*/
6365
public function getSignature(): string
6466
{
65-
return md5(
66-
json_encode(
67-
array_intersect_key($this->json, array_fill_keys(self::SCHEMA_SIGNATURE_RELEVANT_FIELDS, null))
68-
)
69-
);
67+
return ArrayHash::hash($this->json, self::SCHEMA_SIGNATURE_RELEVANT_FIELDS);
7068
}
7169

7270
/**

src/Model/Validator/EnumValidator.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ class EnumValidator extends PropertyValidator
2323
*/
2424
public function __construct(PropertyInterface $property, array $allowedValues)
2525
{
26-
2726
parent::__construct(
2827
$property,
2928
'!in_array($value, ' . RenderHelper::varExportArray($allowedValues) . ', true)',

0 commit comments

Comments
 (0)