Skip to content

Commit c461e32

Browse files
committed
Fix filter combined with multi type properties
1 parent bad2b3b commit c461e32

18 files changed

+229
-62
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ The recommended way to install php-json-schema-model-generator is through [Compo
3838
$ composer require --dev wol-soft/php-json-schema-model-generator
3939
$ composer require wol-soft/php-json-schema-model-generator-production
4040
```
41-
To avoid adding all dependencies of the php-json-schema-model-generator to your production dependencies it's recommended to add the library as a dev-dependency and include the [wol-soft/php-json-schema-model-generator-production](https://github.com/wol-soft/php-json-schema-model-generator-production) library. The production library provides all classes to run the generated code. Generating the classes should either be a step done in the development environment (if you decide to commit the models) or as a build step of your application.
41+
42+
To avoid adding all dependencies of the php-json-schema-model-generator to your production dependencies it's recommended to add the library as a dev-dependency and include the [wol-soft/php-json-schema-model-generator-production](https://github.com/wol-soft/php-json-schema-model-generator-production) library. The production library provides all classes to run the generated code. Generating the classes should either be a step done in the development environment or as a build step of your application (for example you could generate the models in a [composer post-update-cmd script](https://getcomposer.org/doc/articles/scripts.md#command-events), which is the recommended workflow).
4243

4344
## Basic usage ##
4445

@@ -83,6 +84,7 @@ Method | Configuration | Default
8384
--- | --- | ---
8485
``` setNamespacePrefix(string $prefix) ``` <br><br>Example:<br> ``` setNamespacePrefix('\MyApp\Model') ``` | Configures a namespace prefix for all generated classes. The namespaces will be extended with the directory structure of the source directory. | Empty string so no namespace prefix will be used
8586
``` setImmutable(bool $immutable) ``` <br><br>Example:<br> ``` setImmutable(false) ``` | If set to true the generated model classes will be delivered without setter methods for the object properties. | true
87+
``` setImplicitNull(bool $allowImplicitNull) ``` <br><br>Example:<br> ``` setImplicitNull(true) ``` | By setting the implicit null option to true all of your object properties which aren't required will implicitly accept null. | false
8688
``` setCollectErrors(bool $collectErrors) ``` <br><br>Example:<br> ``` setCollectErrors(false) ``` | By default the complete input is validated and in case of failing validations all error messages will be thrown in a single exception. If set to false the first failing validation will throw an exception. | true
8789
``` setPrettyPrint(bool $prettyPrint) ``` <br><br>Example:<br> ``` setPrettyPrint(true) ``` | If set to false, the generated model classes won't follow coding guidelines (but the generation is faster). If enabled the package [Symplify/EasyCodingStandard](https://github.com/Symplify/EasyCodingStandard) will be used to clean up the generated code. By default pretty printing is disabled. | false
8890
``` setSerialization(bool $serialization) ``` <br><br>Example:<br> ``` setSerialization(true) ``` | If set to true the serialization methods `toArray` and `toJSON` will be added to the public interface of the generated classes. | false

docs/source/gettingStarted.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ The recommended way to install php-json-model-generator is through `Composer <ht
1111
composer require --dev wol-soft/php-json-schema-model-generator
1212
composer require wol-soft/php-json-schema-model-generator-production
1313
14-
To avoid adding all dependencies of the php-json-model-generator to your production dependencies it's recommended to add the library as a dev-dependency and include the php-json-model-generator-exception library. The exception library provides all classes to run the generated code. Generating the classes should either be a step done in the development environment (if you decide to commit the models) or as a build step of your application.
14+
To avoid adding all dependencies of the php-json-model-generator to your production dependencies it's recommended to add the library as a dev-dependency and include the php-json-model-generator-exception library. The exception library provides all classes to run the generated code. Generating the classes should either be a step done in the development environment or as a build step of your application (for example you could generate the models in a `composer post-update-cmd script<https://getcomposer.org/doc/articles/scripts.md#command-events>`__, which is the recommended workflow).
1515

1616
Generating classes
1717
------------------
@@ -154,6 +154,22 @@ If set to true the generated model classes will be delivered without setter meth
154154
(new GeneratorConfiguration())
155155
->setImmutable(false);
156156
157+
Implicit null
158+
^^^^^^^^^^^^^
159+
160+
By default the properties are strictly checked against their defined types. Consequently if you want a property to accept null you have to extend the type of your property explicitly (eg. ['string', 'null']).
161+
162+
By setting the implicit null option to true all of your object properties which aren't required will implicitly accept null. All properties which are required and don't explicitly allow null in the type definition will still reject null.
163+
164+
.. code-block:: php
165+
166+
setImplicitNull(bool $allowImplicitNull);
167+
168+
.. code-block:: php
169+
170+
(new GeneratorConfiguration())
171+
->setImplicitNull(true);
172+
157173
Collect errors vs. early return
158174
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
159175

docs/source/nonStandardExtensions/filter.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,9 @@ The callable filter method must be a static method. Internally it will be called
274274
public function getAcceptedTypes(): array
275275
{
276276
// return an array of types which can be handled by the filter.
277-
// valid types are: [integer, number, boolean, string, array] or available classes (FQCN required, eg.
278-
// DateTime::class)
279-
return ['string'];
277+
// valid types are: [integer, number, boolean, string, array, null]
278+
// or available classes (FQCN required, eg. DateTime::class)
279+
return ['string', 'null'];
280280
}
281281
282282
public function getToken(): string
@@ -290,6 +290,10 @@ The callable filter method must be a static method. Internally it will be called
290290
}
291291
}
292292
293+
.. hint::
294+
295+
If your filter accepts null values add 'null' to your *getAcceptedTypes* to make sure your filter is compatible with explicit null type
296+
293297
.. hint::
294298

