diff --git a/demo/example.fpp b/demo/example.fpp index dbcac49f..9aa8f654 100644 --- a/demo/example.fpp +++ b/demo/example.fpp @@ -4,9 +4,17 @@ namespace Foo\Bar { marker Event; marker Command; - data Person = NonPerson { DateTimeImmutable $registered, ?UserId $userId, Name[] $names, ?int $age = 18, string[] $emails = [], HumanId[] $humanIds, - Address[] $addresses - } | GoodPerson { UserId $userId, ?int $age = 18 }; + data Person = + NonPerson { + DateTimeImmutable $registered, + ?UserId $userId, + Name[] $names, + ?int $age = 18, + string[] $emails = [], + HumanId[] $humanIds, + Address[] $addresses + } + | GoodPerson { UserId $userId, ?int $age = 18 }; data Address = { string $street, int $no }; diff --git a/src/Functions/fpp.php b/src/Functions/fpp.php index d4d8a526..e2bd12b7 100644 --- a/src/Functions/fpp.php +++ b/src/Functions/fpp.php @@ -325,3 +325,209 @@ function renameDuplicateArgumentNames(array $names, array $arguments): array return $result; } + +const generateValidationFor = 'Fpp\generateValidationFor'; + +function generateValidationFor( + Argument $a, + string $paramName, + ?string $resolvedType, + array $definitions, + Configuration $config +): string { + if ($a->isList()) { + $code = "if (! isset(\$data['{$a->name()}']) || ! \is_array(\${$paramName}['{$a->name()}'])) {\n"; + $code .= " throw new \InvalidArgumentException('Error on \"{$a->name()}\": array expected');\n"; + $code .= "}\n\n"; + + return $code; + } + + if ($a->nullable()) { + $code = "if (isset(\${$paramName}['{$a->name()}']) && ! {%validation%}) {\n"; + $code .= " throw new \InvalidArgumentException('{%validationErrorMessage%}');\n"; + $code .= "}\n\n"; + } else { + $code = "if (! isset(\${$paramName}['{$a->name()}']) || ! {%validation%}) {\n"; + $code .= " throw new \InvalidArgumentException('{%validationErrorMessage%}');\n"; + $code .= "}\n\n"; + } + + switch ($a->type()) { + case null: + return ''; + case 'int': + $validation = "\is_int(\${$paramName}['{$a->name()}'])"; + $validationErrorMessage = "Error on \"{$a->name()}\", int expected"; + break; + case 'float': + $validation = "\is_float(\${$paramName}['{$a->name()}'])"; + $validationErrorMessage = "Error on \"{$a->name()}\", float expected"; + break; + case 'bool': + $validation = "\is_bool(\${$paramName}['{$a->name()}'])"; + $validationErrorMessage = "Error on \"{$a->name()}\", bool expected"; + break; + case 'string': + $validation = "\is_string(\${$paramName}['{$a->name()}'])"; + $validationErrorMessage = "Error on \"{$a->name()}\", string expected"; + break; + case 'array': + $validation = "\is_array(\${$paramName}['{$a->name()}'])"; + $validationErrorMessage = "Error on \"{$a->name()}\", array expected"; + break; + default: + $definition = $definitions[$resolvedType] ?? null; + + if (null === $definition) { + /** @var TypeConfiguration|null $typeConfig */ + $typeConfig = $config->types()[$resolvedType] ?? null; + + if (null === $typeConfig) { + return ''; + } + + $validator = $typeConfig->validator(); + $validatorErrorMessage = $typeConfig->validationErrorMessage(); + } else { + $type = $definition->type(); + + $validator = $config->validatorFor($type); + $validatorErrorMessage = $config->validationErrorMessageFor($type); + } + + $validation = $validator("{$paramName}['{$a->name()}']"); + $validationErrorMessage = $validatorErrorMessage("\$data[\'{$a->name()}\']"); + + break; + } + + return \str_replace( + [ + '{%validation%}', + '{%validationErrorMessage%}', + ], + [ + $validation, + $validationErrorMessage, + ], + $code + ); +} + +const generateFromPhpValueFor = 'Fpp\generateFromPhpValueFor'; + +function generateFromPhpValueFor( + Argument $a, + string $paramName, + int $intentLevel, + ?string $resolvedType, + array $definitions, + Configuration $config +): string { + $intent = \str_repeat(' ', $intentLevel * 4); + + switch ($a->type()) { + case null: + case 'int': + case 'float': + case 'bool': + case 'string': + case 'array': + // yes all above are treated the same + if ($a->nullable()) { + return "{$intent}{$paramName}['{$a->name()}'] ?? null"; + } + + return "{$intent}{$paramName}['{$a->name()}']"; + default: + $definition = $definitions[$resolvedType] ?? null; + + if (null === $definition) { + /** @var TypeConfiguration|null $typeConfig */ + $typeConfig = $config->types()[$resolvedType] ?? null; + + if (null === $typeConfig) { + return "{$intent}{$paramName}['{$a->name()}']"; + } + + if ($a->isList()) { + return << {$typeConfig->fromPhpValue()($a->type(), 'e')}, {$paramName}['{$a->name()}']) +CODE; + } + + return $intent . $typeConfig->fromPhpValue()($a->type(), "this->payload['{$a->name()}']"); + } + + $builder = $config->fromPhpValueFor($definition->type()); + + if ($a->isList()) { + $callback = "fn(\$e) => {$builder($definition->type(), '$e')}"; + + return "{$intent}\array_map($callback, {$paramName}['{$a->name()}'])"; + } + + if ($a->nullable()) { + return "{$intent}isset({$paramName}['{$a->name()}']) ? " . $builder($definition->type(), "{$paramName}['{$a->name()}']") . ' : null'; + } + + return $intent . $builder($definition->type(), "{$paramName}['{$a->name()}']") . ''; + } +} + +function generateToArrayBodyFor(Argument $a, $prefix, ?string $resolvedType, array $definitions, Configuration $config): string +{ + switch ($a->type()) { + case null: + case 'int': + case 'float': + case 'bool': + case 'string': + case 'array': + // yes all above are treated the same + return " '{$a->name()}' => {$prefix}{$a->name()},\n"; + default: + $definition = $definitions[$resolvedType] ?? null; + + if (null === $definition) { + /** @var TypeConfiguration|null $typeConfiguration */ + $typeConfiguration = $config->types()[$resolvedType] ?? null; + + if (null === $typeConfiguration) { + return " '{$a->name()}' => {$prefix}{$a->name()},\n"; + } + + if ($a->isList()) { + $callback = "fn({$a->type()} \$e) => {$typeConfiguration->toPhpValue()('$e')}"; + + return <<name()}' => \array_map($callback, {$prefix}{$a->name()}), + +CODE; + } + + if ($a->nullable()) { + return " '{$a->name()}' => {$prefix}{$a->name()} !=== null ? " + . ($typeConfiguration->toPhpValue()('$this->' . $a->name())) . " : null,\n"; + } + + return " '{$a->name()}' => " . ($typeConfiguration->toPhpValue()('$this->' . $a->name())) . ",\n"; + } + + $builder = $config->toPhpValueFor($definition->type()); + + if ($a->isList()) { + $callback = "fn({$a->type()} \$e) => {$builder($definition->type(), '$e')}"; + + return " '{$a->name()}' => \array_map($callback, {$prefix}{$a->name()}),\n"; + } + + if ($a->nullable()) { + return " '{$a->name()}' => {$prefix}{$a->name()} !== null ? {$prefix}" + . ($builder)($definition->type(), $a->name()) . " : null,\n"; + } + + return " '{$a->name()}' => {$prefix}" . ($builder)($definition->type(), $a->name()) . ",\n"; + } +} diff --git a/src/Type/Bool_.php b/src/Type/Bool_.php index 2cdab92c..32e9a78d 100644 --- a/src/Type/Bool_.php +++ b/src/Type/Bool_.php @@ -94,8 +94,8 @@ function build(Definition $definition, array $definitions, Configuration $config $method->setBody('return $this->value;'); $method = $class->addMethod('equals')->setReturnType(Type::BOOL); - $method->addParameter('other')->setType(Type::SELF); - $method->setBody('return $this->value === $other->value;'); + $method->addParameter('other')->setType(Type::SELF)->setNullable(); + $method->setBody('return null !== $other && $this->value === $other->value;'); return [$fqcn => $file]; } diff --git a/src/Type/Command.php b/src/Type/Command.php index 38c1d8cc..b48b272f 100644 --- a/src/Type/Command.php +++ b/src/Type/Command.php @@ -19,6 +19,8 @@ use Fpp\Configuration; use function Fpp\constructorSeparator; use Fpp\Definition; +use function Fpp\generateFromPhpValueFor; +use function Fpp\generateValidationFor; use function Fpp\many1; use function Fpp\not; use function Fpp\parseArguments; @@ -138,8 +140,8 @@ function build(Definition $definition, array $definitions, Configuration $config ->setDefaultValue([]); $constructor->setBody(<<commandId = \$commandId ?? {$type->commandIdType()}::generate(); -\$this->payload = \$payload; \$this->metadata = \$metadata; +\$this->setPayload(\$payload); CODE ); @@ -147,6 +149,12 @@ function build(Definition $definition, array $definitions, Configuration $config ->setAbstract() ->setReturnType(Type::STRING); + $class->addMethod('setPayload') + ->setProtected() + ->setReturnType(Type::VOID) + ->setBody('$this->payload = $payload;') + ->addParameter('payload')->setType(Type::ARRAY); + $class->addMethod('commandId') ->setBody('return $this->commandId;') ->setReturnType($type->commandIdType()); @@ -395,15 +403,24 @@ function buildSubType( ->setReturnType(Type::STRING) ->setBody('return $this->commandType;'); + $setPayloadMethod = $class->addMethod('setPayload') + ->setProtected() + ->setReturnType(Type::VOID); + + $setPayloadMethod->addParameter('payload')->setType(Type::ARRAY); + $setPayloadMethodBody = ''; + \array_map( function (Argument $a) use ( $class, $definition, $definitions, - $config + $config, + &$setPayloadMethodBody ) { $resolvedType = resolveType($a->type(), $definition); - $fromPhpValue = calculateFromPhpValueFor($a, $resolvedType, $definitions, $config); + $fromPhpValue = generateFromPhpValueFor($a, '$this->payload', 0, $resolvedType, $definitions, $config); + $setPayloadMethodBody .= generateValidationFor($a, 'payload', $resolvedType, $definitions, $config); $psalmAnnotation = ''; @@ -427,58 +444,10 @@ function (Argument $a) use ( $constructor->arguments() ); - return $file; -} - -function calculateFromPhpValueFor(Argument $a, ?string $resolvedType, array $definitions, Configuration $config): string -{ - switch ($a->type()) { - case null: - case 'int': - case 'float': - case 'bool': - case 'string': - case 'array': - // yes all above are treated the same - if ($a->nullable()) { - return "\$this->payload['{$a->name()}'] ?? null"; - } - - return "\$this->payload['{$a->name()}']"; - default: - $definition = $definitions[$resolvedType] ?? null; - - if (null === $definition) { - /** @var TypeConfiguration|null $typeConfig */ - $typeConfig = $config->types()[$resolvedType] ?? null; - - if (null === $typeConfig) { - return "\$this->payload['{$a->name()}']"; - } - - if ($a->isList()) { - return << {$typeConfig->fromPhpValue()($a->type(), 'e')}, \$this->payload['{$a->name()}']) -CODE; - } - - return $typeConfig->fromPhpValue()($a->type(), "this->payload['{$a->name()}']"); - } - - $builder = $config->fromPhpValueFor($definition->type()); - - if ($a->isList()) { - $callback = "fn(\$e) => {$builder($definition->type(), '$e')}"; - - return "\array_map($callback, \$this->payload['{$a->name()}'])"; - } + $setPayloadMethodBody .= 'parent::setPayload($payload);'; + $setPayloadMethod->setBody($setPayloadMethodBody); - if ($a->nullable()) { - return "isset(\$this->payload['{$a->name()}']) ? " . $builder($definition->type(), "\$this->payload['{$a->name()}']") . ' : null'; - } - - return $builder($definition->type(), "\$this->payload['{$a->name()}']") . ''; - } + return $file; } function commandType(Constructor $constructor, string $namespace): string diff --git a/src/Type/Data.php b/src/Type/Data.php index ee37ea0b..0dba5a1d 100644 --- a/src/Type/Data.php +++ b/src/Type/Data.php @@ -20,6 +20,9 @@ use Fpp\Configuration; use function Fpp\constructorSeparator; use Fpp\Definition; +use function Fpp\generateFromPhpValueFor; +use function Fpp\generateToArrayBodyFor; +use function Fpp\generateValidationFor; use function Fpp\parseArguments; use Fpp\Parser; use function Fpp\plus; @@ -274,7 +277,7 @@ function buildSubType( $fromArrayBody = "return new self(\n"; $toArrayBody = "return [\n"; $equalsBody = <<type(), $definition); $defaultValue = calculateDefaultValue($a); - $fromArrayValidationBody .= calculateFromArrayValidationBodyFor($a, $resolvedType, $definitions, $config); - $fromArrayBody .= calculateFromArrayBodyFor($a, $resolvedType, $definitions, $config); - $toArrayBody .= calculateToArrayBodyFor($a, $resolvedType, $definitions, $config); + $fromArrayValidationBody .= generateValidationFor($a, 'data', $resolvedType, $definitions, $config); + $fromArrayBody .= generateFromPhpValueFor($a, '$data', 1, $resolvedType, $definitions, $config) . ",\n"; + $toArrayBody .= generateToArrayBodyFor($a, '$this->', $resolvedType, $definitions, $config); $equalsBody .= equalsBodyFor($a, $resolvedType, $definitions, $config); if (null !== $defaultValue) { @@ -351,7 +354,7 @@ function (Argument $a) use ( $toArray->setBody($toArrayBody); $equals = $class->addMethod('equals')->setReturnType(Type::BOOL); - $equals->addParameter('other')->setType($definition->type()->classname()); + $equals->addParameter('other')->setType($definition->type()->classname())->setNullable(); $equals->setBody($equalsBody . <<isList()) { - $code = "if (! isset(\$data['{$a->name()}']) || ! \is_array(\$data['{$a->name()}'])) {\n"; - $code .= " throw new \InvalidArgumentException('Error on \"{$a->name()}\": array expected');\n"; - $code .= "}\n\n"; - - return $code; - } - - if ($a->nullable()) { - $code = "if (isset(\$data['{$a->name()}']) && ! {%validator%}) {\n"; - $code .= " throw new \InvalidArgumentException('{%validationErrorMessage%}');\n"; - $code .= "}\n\n"; - } else { - $code = "if (! {%validator%}) {\n"; - $code .= " throw new \InvalidArgumentException('{%validationErrorMessage%}');\n"; - $code .= "}\n\n"; - } - - switch ($a->type()) { - case null: - return ''; - case 'int': - $validator = "\is_int(\$data['{$a->name()}'])"; - $validationErrorMessage = "Error on \"{$a->name()}\", int expected"; - break; - case 'float': - $validator = "\is_float(\$data['{$a->name()}'])"; - $validationErrorMessage = "Error on \"{$a->name()}\", float expected"; - break; - case 'bool': - $validator = "\is_bool(\$data['{$a->name()}'])"; - $validationErrorMessage = "Error on \"{$a->name()}\", bool expected"; - break; - case 'string': - $validator = "\is_string(\$data['{$a->name()}'])"; - $validationErrorMessage = "Error on \"{$a->name()}\", string expected"; - break; - case 'array': - $validator = "\is_array(\$data['{$a->name()}'])"; - $validationErrorMessage = "Error on \"{$a->name()}\", array expected"; - break; - default: - $definition = $definitions[$resolvedType] ?? null; - - if (null === $definition) { - /** @var TypeConfiguration|null $typeConfig */ - $typeConfig = $config->types()[$resolvedType] ?? null; - - if (null === $typeConfig) { - return ''; - } - - $validator = $typeConfig->validator()("data['{$a->name()}']"); - $validationErrorMessage = $typeConfig->validationErrorMessage()("\$data[\'{$a->name()}\']"); - } else { - $type = $definition->type(); - - $validator = $config->validatorFor($type)("data['{$a->name()}']"); - $validationErrorMessage = $config->validationErrorMessageFor($type)("\$data[\'{$a->name()}\']"); - } - - break; - } - - return \str_replace( - [ - '{%validator%}', - '{%validationErrorMessage%}', - ], - [ - $validator, - $validationErrorMessage, - ], - $code - ); -} - -function calculateFromArrayBodyFor(Argument $a, ?string $resolvedType, array $definitions, Configuration $config): string -{ - switch ($a->type()) { - case null: - case 'int': - case 'float': - case 'bool': - case 'string': - case 'array': - // yes all above are treated the same - if ($a->nullable()) { - return " \$data['{$a->name()}'] ?? null,\n"; - } - - return " \$data['{$a->name()}'],\n"; - default: - $definition = $definitions[$resolvedType] ?? null; - - if (null === $definition) { - /** @var TypeConfiguration|null $typeConfig */ - $typeConfig = $config->types()[$resolvedType] ?? null; - - if (null === $typeConfig) { - return " \$data['{$a->name()}'],\n"; - } - - if ($a->isList()) { - return << {$typeConfig->fromPhpValue()($a->type(), 'e')}, \$data['{$a->name()}']), - -CODE; - } - - return ' ' . $typeConfig->fromPhpValue()($a->type(), "data['{$a->name()}']") . ",\n"; - } - - $builder = $config->fromPhpValueFor($definition->type()); - - if ($a->isList()) { - $callback = "fn(\$e) => {$builder($definition->type(), '$e')}"; - - return " \array_map($callback, \$data['{$a->name()}']),\n"; - } - - if ($a->nullable()) { - return " isset(\$data['{$a->name()}']) ? " . $builder($definition->type(), "\$data['{$a->name()}']") . " : null,\n"; - } - - return ' ' . $builder($definition->type(), "\$data['{$a->name()}']") . ",\n"; - } -} - -function calculateToArrayBodyFor(Argument $a, ?string $resolvedType, array $definitions, Configuration $config): string -{ - switch ($a->type()) { - case null: - case 'int': - case 'float': - case 'bool': - case 'string': - case 'array': - // yes all above are treated the same - return " '{$a->name()}' => \$this->{$a->name()},\n"; - default: - $definition = $definitions[$resolvedType] ?? null; - - if (null === $definition) { - /** @var TypeConfiguration|null $typeConfiguration */ - $typeConfiguration = $config->types()[$resolvedType] ?? null; - - if (null === $typeConfiguration) { - return " '{$a->name()}' => \$this->{$a->name()},\n"; - } - - if ($a->isList()) { - $callback = "fn({$a->type()} \$e) => {$typeConfiguration->toPhpValue()('$e')}"; - - return <<name()}' => \array_map($callback, \$this->{$a->name()}), - -CODE; - } - - if ($a->nullable()) { - return " '{$a->name()}' => \$this->{$a->name()} !=== null ? " - . ($typeConfiguration->toPhpValue()('$this->' . $a->name())) . " : null,\n"; - } - - return " '{$a->name()}' => " . ($typeConfiguration->toPhpValue()('$this->' . $a->name())) . ",\n"; - } - - $builder = $config->toPhpValueFor($definition->type()); - - if ($a->isList()) { - $callback = "fn({$a->type()} \$e) => {$builder($definition->type(), '$e')}"; - - return " '{$a->name()}' => \array_map($callback, \$this->{$a->name()}),\n"; - } - - if ($a->nullable()) { - return " '{$a->name()}' => \$this->{$a->name()} !== null ? \$this->" - . ($builder)($definition->type(), $a->name()) . " : null,\n"; - } - - return " '{$a->name()}' => \$this->" . ($builder)($definition->type(), $a->name()) . ",\n"; - - } -} - function equalsBodyFor(Argument $a, ?string $resolvedType, array $definitions, Configuration $config): string { switch ($a->type()) { @@ -605,11 +420,9 @@ function equalsBodyFor(Argument $a, ?string $resolvedType, array $definitions, C if ($a->nullable()) { return <<{$a->name()}) { - if (null !== \$other->{$a->name()}) { - return false; - } -} elseif (null === \$other->{$a->name()} || ! $equals) { +if ((null === \$this->{$a->name()} && null !== \$other->{$a->name()}) + || ! $equals +) { return false; } diff --git a/src/Type/Enum.php b/src/Type/Enum.php index fb6a1c57..1cf9cd54 100644 --- a/src/Type/Enum.php +++ b/src/Type/Enum.php @@ -145,8 +145,8 @@ function ($c) use ($class, &$options, &$i) { ); $method = $class->addMethod('equals')->setPublic()->setReturnType(Type::BOOL); - $method->addParameter('other')->setType(Type::SELF); - $method->setBody('return $this->name === $other->name;'); + $method->addParameter('other')->setType(Type::SELF)->setNullable(); + $method->setBody('return null !== $other && $this->name === $other->name;'); $method = $class->addMethod('name')->setPublic()->setReturnType(Type::STRING); $method->setBody('return $this->name;'); diff --git a/src/Type/Event.php b/src/Type/Event.php index 39c0f16c..7440f244 100644 --- a/src/Type/Event.php +++ b/src/Type/Event.php @@ -21,6 +21,8 @@ use Fpp\Configuration; use function Fpp\constructorSeparator; use Fpp\Definition; +use function Fpp\generateFromPhpValueFor; +use function Fpp\generateToArrayBodyFor; use function Fpp\many1; use function Fpp\not; use function Fpp\parseArguments; @@ -455,8 +457,8 @@ function (Argument $a) use ( $resolvedType = resolveType($a->type(), $definition); $defaultValue = calculateDefaultValue($a); - $fromPhpValue = calculateFromPhpValueFor($a, $resolvedType, $definitions, $config); - $toPhpValue = calculateToPhpValueFor($a, $resolvedType, $definitions, $config); + $fromPhpValue = generateFromPhpValueFor($a, '$this->payload', 0, $resolvedType, $definitions, $config); + $toPhpValue = generateToArrayBodyFor($a, '$', $resolvedType, $definitions, $config); if (null !== $defaultValue) { $param = $occur->addParameter($a->name(), $defaultValue); @@ -524,113 +526,6 @@ function (Argument $a) use ( return $file; } -function calculateFromPhpValueFor(Argument $a, ?string $resolvedType, array $definitions, Configuration $config): string -{ - switch ($a->type()) { - case null: - case 'int': - case 'float': - case 'bool': - case 'string': - case 'array': - // yes all above are treated the same - if ($a->nullable()) { - return "\$this->payload['{$a->name()}'] ?? null"; - } - - return "\$this->payload['{$a->name()}']"; - default: - $definition = $definitions[$resolvedType] ?? null; - - if (null === $definition) { - /** @var TypeConfiguration|null $typeConfig */ - $typeConfig = $config->types()[$resolvedType] ?? null; - - if (null === $typeConfig) { - return "\$this->payload['{$a->name()}']"; - } - - if ($a->isList()) { - return << {$typeConfig->fromPhpValue()($a->type(), 'e')}, \$this->payload['{$a->name()}']) -CODE; - } - - return $typeConfig->fromPhpValue()($a->type(), "\$this->payload['{$a->name()}']"); - } - - $builder = $config->fromPhpValueFor($definition->type()); - - if ($a->isList()) { - $callback = "fn(\$e) => {$builder($definition->type(), '$e')}"; - - return " \array_map($callback, \$this->payload['{$a->name()}'])"; - } - - if ($a->nullable()) { - return "isset(\$this->payload['{$a->name()}']) ? " . $builder($definition->type(), "\$this->payload['{$a->name()}']") . ' : null'; - } - - return $builder($definition->type(), "\$this->payload['{$a->name()}']") . ''; - } -} - -function calculateToPhpValueFor(Argument $a, ?string $resolvedType, array $definitions, Configuration $config): string -{ - switch ($a->type()) { - case null: - case 'int': - case 'float': - case 'bool': - case 'string': - case 'array': - // yes all above are treated the same - return " '{$a->name()}' => \${$a->name()},\n"; - default: - $definition = $definitions[$resolvedType] ?? null; - - if (null === $definition) { - /** @var TypeConfiguration|null $typeConfiguration */ - $typeConfiguration = $config->types()[$resolvedType] ?? null; - - if (null === $typeConfiguration) { - return " '{$a->name()}' => \${$a->name()},\n"; - } - - if ($a->isList()) { - return <<name()}' => \array_map(fn ({$a->type()} \$e) => {$typeConfiguration->toPhpValue()('$e')}, \${$a->name()}), - -CODE; - } - - $nullableCheck = ''; - - if ($a->nullable()) { - $nullableCheck = "null === \${$a->name()} ? null : "; - } - - return " '{$a->name()}' => $nullableCheck" . ($typeConfiguration->toPhpValue()('$' . $a->name())) . ",\n"; - } - - $builder = $config->toPhpValueFor($definition->type()); - - if ($a->isList()) { - $callback = "fn({$a->type()} \$e) => {$builder($definition->type(), '$e')}"; - - return " '{$a->name()}' => \array_map($callback, \${$a->name()}),\n"; - } - - $nullableCheck = ''; - - if ($a->nullable()) { - $nullableCheck = "null === \${$a->name()} ? null : "; - } - - return " '{$a->name()}' => $nullableCheck\$" . ($builder)($definition->type(), $a->name()) . ",\n"; - } -} - function eventType(Constructor $constructor, string $namespace): string { return empty($constructor->eventType()) diff --git a/src/Type/Float_.php b/src/Type/Float_.php index 5bdfca90..4a6ca3ca 100644 --- a/src/Type/Float_.php +++ b/src/Type/Float_.php @@ -88,8 +88,8 @@ function build(Definition $definition, array $definitions, Configuration $config $method->setBody('return $this->value;'); $method = $class->addMethod('equals')->setPublic()->setReturnType(Type::BOOL); - $method->addParameter('other')->setType(Type::SELF); - $method->setBody('return $this->value === $other->value;'); + $method->addParameter('other')->setType(Type::SELF)->setNullable(); + $method->setBody('return null !== $other && $this->value === $other->value;'); return [$fqcn => $file]; } diff --git a/src/Type/Guid.php b/src/Type/Guid.php index 42274265..dfa898a3 100644 --- a/src/Type/Guid.php +++ b/src/Type/Guid.php @@ -115,8 +115,8 @@ function build(Definition $definition, array $definitions, Configuration $config $toBinary->setBody('return $this->uuid->getBytes();'); $equals = $class->addMethod('equals')->setReturnType(Type::BOOL); - $equals->addParameter('other')->setType('self'); - $equals->setBody('return $this->uuid->equals($other->uuid);'); + $equals->addParameter('other')->setType('self')->setNullable(); + $equals->setBody('return null !== $other && $this->uuid->equals($other->uuid);'); $factory = $class->addMethod('factory')->setReturnType('UuidFactory'); $factory->setPrivate()->setStatic(); diff --git a/src/Type/Int_.php b/src/Type/Int_.php index 25acb4d3..51d0b0ef 100644 --- a/src/Type/Int_.php +++ b/src/Type/Int_.php @@ -88,8 +88,8 @@ function build(Definition $definition, array $definitions, Configuration $config $method->setBody('return $this->value;'); $method = $class->addMethod('equals')->setPublic()->setReturnType(Type::BOOL); - $method->addParameter('other')->setType(Type::SELF); - $method->setBody('return $this->value === $other->value;'); + $method->addParameter('other')->setType(Type::SELF)->setNullable(); + $method->setBody('return null !== $other && $this->value === $other->value;'); return [$fqcn => $file]; } diff --git a/src/Type/String_.php b/src/Type/String_.php index 65eb2f9f..f0f383f8 100644 --- a/src/Type/String_.php +++ b/src/Type/String_.php @@ -88,8 +88,8 @@ function build(Definition $definition, array $definitions, Configuration $config $method->setBody('return $this->value;'); $method = $class->addMethod('equals')->setPublic()->setReturnType(Type::BOOL); - $method->addParameter('other')->setType(Type::SELF); - $method->setBody('return $this->value === $other->value;'); + $method->addParameter('other')->setType(Type::SELF)->setNullable(); + $method->setBody('return null !== $other && $this->value === $other->value;'); return [$fqcn => $file]; } diff --git a/src/Type/Uuid.php b/src/Type/Uuid.php index 335a02d8..e29bc305 100644 --- a/src/Type/Uuid.php +++ b/src/Type/Uuid.php @@ -113,8 +113,8 @@ function build(Definition $definition, array $definitions, Configuration $config $toBinary->setBody('return $this->uuid->getBytes();'); $equals = $class->addMethod('equals')->setReturnType(Type::BOOL); - $equals->addParameter('other')->setType('self'); - $equals->setBody('return $this->uuid->equals($other->uuid);'); + $equals->addParameter('other')->setType('self')->setNullable(); + $equals->setBody('return null !== $other && $this->uuid->equals($other->uuid);'); return [$fqcn => $file]; }