Skip to content
Merged
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
8 changes: 7 additions & 1 deletion src/DataMapper/ConstructorDataMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
$required = $fallback !== null; // Null fallback means that the parameter is nullable, so it's not required
$requiredMessage = $baseRequired->message;

if ($required && $parameter->isPromoted()) {

Check warning on line 166 in src/DataMapper/ConstructorDataMapper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "LogicalAnd": --- Original +++ New @@ @@ $required = $fallback !== null; // Null fallback means that the parameter is nullable, so it's not required $requiredMessage = $baseRequired->message; - if ($required && $parameter->isPromoted()) { + if ($required || $parameter->isPromoted()) { // Get the actual required error message foreach ((new ReflectionProperty($this->className, $parameter->name))->getAttributes(Required::class) as $attribute) { $requiredMessage = $attribute->newInstance()->message;
// Get the actual required error message
foreach ((new ReflectionProperty($this->className, $parameter->name))->getAttributes(Required::class) as $attribute) {
$requiredMessage = $attribute->newInstance()->message;
Expand All @@ -184,24 +184,30 @@

private function resolveFallbackValueFromType(?ReflectionType $type): mixed
{
if (!$type || $type->allowsNull()) {

Check warning on line 187 in src/DataMapper/ConstructorDataMapper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "LogicalOr": --- Original +++ New @@ @@ } private function resolveFallbackValueFromType(?ReflectionType $type) : mixed { - if (!$type || $type->allowsNull()) { + if (!$type && $type->allowsNull()) { return null; } if (!$type instanceof ReflectionNamedType) {
return null;
}

if (!$type instanceof ReflectionNamedType) {

Check warning on line 191 in src/DataMapper/ConstructorDataMapper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "InstanceOf_": --- Original +++ New @@ @@ if (!$type || $type->allowsNull()) { return null; } - if (!$type instanceof ReflectionNamedType) { + if (!true) { throw new LogicException(sprintf('Cannot use complex type with %s on %s', self::class, $this->className)); } if (!$type->isBuiltin()) {
throw new LogicException(sprintf('Cannot use complex type with %s on %s', self::class, $this->className));
}

if (!$type->isBuiltin()) {
// @phpstan-ignore-next-line
return (new ReflectionClass($type->getName()))->newInstanceWithoutConstructor();
$class = new ReflectionClass($type->getName());

if ($class->isEnum()) {
return $type->getName()::cases()[0];
}

return $class->newInstanceWithoutConstructor();
}

return match ($type->getName()) {
'int' => 0,
'float' => 0.0,

Check warning on line 208 in src/DataMapper/ConstructorDataMapper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "OneZeroFloat": --- Original +++ New @@ @@ } return match ($type->getName()) { 'int' => 0, - 'float' => 0.0, + 'float' => 1.0, 'string' => '', 'bool' => false, 'array' => [],
'string' => '',
'bool' => false,

Check warning on line 210 in src/DataMapper/ConstructorDataMapper.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "FalseValue": --- Original +++ New @@ @@ 'int' => 0, 'float' => 0.0, 'string' => '', - 'bool' => false, + 'bool' => true, 'array' => [], 'object' => new stdClass(), default => throw new LogicException(sprintf('Cannot resolve fallback value for type %s on %s', $type->getName(), $this->className)),
'array' => [],
'object' => new stdClass(),
default => throw new LogicException(sprintf('Cannot resolve fallback value for type %s on %s', $type->getName(), $this->className)),
Expand Down
7 changes: 6 additions & 1 deletion src/DataMapper/ConstructorParameterMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Quatrevieux\Form\Util\Expr;
use ReflectionClass;
use stdClass;
use UnitEnum;

use function is_object;

Expand All @@ -20,8 +21,12 @@

public function compiledFallback(): string
{
if (is_object($this->fallback) && $this->fallback::class !== stdClass::class) {
if (
is_object($this->fallback)
&& $this->fallback::class !== stdClass::class
&& !$this->fallback instanceof UnitEnum
) {
return (string) Expr::new(ReflectionClass::class, [$this->fallback::class])->newInstanceWithoutConstructor();

Check warning on line 29 in src/DataMapper/ConstructorParameterMetadata.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "CastString": --- Original +++ New @@ @@ public function compiledFallback() : string { if (is_object($this->fallback) && $this->fallback::class !== stdClass::class && !$this->fallback instanceof UnitEnum) { - return (string) Expr::new(ReflectionClass::class, [$this->fallback::class])->newInstanceWithoutConstructor(); + return Expr::new(ReflectionClass::class, [$this->fallback::class])->newInstanceWithoutConstructor(); } return Code::value($this->fallback); } }
}

return Code::value($this->fallback);
Expand Down
44 changes: 43 additions & 1 deletion tests/DataMapper/ConstructorDataMapperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use PHPUnit\Framework\TestCase;
use Quatrevieux\Form\DataMapper\Generator\DataMapperGenerator;
use Quatrevieux\Form\DefaultRegistry;
use Quatrevieux\Form\Fixtures\ConstructorWithSimpleEnum;
use Quatrevieux\Form\Fixtures\EmbeddedFormConstructor;
use Quatrevieux\Form\Fixtures\MySimpleEnum;
use Quatrevieux\Form\Fixtures\RequestWithDefaultValueConstructor;
use Quatrevieux\Form\Fixtures\RequiredParametersRequestConstructor;
use Quatrevieux\Form\Fixtures\SimpleRequestConstructor;
Expand Down Expand Up @@ -148,7 +150,6 @@ public function test_with_embedded()
$this->assertEquals('This value is required', (string) $dto->errors['bar']);
$this->assertEquals('This value is required', (string) $dto->errors['embedded']);


$dto = $mapper->toDataObject([
'foo' => 'azerty',
'bar' => 'uiop',
Expand Down Expand Up @@ -185,4 +186,45 @@ public function test_with_embedded()
$mapper->generateToDataObject($mapper)
);
}

public function test_with_enum()
{
$mapper = new ConstructorDataMapper(ConstructorWithSimpleEnum::class, new DefaultRegistry());

$dto = $mapper->toDataObject([]);

$this->assertSame(ConstructorWithSimpleEnum::class, $mapper->className());
$this->assertInstanceOf(ConstructorWithSimpleEnum::class, $dto->dto);

$this->assertSame(MySimpleEnum::Foo, $dto->dto->enum);

$this->assertCount(1, $dto->errors);
$this->assertEquals('This value is required', (string) $dto->errors['enum']);

$dto = $mapper->toDataObject([
'enum' => MySimpleEnum::Bar,
]);

$this->assertEquals(new ConstructorWithSimpleEnum(
enum: MySimpleEnum::Bar
), $dto->dto);

$this->assertEmpty($dto->errors);

$this->assertSame(
<<<'PHP'
$errors = [];
$dto = new \Quatrevieux\Form\Fixtures\ConstructorWithSimpleEnum(enum: $fields['enum'] ?? \Quatrevieux\Form\Fixtures\MySimpleEnum::Foo);

foreach (['enum' => 'This value is required'] as $field => $message) {
if (!isset($fields[$field])) {
$errors[$field] = new \Quatrevieux\Form\Validator\FieldError($message, [], 'b1ac3a70-06db-5cd6-8f0e-8e6b98b3fcb5', $this->registry->getTranslator());
}
}

return new \Quatrevieux\Form\DataMapper\DataMapperResult($dto, $errors);
PHP,
$mapper->generateToDataObject($mapper)
);
}
}
16 changes: 16 additions & 0 deletions tests/Fixtures/ConstructorWithSimpleEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Quatrevieux\Form\Fixtures;

class ConstructorWithSimpleEnum
{
public function __construct(
public readonly MySimpleEnum $enum,
) {}
}

enum MySimpleEnum
{
case Foo;
case Bar;
}
Loading