295299
If a filter with the token of your custom filter already exists the existing filter will be overwritten when adding the filter to the generator configuration. By overwriting filters you may change the behaviour of builtin filters by replacing them with your custom implementation.
@@ -387,12 +391,12 @@ The custom serializer method will be called if the model utilizing the custom fi
387391
388392
public function getAcceptedTypes(): array
389393
{
390-
return ['object'];
394+
return ['string', 'null'];
391395
}
392396
393397
public function getToken(): string
394398
{
395-
return 'uppercase';
399+
return 'customer';
396400
}
397401
398402
public function getFilter(): array

docs/source/types/null.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ Generated interface (as null is no explicit type no typehints are generated):
2525
Possible exceptions:
2626

2727
* Invalid type for property. Requires null, got __TYPE__
28+
29+
The main use case for the **null** type is a property with `multiple types <complexTypes/multiType.html>`__ accepting for example a string and null values when using explicit null types.

src/Model/GeneratorConfiguration.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class GeneratorConfiguration
2727
/** @var bool */
2828
protected $immutable = false;
2929
/** @var bool */
30-
protected $allowImplicitNull = true;
30+
protected $allowImplicitNull = false;
3131
/** @var bool */
3232
protected $prettyPrint = false;
3333
/** @var bool */
@@ -88,7 +88,7 @@ public function addFilter(FilterInterface $filter): self
8888
}
8989

