respect-validation/bin/create-mixin
Henrique Moody d7dc0f2b4e
Refactor the "NullOr" rule and related classes
This commit will rename the "Nullable" rule to "NullOr" while soft
deprecating the old name. It should work the same as the previous one
but with a different name. It will also prefix the result ID, allowing
more message customization.

While working on it, I realized that the prefix "nullOr" had a typo,
and it was using "nullOf" instead. I fixed that, too.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-26 01:55:50 +01:00

248 lines
8.4 KiB
PHP
Executable file

#!/usr/bin/env php
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use Nette\PhpGenerator\InterfaceType;
use Nette\PhpGenerator\PhpNamespace;
use Nette\PhpGenerator\Printer;
use Respect\Validation\Mixins\ChainedKey;
use Respect\Validation\Mixins\ChainedLength;
use Respect\Validation\Mixins\ChainedMax;
use Respect\Validation\Mixins\ChainedMin;
use Respect\Validation\Mixins\ChainedNot;
use Respect\Validation\Mixins\ChainedNullOr;
use Respect\Validation\Mixins\ChainedProperty;
use Respect\Validation\Mixins\ChainedUndefOr;
use Respect\Validation\Mixins\ChainedValidator;
use Respect\Validation\Mixins\StaticKey;
use Respect\Validation\Mixins\StaticLength;
use Respect\Validation\Mixins\StaticMax;
use Respect\Validation\Mixins\StaticMin;
use Respect\Validation\Mixins\StaticNot;
use Respect\Validation\Mixins\StaticNullOr;
use Respect\Validation\Mixins\StaticProperty;
use Respect\Validation\Mixins\StaticUndefOr;
use Respect\Validation\Rules\NullOr;
use Respect\Validation\Rules\UndefOr;
use Respect\Validation\Validatable;
function addMethodToInterface(
string $originalName,
InterfaceType $interfaceType,
ReflectionClass $reflection,
?string $prefix,
array $allowList,
array $denyList,
): void {
if ($allowList !== [] && !in_array($reflection->getShortName(), $allowList, true)) {
return;
}
if ($denyList !== [] && in_array($reflection->getShortName(), $denyList, true)) {
return;
}
$name = $prefix ? $prefix . ucfirst($originalName) : lcfirst($originalName);
$method = $interfaceType->addMethod($name)->setPublic()->setReturnType(ChainedValidator::class);
if (str_starts_with($interfaceType->getName(), 'Static')) {
$method->setStatic();
}
if ($prefix === 'key') {
$method->addParameter('key')->setType('int|string');
}
if ($prefix === 'property') {
$method->addParameter('propertyName')->setType('string');
}
$reflrectionConstructor = $reflection->getConstructor();
if ($reflrectionConstructor === null) {
return;
}
$commend = $reflrectionConstructor->getDocComment();
if ($commend) {
$method->addComment(preg_replace('@(/\*\* *| +\* +| +\*/)@', '', $commend));
}
if ($originalName === 'Optional') {
$method->addComment('@deprecated Use {@see undefOr()} instead.');
}
if ($originalName === 'Nullable') {
$method->addComment('@deprecated Use {@see nullOr()} instead.');
}
foreach ($reflrectionConstructor->getParameters() as $reflectionParameter) {
if ($reflectionParameter->isVariadic()) {
$method->setVariadic();
}
$type = $reflectionParameter->getType();
$types = [];
if ($type instanceof ReflectionUnionType) {
foreach ($type->getTypes() as $type) {
$types[] = $type->getName();
}
} elseif ($type instanceof ReflectionNamedType) {
$types[] = $type->getName();
if ( str_starts_with($type->getName(), 'Sokil')
|| str_starts_with($type->getName(), 'Egulias')
|| $type->getName() === 'finfo'
) {
continue;
}
}
$parameter = $method->addParameter($reflectionParameter->getName());
$parameter->setType(implode('|', $types));
if (!$reflectionParameter->isDefaultValueAvailable()) {
$parameter->setNullable($reflectionParameter->isOptional());
}
if (count($types) > 1 || $reflectionParameter->isVariadic()) {
$parameter->setNullable(false);
}
if (!$reflectionParameter->isDefaultValueAvailable()) {
continue;
}
$defaultValue = $reflectionParameter->getDefaultValue();
if (is_object($defaultValue)) {
continue;
}
$parameter->setDefaultValue($reflectionParameter->getDefaultValue());
$parameter->setNullable(false);
}
}
function overwriteFile(string $content, string $basename): void
{
file_put_contents(sprintf('%s/../library/Mixins/%s.php', __DIR__, $basename), implode(PHP_EOL . PHP_EOL, [
'<?php',
file_get_contents(__DIR__.'/../.docheader'),
'declare(strict_types=1);',
preg_replace('/extends (.+, )+/', 'extends' . PHP_EOL . '\1', $content),
]));
}
(static function (): void {
$numberRelatedRules = [
'Between',
'BetweenExclusive',
'Equals',
'Equivalent',
'Even',
'Factor',
'Fibonacci',
'Finite',
'GreaterThan',
'Identical',
'In',
'Infinite',
'LessThan',
'LessThanOrEqual',
'GreaterThanOrEqual',
'Multiple',
'Odd',
'PerfectSquare',
'Positive',
'PrimeNumber',
];
$structureRelatedRules = [
'Exists',
'Key',
'KeyExists',
'KeyOptional',
'KeySet',
'Optional',
'NullOr',
'Nullable',
'UndefOr',
'Property',
'PropertyExists',
'PropertyOptional',
];
$mixins = [
['Key', 'key', [], $structureRelatedRules],
['Length', 'length', $numberRelatedRules, []],
['Max', 'max', $numberRelatedRules, []],
['Min', 'min', $numberRelatedRules, []],
['Not', 'not', [], ['Not', 'NotEmpty', 'NotBlank', 'NotEmoji', 'NotOptional', 'NullOr', 'UndefOr', 'Optional']],
['NullOr', 'nullOr', [], ['Nullable', 'NullOr', 'Optional', 'UndefOr']],
['Property', 'property', [], $structureRelatedRules],
['UndefOr', 'undefOr', [], ['Nullable', 'NullOr', 'Optional', 'UndefOr']],
['Validator', null, [], []],
];
$names = [];
foreach (new DirectoryIterator(__DIR__ . '/../library/Rules') as $file) {
if (!$file->isFile()) {
continue;
}
$className = 'Respect\\Validation\\Rules\\' . $file->getBasename('.php');
$reflection = new ReflectionClass($className);
if ($reflection->isAbstract()) {
continue;
}
$names[$reflection->getShortName()] = $reflection;
if ($className === UndefOr::class) {
$names['Optional'] = $reflection;
}
if ($className === NullOr::class) {
$names['Nullable'] = $reflection;
}
}
ksort($names);
foreach ($mixins as [$name, $prefix, $allowList, $denyList]) {
$chainedNamespace = new PhpNamespace('Respect\\Validation\\Mixins');
$chainedNamespace->addUse(Validatable::class);
$chainedInterface = $chainedNamespace->addInterface('Chained' . $name);
$staticNamespace = new PhpNamespace('Respect\\Validation\\Mixins');
$staticNamespace->addUse(Validatable::class);
$staticInterface = $staticNamespace->addInterface('Static' . $name);
if ($name === 'Validator') {
$chainedInterface->addExtend(Validatable::class);
$chainedInterface->addExtend(ChainedKey::class);
$chainedInterface->addExtend(ChainedLength::class);
$chainedInterface->addExtend(ChainedMax::class);
$chainedInterface->addExtend(ChainedMin::class);
$chainedInterface->addExtend(ChainedNot::class);
$chainedInterface->addExtend(ChainedNullOr::class);
$chainedInterface->addExtend(ChainedProperty::class);
$chainedInterface->addExtend(ChainedUndefOr::class);
$staticInterface->addExtend(StaticKey::class);
$staticInterface->addExtend(StaticLength::class);
$staticInterface->addExtend(StaticMax::class);
$staticInterface->addExtend(StaticMin::class);
$staticInterface->addExtend(StaticNot::class);
$staticInterface->addExtend(StaticNullOr::class);
$staticInterface->addExtend(StaticProperty::class);
$staticInterface->addExtend(StaticUndefOr::class);
}
foreach ($names as $originalName => $reflection) {
addMethodToInterface($originalName, $staticInterface, $reflection, $prefix, $allowList, $denyList);
addMethodToInterface($originalName, $chainedInterface, $reflection, $prefix, $allowList, $denyList);
}
$printer = new Printer();
$printer->wrapLength = 117;
overwriteFile($printer->printNamespace($staticNamespace), $staticInterface->getName());
overwriteFile($printer->printNamespace($chainedNamespace), $chainedInterface->getName());
}
})();