diff --git a/src/Parser.php b/src/Parser.php index 839db6b..0a0c963 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -101,9 +101,9 @@ public function literal(bool|float|int|string $literal): LiteralSchema * @param array $fieldNameToSchema * @param class-string $classname */ - public function object(array $fieldNameToSchema, string $classname = \stdClass::class): ObjectSchema + public function object(array $fieldNameToSchema, string $classname = \stdClass::class, bool $construct = false): ObjectSchema { - return new ObjectSchema($fieldNameToSchema, $classname); + return new ObjectSchema($fieldNameToSchema, $classname, $construct); } public function record(SchemaInterface $fieldSchema): RecordSchema diff --git a/src/Schema/ObjectSchema.php b/src/Schema/ObjectSchema.php index 90fa4f0..c323ec0 100644 --- a/src/Schema/ObjectSchema.php +++ b/src/Schema/ObjectSchema.php @@ -16,7 +16,7 @@ final class ObjectSchema extends AbstractObjectSchema implements ObjectSchemaInt * @param array $fieldToSchema * @param class-string $classname */ - public function __construct(array $fieldToSchema, private string $classname = \stdClass::class) + public function __construct(array $fieldToSchema, private string $classname = \stdClass::class, private bool $construct = false) { parent::__construct($fieldToSchema); } @@ -26,20 +26,29 @@ public function __construct(array $fieldToSchema, private string $classname = \s */ protected function parseFields(array $input, Errors $childrenErrors): object { - $object = new ($this->classname); - + $fields = []; foreach ($this->getFieldToSchema() as $fieldName => $fieldSchema) { try { if ($this->skip($input, $fieldName)) { continue; } - $object->{$fieldName} = $fieldSchema->parse($input[$fieldName] ?? null); + $fields[$fieldName] = $fieldSchema->parse($input[$fieldName] ?? null); } catch (ErrorsException $e) { $childrenErrors->add($e->errors, $fieldName); } } - return $object; + if (!$this->construct) { + $object = new ($this->classname); + + foreach ($fields as $fieldName => $fieldValue) { + $object->{$fieldName} = $fieldValue; + } + + return $object; + } + + return new ($this->classname)(...$fields); } } diff --git a/tests/Unit/ParserTest.php b/tests/Unit/ParserTest.php index 352896b..1bcec53 100644 --- a/tests/Unit/ParserTest.php +++ b/tests/Unit/ParserTest.php @@ -33,6 +33,30 @@ enum BackedSuit: string case Spades = 'S'; } +final class ObjectDemo implements \JsonSerializable +{ + public string $field1; + + public function jsonSerialize(): array + { + return [ + 'field1' => $this->field1, + ]; + } +} + +final class ObjectConstructDemo implements \JsonSerializable +{ + public function __construct(public string $field1) {} + + public function jsonSerialize(): array + { + return [ + 'field1' => $this->field1, + ]; + } +} + /** * @covers \Chubbyphp\Parsing\Parser * @@ -159,7 +183,7 @@ public function testLiteral(): void self::assertInstanceOf(LiteralSchema::class, $literalSchema); } - public function testObject(): void + public function testObjectStdClass(): void { $p = new Parser(); @@ -170,6 +194,44 @@ public function testObject(): void self::assertInstanceOf(ObjectSchema::class, $objectSchema); } + public function testObjectWithObject(): void + { + $p = new Parser(); + + $objectSchema = $p->object([ + 'field' => $p->string(), + ], ObjectDemo::class, ); + + $classnameReflection = new \ReflectionProperty(ObjectSchema::class, 'classname'); + + self::assertSame(ObjectDemo::class, $classnameReflection->getValue($objectSchema)); + + $constructReflection = new \ReflectionProperty(ObjectSchema::class, 'construct'); + + self::assertFalse($constructReflection->getValue($objectSchema)); + + self::assertInstanceOf(ObjectSchema::class, $objectSchema); + } + + public function testObjectWithObjectConstruct(): void + { + $p = new Parser(); + + $objectSchema = $p->object([ + 'field' => $p->string(), + ], ObjectConstructDemo::class, true); + + $classnameReflection = new \ReflectionProperty(ObjectSchema::class, 'classname'); + + self::assertSame(ObjectConstructDemo::class, $classnameReflection->getValue($objectSchema)); + + $constructReflection = new \ReflectionProperty(ObjectSchema::class, 'construct'); + + self::assertTrue($constructReflection->getValue($objectSchema)); + + self::assertInstanceOf(ObjectSchema::class, $objectSchema); + } + public function testRecord(): void { $p = new Parser(); diff --git a/tests/Unit/Schema/ObjectSchemaTest.php b/tests/Unit/Schema/ObjectSchemaTest.php index f0ccfd2..4f18ea2 100644 --- a/tests/Unit/Schema/ObjectSchemaTest.php +++ b/tests/Unit/Schema/ObjectSchemaTest.php @@ -16,12 +16,28 @@ final class ObjectDemo implements \JsonSerializable { public string $field1; public int $field2; + public ?float $field3; public function jsonSerialize(): array { return [ 'field1' => $this->field1, 'field2' => $this->field2, + 'field3' => $this->field3, + ]; + } +} + +final class ObjectConstructDemo implements \JsonSerializable +{ + public function __construct(public string $field1, public int $field2, public ?float $field3) {} + + public function jsonSerialize(): array + { + return [ + 'field1' => $this->field1, + 'field2' => $this->field2, + 'field3' => $this->field3, ]; } } @@ -35,7 +51,11 @@ final class ObjectSchemaTest extends TestCase { public function testImmutability(): void { - $schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]); + $schema = new ObjectSchema([ + 'field1' => new StringSchema(), + 'field2' => new IntSchema(), + 'field3' => new FloatSchema(), + ]); self::assertNotSame($schema, $schema->nullable()); self::assertNotSame($schema, $schema->nullable(false)); @@ -78,11 +98,15 @@ public function testConstructWithoutFieldSchema(): void public function testParseSuccess(): void { - $input = ['field1' => 'test', 'field2' => 1]; + $input = ['field1' => 'test', 'field2' => 1, 'field3' => 3.14159]; - $schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]); + $schema = new ObjectSchema([ + 'field1' => new StringSchema(), + 'field2' => new IntSchema(), + 'field3' => new FloatSchema(), + ]); - $output = $schema->parse([...$input, 'field3' => 1.5]); + $output = $schema->parse($input); self::assertInstanceOf(\stdClass::class, $output); @@ -91,9 +115,13 @@ public function testParseSuccess(): void public function testParseSuccessWithClass(): void { - $input = ['field1' => 'test', 'field2' => 1]; + $input = ['field1' => 'test', 'field2' => 1, 'field3' => 3.14159]; - $schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()], ObjectDemo::class); + $schema = new ObjectSchema([ + 'field1' => new StringSchema(), + 'field2' => new IntSchema(), + 'field3' => new FloatSchema(), + ], ObjectDemo::class); $output = $schema->parse($input); @@ -102,13 +130,35 @@ public function testParseSuccessWithClass(): void self::assertSame($input, (array) $output); } + public function testParseSuccessWithConstructClass(): void + { + $input = ['field1' => 'test', 'field2' => 1, 'field3' => 3.14159]; + + $schema = new ObjectSchema([ + 'field1' => new StringSchema(), + 'field2' => new IntSchema(), + 'field3' => new FloatSchema(), + ], ObjectConstructDemo::class, true); + + $output = $schema->parse($input); + + self::assertInstanceOf(ObjectConstructDemo::class, $output); + + self::assertSame($input, (array) $output); + } + public function testParseSuccessWithStdClassInput(): void { $input = new \stdClass(); $input->field1 = 'test'; $input->field2 = 1; + $input->field3 = 3.14159; - $schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]); + $schema = new ObjectSchema([ + 'field1' => new StringSchema(), + 'field2' => new IntSchema(), + 'field3' => new FloatSchema(), + ]); $output = $schema->parse($input); @@ -119,9 +169,13 @@ public function testParseSuccessWithStdClassInput(): void public function testParseSuccessWithIteratorInput(): void { - $input = new \ArrayIterator(['field1' => 'test', 'field2' => 1]); + $input = new \ArrayIterator(['field1' => 'test', 'field2' => 1, 'field3' => 3.14159]); - $schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]); + $schema = new ObjectSchema([ + 'field1' => new StringSchema(), + 'field2' => new IntSchema(), + 'field3' => new FloatSchema(), + ]); $output = $schema->parse($input); @@ -135,8 +189,13 @@ public function testParseSuccessWithJsonSerialzableObject(): void $input = new ObjectDemo(); $input->field1 = 'test'; $input->field2 = 1; + $input->field3 = 3.14159; - $schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]); + $schema = new ObjectSchema([ + 'field1' => new StringSchema(), + 'field2' => new IntSchema(), + 'field3' => new FloatSchema(), + ]); $output = $schema->parse($input);