9090
foreach ($filter->getAcceptedTypes() as $acceptedType) {
91-
if (!in_array($acceptedType, ['integer', 'number', 'boolean', 'string', 'array']) &&
91+
if (!in_array($acceptedType, ['integer', 'number', 'boolean', 'string', 'array', 'null']) &&
9292
!class_exists($acceptedType)
9393
) {
9494
throw new InvalidFilterException('Filter accepts invalid types');

src/Model/Validator/FilterValidator.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,7 @@ public function __construct(
5656
'typeCheck' => !empty($filter->getAcceptedTypes())
5757
? '($value !== null && (' .
5858
implode(' && ', array_map(function (string $type) use ($property): string {
59-
return (new ReflectionTypeCheckValidator(
60-
in_array($type, ['int', 'float', 'string', 'bool', 'array', 'object']),
61-
$type,
62-
$property
63-
))->getCheck();
59+
return ReflectionTypeCheckValidator::fromType($type, $property)->getCheck();
6460
}, $this->mapDataTypes($filter->getAcceptedTypes()))) .
6561
'))'
6662
: '',
@@ -179,9 +175,7 @@ private function mapDataTypes(array $acceptedTypes): array
179175
switch ($jsonSchemaType) {
180176
case 'integer': return 'int';
181177
case 'number': return 'float';
182-
case 'string': return 'string';
183178
case 'boolean': return 'bool';
184-
case 'array': return 'array';
185179

186180
default: return $jsonSchemaType;
187181
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace PHPModelGenerator\Model\Validator;
6+
7+
use PHPModelGenerator\Model\Property\PropertyInterface;
8+
9+
/**
10+
* Class MultiTypeCheckValidator
11+
*
12+
* @package PHPModelGenerator\Model\Validator
13+
*/
14+
class MultiTypeCheckValidator extends PropertyValidator implements TypeCheckInterface
15+
{
16+
/** @var string[] */
17+
protected $types;
18+
19+
/**
20+
* MultiTypeCheckValidator constructor.
21+
*
22+
* @param string[] $types
23+
* @param PropertyInterface $property
24+
* @param bool $allowImplicitNull
25+
*/
26+
public function __construct(array $types, PropertyInterface $property, bool $allowImplicitNull)
27+
{
28+
$this->types = $types;
29+
30+
// if null is explicitly allowed we don't need an implicit null pass through
31+
if (in_array('null', $types)) {
32+
$allowImplicitNull = false;
33+
}
34+
35+
parent::__construct(
36+
join(
37+
' && ',
38+
array_map(
39+
function (string $allowedType) use ($property) : string {
40+
return ReflectionTypeCheckValidator::fromType($allowedType, $property)->getCheck();
41+
},
42+
$types
43+
)
44+
) . ($allowImplicitNull ? ' && $value !== null' : ''),
45+
sprintf(
46+
'Invalid type for %s. Requires [%s], got " . gettype($value) . "',
47+
$property->getName(),
48+
implode(', ', $types)
49+
)
50+
51+
);
52+
}
53+
54+
/**
55+
* @inheritDoc
56+
*/
57+
public function getTypes(): array
58+
{
59+
return $this->types;
60+
}
61+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace PHPModelGenerator\Model\Validator;
6+
7+
use PHPModelGenerator\Model\Property\PropertyInterface;
8+
use ReflectionType;
9+
10+
/**
11+
* Class PassThroughTypeCheckValidator
12+
*
13+
* @package PHPModelGenerator\Model\Validator
14+
*/
15+
class PassThroughTypeCheckValidator extends PropertyValidator implements TypeCheckInterface
16+
{
17+
/** @var string[] */
18+
protected $types;
19+
20+
/**
21+
* PassThroughTypeCheckValidator constructor.
22+
*
23+
* @param ReflectionType $passThroughType
24+
* @param PropertyInterface $property
25+
* @param TypeCheckValidator $typeCheckValidator
26+
*/
27+
public function __construct(
28+
ReflectionType $passThroughType,
29+
PropertyInterface $property,
30+
TypeCheckValidator $typeCheckValidator
31+
) {
32+
$this->types = array_merge($typeCheckValidator->getTypes(), [$passThroughType->getName()]);
33+
34+
parent::__construct(
35+
sprintf(
36+
'%s && %s',
37+
ReflectionTypeCheckValidator::fromReflectionType($passThroughType, $property)->getCheck(),
38+
$typeCheckValidator->getCheck()
39+
),
40+
sprintf(
41+
'Invalid type for %s. Requires [%s, %s], got " . gettype($value) . "',
42+
$property->getName(),
43+
$passThroughType->getName(),
44+
$property->getType()
45+
)
46+
);
47+
}
48+
49+
/**
50+
* @inheritDoc
51+
*/
52+
public function getTypes(): array
53+
{
54+
return $this->types;
55+
}
56+
}

src/Model/Validator/ReflectionTypeCheckValidator.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ public static function fromReflectionType(
3131
);
3232
}
3333

34+
/**
35+
* @param string $type
36+
* @param PropertyInterface $property
37+
*
38+
* @return static
39+
*/
40+
public static function fromType(
41+
string $type,
42+
PropertyInterface $property
43+
): self {
44+
return new self(
45+
in_array($type, ['int', 'float', 'string', 'bool', 'array', 'object', 'null']),
46+
$type,
47+
$property
48+
);
49+
}
50+
3451
/**
3552
* ReflectionTypeCheckValidator constructor.
3653
*
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace PHPModelGenerator\Model\Validator;
6+
7+
/**
8+
* Interface TypeCheckInterface
9+
*
10+
* @package PHPModelGenerator\Model\Validator
11+
*/
12+
interface TypeCheckInterface
13+
{
14+
/**
15+
* Get all types accepted by the type check validator
16+
*
17+
* @return string[]
18+
*/
19+
public function getTypes(): array;
20+
}

0 commit comments

Comments
 (0)