From 3c01fed6b8428d7f2143a28934e3703a91b46173 Mon Sep 17 00:00:00 2001 From: SignpostMarv Date: Sun, 4 Aug 2019 18:44:33 +0100 Subject: [PATCH] amending type hinting, fleshing out examples --- README.md | 119 +++++++++++++++++++++++- Tests/DaftTypedObjectTest.php | 40 +++++--- Tests/Fixtures/Immutable.php | 4 +- Tests/Fixtures/Mutable.php | 4 +- Tests/Fixtures/MutableWithNullables.php | 88 +++++++++++++++++- psalm.baseline.xml | 3 +- src/DaftTypedObject.php | 18 +++- src/Immutable.php | 3 +- 8 files changed, 254 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 6997b00..41807e3 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,12 @@ Typed Object, a simplified version of [signpostmarv/daft-object](https://github. ```php use SignpostMarv\DaftTypedObject\Immutable as Base; +use SignpostMarv\DaftTypedObject\Immutable as Base; + /** -* @template-extends Base +* @psalm-type DATA = array{id:int, name:string} +* +* @template-extends Base * * @property-read int $id * @property-read string $name @@ -39,7 +43,9 @@ class Immutable extends Base use SignpostMarv\DaftTypedObject\DaftTypedObject as Base; /** -* @template-extends Base +* @psalm-type DATA = array{id:int, name:string} +* +* @template-extends Base * * @property int $id * @property string $name @@ -58,4 +64,113 @@ class Mutable extends Base */ protected $name; } + +``` + +### Mutable with `DateTimeImmutable` & nullables +```php +use DateTimeImmutable; +use SignpostMarv\DaftTypedObject\DaftTypedObject as Base; + +/** +* @template T as array{id:int, name:string, date:DateTimeImmutable|null} +* @template S as array{id:int, name:string, date:string|null} +* +* @template-extends Base +* +* @property-read int $id +* @property-read string $name +* @property-read DateTimeImmutable $date +*/ +class MutableWithNullables extends Base +{ + const TYPED_PROPERTIES = ['id', 'name', 'date']; + + const TYPED_NULLABLE_PROPERTIES = ['date']; + + /** + * @var int + */ + protected $id; + + /** + * @var string|null + */ + protected $name; + + /** + * @var DateTimeImmutable|null + */ + protected $date; + + /** + * @template K as key-of + * + * @param K $property + * @param T[K] $value + * + * @return S[K] + */ + public static function PropertyValueToScalarOrNull( + string $property, + $value + ) { + /** + * @var T[K]|DateTimeImmutable + */ + $value = $value; + + if ($value instanceof DateTimeImmutable) { + /** + * @var S[K] + */ + return (string) $value->format('Y-m-d'); + } + + /** + * @var S[K] + */ + return parent::PropertyValueToScalarOrNull((string) $property, $value); + } + + /** + * @template K as key-of + * + * @param K|'date' $property + * @param S[K] $value + * + * @return T[K] + */ + public static function PropertyScalarOrNullToValue( + string $property, + $value + ) { + /** + * @var S[K]|string + */ + $value = $value; + + if ('date' === $property && is_string($value)) { + $out = new DateTimeImmutable($value); + } else { + /** + * @var S[K] + */ + $value = $value; + + /** + * @var T[K] + */ + $out = parent::PropertyScalarOrNullToValue( + (string) $property, + $value + ); + } + + /** + * @var T[K] + */ + return $out; + } +} ``` diff --git a/Tests/DaftTypedObjectTest.php b/Tests/DaftTypedObjectTest.php index 6d73450..4c95be5 100644 --- a/Tests/DaftTypedObjectTest.php +++ b/Tests/DaftTypedObjectTest.php @@ -52,12 +52,16 @@ public function dataProviderPackedImplementations() : array ], Fixtures\MutableWithNullables::class => [ [ - ['id' => 1, 'name' => 'foo'], - ['id' => 1, 'name' => 'foo'], + [ + 'id' => 1, + 'name' => 'foo', + 'date' => new DateTimeImmutable('1970-01-01'), + ], + ['id' => 1, 'name' => 'foo', 'date' => '1970-01-01'], ], [ - ['id' => 1, 'name' => null], - ['id' => 1, 'name' => null], + ['id' => 1, 'name' => 'foo', 'date' => null], + ['id' => 1, 'name' => 'foo', 'date' => null], ], ], ]; @@ -82,9 +86,13 @@ public function dataProviderImplementations() : Generator /** * @dataProvider dataProviderImplementations * + * @template T as array + * @template K as key-of + * @template S as array + * * @param class-string $type - * @param array $args - * @param array $expected + * @param T $args + * @param S $expected */ public function testJsonSerialize( string $type, @@ -92,30 +100,36 @@ public function testJsonSerialize( array $expected ) : void { /** - * @var array + * @var S */ $jsonified = (new $type($args))->jsonSerialize(); static::assertSame($expected, $jsonified); + /** + * @var array + */ + $properties = array_keys($jsonified); + static::assertSame( $expected, (new $type(array_combine(array_keys($jsonified), array_map( /** - * @param scalar|null $value + * @param K $property + * @param S[K] $value * - * @return scalar|array|object|null + * @return T[K] */ function (string $property, $value) use ($type) { /** - * @var scalar|array|object|null + * @var T[K] */ return $type::PropertyScalarOrNullToValue( - $property, + (string) $property, $value ); }, - array_keys($jsonified), + $properties, $jsonified ))))->jsonSerialize() ); @@ -341,6 +355,8 @@ public function testMutableSetSucceeds( ++$value; } elseif (is_string($value)) { $value = strrev($value); + } elseif ($value instanceof DateTimeImmutable) { + $value = $value->modify('+1 second'); } $object->$property = $value; diff --git a/Tests/Fixtures/Immutable.php b/Tests/Fixtures/Immutable.php index 843f237..7df4f4c 100644 --- a/Tests/Fixtures/Immutable.php +++ b/Tests/Fixtures/Immutable.php @@ -9,7 +9,9 @@ use SignpostMarv\DaftTypedObject\Immutable as Base; /** -* @template-extends Base +* @psalm-type DATA = array{id:int, name:string} +* +* @template-extends Base * * @property-read int $id * @property-read string $name diff --git a/Tests/Fixtures/Mutable.php b/Tests/Fixtures/Mutable.php index b409a8a..596cda3 100644 --- a/Tests/Fixtures/Mutable.php +++ b/Tests/Fixtures/Mutable.php @@ -9,7 +9,9 @@ use SignpostMarv\DaftTypedObject\DaftTypedObject as Base; /** -* @template-extends Base +* @psalm-type DATA = array{id:int, name:string} +* +* @template-extends Base * * @property int $id * @property string $name diff --git a/Tests/Fixtures/MutableWithNullables.php b/Tests/Fixtures/MutableWithNullables.php index 4ad47b6..0c93a8d 100644 --- a/Tests/Fixtures/MutableWithNullables.php +++ b/Tests/Fixtures/MutableWithNullables.php @@ -6,19 +6,24 @@ namespace SignpostMarv\DaftTypedObject\Fixtures; +use DateTimeImmutable; use SignpostMarv\DaftTypedObject\DaftTypedObject as Base; /** -* @template-extends Base +* @template T as array{id:int, name:string, date:DateTimeImmutable|null} +* @template S as array{id:int, name:string, date:string|null} +* +* @template-extends Base * * @property-read int $id -* @property-read string|null $name +* @property-read string $name +* @property-read DateTimeImmutable $date */ class MutableWithNullables extends Base { - const TYPED_PROPERTIES = ['id', 'name']; + const TYPED_PROPERTIES = ['id', 'name', 'date']; - const TYPED_NULLABLE_PROPERTIES = ['name']; + const TYPED_NULLABLE_PROPERTIES = ['date']; /** * @var int @@ -29,4 +34,79 @@ class MutableWithNullables extends Base * @var string|null */ protected $name; + + /** + * @var DateTimeImmutable|null + */ + protected $date; + + /** + * @template K as key-of + * + * @param K $property + * @param T[K] $value + * + * @return S[K] + */ + public static function PropertyValueToScalarOrNull( + string $property, + $value + ) { + /** + * @var T[K]|DateTimeImmutable + */ + $value = $value; + + if ($value instanceof DateTimeImmutable) { + /** + * @var S[K] + */ + return (string) $value->format('Y-m-d'); + } + + /** + * @var S[K] + */ + return parent::PropertyValueToScalarOrNull((string) $property, $value); + } + + /** + * @template K as key-of + * + * @param K|'date' $property + * @param S[K] $value + * + * @return T[K] + */ + public static function PropertyScalarOrNullToValue( + string $property, + $value + ) { + /** + * @var S[K]|string + */ + $value = $value; + + if ('date' === $property && is_string($value)) { + $out = new DateTimeImmutable($value); + } else { + /** + * @var S[K] + */ + $value = $value; + + /** + * @var T[K] + */ + $out = parent::PropertyScalarOrNullToValue( + (string) $property, + $value + ); + } + + /** + * @var T[K] + */ + return $out; + } } diff --git a/psalm.baseline.xml b/psalm.baseline.xml index 6fea812..b94647b 100644 --- a/psalm.baseline.xml +++ b/psalm.baseline.xml @@ -1,8 +1,9 @@ - + new DateTimeImmutable() + testJsonSerialize diff --git a/src/DaftTypedObject.php b/src/DaftTypedObject.php index 907e55e..059cdbd 100644 --- a/src/DaftTypedObject.php +++ b/src/DaftTypedObject.php @@ -11,6 +11,7 @@ /** * @template T as array +* @template S as array */ abstract class DaftTypedObject implements JsonSerializable { @@ -99,6 +100,8 @@ public function __unset(string $property) : void /** * @template K as key-of + * + * @return S */ public function jsonSerialize() : array { @@ -107,6 +110,9 @@ public function jsonSerialize() : array */ $properties = static::TYPED_PROPERTIES; + /** + * @var S + */ return array_combine($properties, array_map( [$this, 'PropertyMapperToScalarOrNull'], $properties @@ -119,7 +125,7 @@ public function jsonSerialize() : array * @param K $_property * @param T[K] $value * - * @return scalar|null + * @return S[K] */ public static function PropertyValueToScalarOrNull( string $_property, @@ -141,6 +147,9 @@ public static function PropertyValueToScalarOrNull( )); } + /** + * @var S[K] + */ return $value; } @@ -148,7 +157,7 @@ public static function PropertyValueToScalarOrNull( * @template K as key-of * * @param K $_property - * @param scalar|null $value + * @param S[K] $value * * @return T[K] */ @@ -167,7 +176,7 @@ public static function PropertyScalarOrNullToValue( * * @param K $property * - * @return scalar|null + * @return S[K] */ protected function PropertyMapperToScalarOrNull(string $property) { @@ -176,6 +185,9 @@ protected function PropertyMapperToScalarOrNull(string $property) */ $value = $this->$property; + /** + * @var S[K] + */ return static::PropertyValueToScalarOrNull( (string) $property, $value diff --git a/src/Immutable.php b/src/Immutable.php index 0209d10..6172829 100644 --- a/src/Immutable.php +++ b/src/Immutable.php @@ -10,8 +10,9 @@ /** * @template T as array +* @template S as array * -* @template-extends DaftTypedObject +* @template-extends DaftTypedObject */ abstract class Immutable extends DaftTypedObject {