Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
}
],
"require": {
"wol-soft/php-json-schema-model-generator-production": "dev-jsonSchemaDraft7",
"wol-soft/php-json-schema-model-generator-production": "dev-jsonSchemaDraft2019",
"wol-soft/php-micro-template": "^1.10.2",

"php": ">=8.4",
Expand Down
2 changes: 1 addition & 1 deletion src/Draft/Draft_07.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function getDefinition(): DraftBuilder
->addValidator('minItems', new MinItemsValidatorFactory())
->addValidator('maxItems', new MaxItemsValidatorFactory())
->addValidator('uniqueItems', new UniqueItemsValidatorFactory())
->addValidator('contains', new ContainsValidatorFactory())
->addValidator('contains', new ContainsValidatorFactory(false))
->addModifier(new DefaultArrayToEmptyArrayModifier()))
->addType((new Type('string'))
->addValidator('pattern', new PatternPropertyValidatorFactory())
Expand Down
20 changes: 20 additions & 0 deletions src/Draft/Draft_2019_09.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace PHPModelGenerator\Draft;

use PHPModelGenerator\Model\Validator\Factory\Arrays\ContainsValidatorFactory;

class Draft_2019_09 extends Draft_07
{
public function getDefinition(): DraftBuilder
{
$builder = parent::getDefinition();

$builder->getType('array')
->addValidator('contains', new ContainsValidatorFactory());

return $builder;
}
}
11 changes: 11 additions & 0 deletions src/Draft/Draft_2020_12.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace PHPModelGenerator\Draft;

use PHPModelGenerator\Model\Validator\Factory\Arrays\ContainsValidatorFactory;

class Draft_2020_12 extends Draft_2019_09
{
}
8 changes: 2 additions & 6 deletions src/Model/GeneratorConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,6 @@ class GeneratorConfiguration
*/
public function __construct()
{
$this->draft = new AutoDetectionDraft();
$this->classNameGenerator = new ClassNameGenerator();

// add all built-in filter and format validators
$this->initFilter();
$this->initFormatValidator();
}
Expand Down Expand Up @@ -151,7 +147,7 @@ public function getFilter(string $token): ?FilterInterface

public function getClassNameGenerator(): ClassNameGeneratorInterface
{
return $this->classNameGenerator;
return $this->classNameGenerator ??= new ClassNameGenerator();
}

public function setClassNameGenerator(ClassNameGeneratorInterface $classNameGenerator): self
Expand Down Expand Up @@ -259,7 +255,7 @@ public function setErrorRegistryClass(string $errorRegistryClass): self

public function getDraft(): DraftInterface | DraftFactoryInterface
{
return $this->draft;
return $this->draft ??= new AutoDetectionDraft();
}

public function setDraft(DraftInterface | DraftFactoryInterface $draft): self
Expand Down
4 changes: 0 additions & 4 deletions src/Model/Validator/AdditionalItemsValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
namespace PHPModelGenerator\Model\Validator;

use PHPModelGenerator\Exception\Arrays\InvalidAdditionalTupleItemsException;
use PHPModelGenerator\Exception\SchemaException;
use PHPModelGenerator\Model\Schema;
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
use PHPModelGenerator\SchemaProcessor\SchemaProcessor;

/**
* Class AdditionalItemsValidator
Expand Down
82 changes: 80 additions & 2 deletions src/Model/Validator/Factory/Arrays/ContainsValidatorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,24 @@
namespace PHPModelGenerator\Model\Validator\Factory\Arrays;

use PHPModelGenerator\Exception\Arrays\ContainsException;
use PHPModelGenerator\Exception\Arrays\MaxContainsException;
use PHPModelGenerator\Exception\Arrays\MinContainsException;
use PHPModelGenerator\Exception\SchemaException;
use PHPModelGenerator\Model\Property\PropertyInterface;
use PHPModelGenerator\Model\Schema;
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
use PHPModelGenerator\Model\Validator\Factory\AbstractValidatorFactory;
use PHPModelGenerator\Model\Validator\PropertyTemplateValidator;
use PHPModelGenerator\Model\Validator\PropertyValidator;
use PHPModelGenerator\PropertyProcessor\PropertyFactory;
use PHPModelGenerator\SchemaProcessor\SchemaProcessor;
use PHPModelGenerator\Utils\RenderHelper;

class ContainsValidatorFactory extends AbstractValidatorFactory
{
public function __construct(private readonly bool $supportMinMaxContains = true)
{}

/**
* @throws SchemaException
*/
Expand All @@ -40,18 +46,90 @@ public function modify(
$propertySchema->navigate($this->key),
);

$countMatches = $this->supportMinMaxContains &&
(array_key_exists('minContains', $json) || array_key_exists('maxContains', $json));

$property->addValidator(
new PropertyTemplateValidator(
new class(
$property,
DIRECTORY_SEPARATOR . 'Validator' . DIRECTORY_SEPARATOR . 'ArrayContains.phptpl',
[
'property' => $nestedProperty,
'schema' => $schema,
'countMatches' => $countMatches,
'allowNoMatch' => $json['minContains'] ?? 1 === 0,
'viewHelper' => new RenderHelper($schemaProcessor->getGeneratorConfiguration()),
'generatorConfiguration' => $schemaProcessor->getGeneratorConfiguration(),
],
ContainsException::class,
),
) extends PropertyTemplateValidator {
public function getValidatorSetUp(): string
{
return $this->templateValues['countMatches'] ? '
$containsMatches = 0;
' : '';
}
},
);

if (!$countMatches) {
return;
}

if (array_key_exists('minContains', $json)) {
if (!is_int($json['minContains']) || $json['minContains'] < 0) {
throw new SchemaException(
sprintf(
"Invalid minContains %s for property '%s' in file %s",
str_replace("\n", '', var_export($json[$this->key], true)),
$property->getName(),
$propertySchema->getFile(),
),
);
}

$property->addValidator(
new PropertyValidator(
$property,
"\$containsMatches < {$json['minContains']}",
MinContainsException::class,
[$json['minContains'], '&$containsMatches'],
),
);
}

if (array_key_exists('maxContains', $json)) {
if (!is_int($json['maxContains']) || $json['maxContains'] < 1) {
throw new SchemaException(
sprintf(
"Invalid minContains %s for property '%s' in file %s",
str_replace("\n", '', var_export($json[$this->key], true)),
$property->getName(),
$propertySchema->getFile(),
),
);
}

$property->addValidator(
new PropertyValidator(
$property,
"\$containsMatches > {$json['maxContains']}",
MaxContainsException::class,
[$json['maxContains'], '&$containsMatches'],
),
);
}

if (isset($json['minContains'], $json['maxContains']) && $json['minContains'] > $json['maxContains']) {
throw new SchemaException(
sprintf(
"minContains (%s) must not be larger than maxContains (%s) for property '%s' in file %s",
$json['minContains'],
$json['maxContains'],
$property->getName(),
$propertySchema->getFile(),
),
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ protected function getValidator(PropertyInterface $property, mixed $value): Prop
{
return new PropertyValidator(
$property,
"is_array(\$value) && count(\$value) > $value",
"is_array(\$value) && (\$count = count(\$value)) > $value",
MaxItemsException::class,
[$value],
[$value, '&$count'],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ protected function getValidator(PropertyInterface $property, mixed $value): Prop
{
return new PropertyValidator(
$property,
"is_array(\$value) && count(\$value) < $value",
"is_array(\$value) && (\$count = count(\$value)) < $value",
MinItemsException::class,
[$value],
[$value, '&$count'],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@

class MaxPropertiesValidatorFactory extends SimpleBaseValidatorFactory
{
private const string COUNT_PROPERTIES =
'count(
array_unique(
array_merge(
array_keys($this->rawModelDataInput),
array_keys($modelData),
)
),
)';

protected function isValueValid(mixed $value): bool
{
return is_int($value) && $value >= 0;
Expand All @@ -31,9 +21,9 @@ protected function getValidator(PropertyInterface $property, mixed $value): Prop
{
return new PropertyValidator(
$property,
sprintf('%s > %d', self::COUNT_PROPERTIES, $value),
sprintf('%s > %d', MinPropertiesValidatorFactory::COUNT_PROPERTIES, $value),
MaxPropertiesException::class,
[$value],
[$value, '&$count'],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,22 @@
namespace PHPModelGenerator\Model\Validator\Factory\Object;

use PHPModelGenerator\Exception\Object\MinPropertiesException;
use PHPModelGenerator\Model\Property\Property;
use PHPModelGenerator\Model\Property\PropertyInterface;
use PHPModelGenerator\Model\Validator\Factory\SimpleBaseValidatorFactory;
use PHPModelGenerator\Model\Validator\PropertyValidator;
use PHPModelGenerator\Model\Validator\PropertyValidatorInterface;

class MinPropertiesValidatorFactory extends SimpleBaseValidatorFactory
{
private const string COUNT_PROPERTIES =
'count(
public const string COUNT_PROPERTIES =
'($count = count(
array_unique(
array_merge(
array_keys($this->rawModelDataInput),
array_keys($modelData),
)
),
)';
))';

protected function isValueValid(mixed $value): bool
{
Expand All @@ -34,7 +33,7 @@ protected function getValidator(PropertyInterface $property, mixed $value): Prop
$property,
sprintf('%s < %d', self::COUNT_PROPERTIES, $value),
MinPropertiesException::class,
[$value],
[$value, '&$count'],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected function hasValidValue(PropertyInterface $property, JsonSchema $proper
if (!$this->isValueValid($json[$this->key])) {
throw new SchemaException(
sprintf(
"Invalid %s %s for property '%s' in file %s",
"Invalid %s (%s) for property '%s' in file %s",
$this->key,
str_replace("\n", '', var_export($json[$this->key], true)),
$property->getName(),
Expand Down
6 changes: 1 addition & 5 deletions src/Model/Validator/PropertyTemplateValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
*/
class PropertyTemplateValidator extends AbstractPropertyValidator
{
/** @var array */
protected $templateValues;
/** @var Schema|null */
protected $scope;

Expand All @@ -30,12 +28,10 @@ class PropertyTemplateValidator extends AbstractPropertyValidator
public function __construct(
PropertyInterface $property,
protected string $template,
array $templateValues,
protected array $templateValues,
string $exceptionClass,
array $exceptionParams = [],
) {
$this->templateValues = $templateValues;

parent::__construct($property, $exceptionClass, $exceptionParams);
}

Expand Down
16 changes: 12 additions & 4 deletions src/Templates/Validator/ArrayContains.phptpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
is_array($value) && (function (&$items) {
is_array($value) && (function (&$items){% if countMatches %} use (&$countMatches){% endif %} {
if (empty($items)) {
return true;
}
Expand Down Expand Up @@ -27,8 +27,12 @@ is_array($value) && (function (&$items) {
$this->errorRegistry = $originalErrorRegistry;
{% endif %}

// one matched item is enough to pass the contains check
return false;
{% if countMatches %}
$countMatches++;
{% else %}
// one matched item is enough to pass the contains check
return false;
{% endif %}
} catch (\Exception $e) {
continue;
}
Expand All @@ -38,5 +42,9 @@ is_array($value) && (function (&$items) {
$this->errorRegistry = $originalErrorRegistry;
{% endif %}

return true;
return {% if countMatches %}
{% if allowNoMatch %}false{% else %}$countMatches === 0{% endif %}
{% else %}
true
{% endif %};
})($value)
Loading