From c0d78e829397f80c5fbf289c1d80289c28c7b5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Fri, 10 Jan 2020 20:02:53 +0100 Subject: [PATCH 01/16] Add new CastsKeywords trait --- .gitattributes | 1 + src/Values/CastsKeywordTest.php | 29 ++++++++++++++ src/Values/CastsKeywordTestCase.php | 59 +++++++++++++++++++++++++++++ src/Values/CastsKeywords.php | 30 +++++++++++++++ src/Values/FakeKeyword.php | 36 ++++++++++++++++++ src/Values/Keyword.php | 11 ++++++ 6 files changed, 166 insertions(+) create mode 100644 src/Values/CastsKeywordTest.php create mode 100644 src/Values/CastsKeywordTestCase.php create mode 100644 src/Values/CastsKeywords.php create mode 100644 src/Values/FakeKeyword.php create mode 100644 src/Values/Keyword.php diff --git a/.gitattributes b/.gitattributes index 7380ba8..009d49f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ * text=auto **/*Test.php export-ignore +**/*TestCase.php export-ignore **/Fake*.php export-ignore /.gitattributes export-ignore /.gitignore export-ignore diff --git a/src/Values/CastsKeywordTest.php b/src/Values/CastsKeywordTest.php new file mode 100644 index 0000000..98db566 --- /dev/null +++ b/src/Values/CastsKeywordTest.php @@ -0,0 +1,29 @@ +keyword(); + $castsKeyword = $this->castsKeyword(); + + $instance = $castsKeyword::fromKeyword($keyword); + + $this->assertInstanceOf(get_class($castsKeyword), $instance); + } + + public function testItCanReturnTheKeyword(): void + { + $keyword = $this->keyword(); + $castsKeyword = $this->castsKeyword(); + + $instance = $castsKeyword::fromKeyword($keyword); + + $this->assertSame($keyword, $instance->keyword()); + } + + public function testItReturnsNullWhenNotConstructedFromAKeyword(): void + { + $castsKeyword = $this->castsKeyword(); + + $this->assertNull($castsKeyword->keyword()); + } + + public function testItReturnsTheKeywordWhenCastingToAString(): void + { + $keyword = $this->keyword(); + $castsKeyword = $this->castsKeyword(); + + $instance = $castsKeyword::fromKeyword($keyword); + + $this->assertSame((string) $keyword, (string) $instance); + } + + public function testItReturnsTheStringValueWhenCastingToAStringWhenNotConstructedFromAKeyword(): void + { + $castsKeyword = $this->castsKeyword(); + + $this->assertSame((string) $castsKeyword::ORIGINAL_VALUE, (string) $castsKeyword); + } +} diff --git a/src/Values/CastsKeywords.php b/src/Values/CastsKeywords.php new file mode 100644 index 0000000..d5227d4 --- /dev/null +++ b/src/Values/CastsKeywords.php @@ -0,0 +1,30 @@ +keyword) { + return (string) $this->keyword; + } + + return $this->value; + } + + public static function fromKeyword(Keyword $keyword): self + { + $instance = new static($keyword->castedValue()); + $instance->keyword = $keyword; + + return $instance; + } + + public function keyword(): ?Keyword + { + return $this->keyword; + } +} diff --git a/src/Values/FakeKeyword.php b/src/Values/FakeKeyword.php new file mode 100644 index 0000000..f27dbc4 --- /dev/null +++ b/src/Values/FakeKeyword.php @@ -0,0 +1,36 @@ +castedValue = $castedValue; + $this->keyword = $keyword; + } + + public static function casted($value, string $keyword = 'keyword'): self + { + return new self($keyword, $value); + } + + public function __toString(): string + { + return $this->keyword; + } + + public function castedValue() + { + return $this->castedValue; + } +} diff --git a/src/Values/Keyword.php b/src/Values/Keyword.php new file mode 100644 index 0000000..ccd8f12 --- /dev/null +++ b/src/Values/Keyword.php @@ -0,0 +1,11 @@ + Date: Fri, 10 Jan 2020 22:09:07 +0100 Subject: [PATCH 02/16] Add new CastsToBoolean trait and deprecate BooleanCriterion --- src/Exceptions/InvalidKeyword.php | 22 +++++++++++ src/Types/BooleanCriterion.php | 63 +++-------------------------- src/Values/BooleanKeyword.php | 53 +++++++++++++++++++++++++ src/Values/BooleanKeywordTest.php | 47 ++++++++++++++++++++++ src/Values/CastsToBoolean.php | 44 +++++++++++++++++++++ src/Values/CastsToBooleanTest.php | 66 +++++++++++++++++++++++++++++++ 6 files changed, 238 insertions(+), 57 deletions(-) create mode 100644 src/Exceptions/InvalidKeyword.php create mode 100644 src/Values/BooleanKeyword.php create mode 100644 src/Values/BooleanKeywordTest.php create mode 100644 src/Values/CastsToBoolean.php create mode 100644 src/Values/CastsToBooleanTest.php diff --git a/src/Exceptions/InvalidKeyword.php b/src/Exceptions/InvalidKeyword.php new file mode 100644 index 0000000..3b338b4 --- /dev/null +++ b/src/Exceptions/InvalidKeyword.php @@ -0,0 +1,22 @@ +value = $value; - } - - /** - * @return static - */ - public static function fromString(string $value): self - { - $value = (new Keyword(static::class, $value))->value(); - - if (! is_bool($value)) { - throw InvalidCriterionValue::expectedBoolean(static::class); - } - - return new static($value); - } - - public function value(): bool - { - return $this->value; - } - - public function isTruthy(): bool - { - return $this->value; - } - - public function isFalsy(): bool - { - return ! $this->isTruthy(); - } - - public static function keywords(): array - { - return [ - static::KEYWORD_TRUE => true, - static::KEYWORD_FALSE => false, - ]; - } - - public function __toString(): string - { - return (new Value($this, $this->value))->keyword(); - } + use CastsToBoolean; } diff --git a/src/Values/BooleanKeyword.php b/src/Values/BooleanKeyword.php new file mode 100644 index 0000000..0a5de7b --- /dev/null +++ b/src/Values/BooleanKeyword.php @@ -0,0 +1,53 @@ + true, + '1' => true, + 'false' => false, + '0' => false, + ]; + + private string $stringValue; + private bool $castedValue; + + public function __construct(string $keyword) + { + $this->guardAgainstInvalidValues($keyword); + + $this->stringValue = $keyword; + $this->castedValue = self::CASTED_VALUES[$keyword]; + } + + private function guardAgainstInvalidValues(string $keyword): void + { + if ($this->isNotAKeyword($keyword)) { + throw InvalidKeyword::cannotBeCastedToBoolean($keyword); + } + } + + public function __toString(): string + { + return $this->stringValue; + } + + public function castedValue(): bool + { + return $this->castedValue; + } + + private function isNotAKeyword(string $keyword): bool + { + return ! array_key_exists($keyword, self::CASTED_VALUES); + } +} diff --git a/src/Values/BooleanKeywordTest.php b/src/Values/BooleanKeywordTest.php new file mode 100644 index 0000000..f0f9ff2 --- /dev/null +++ b/src/Values/BooleanKeywordTest.php @@ -0,0 +1,47 @@ +expectException(InvalidKeyword::class); + + new BooleanKeyword('rubbish'); + } + + public function testItCanCastValidTruthyKeywordsToABoolean(): void + { + $truthyKeyword = new BooleanKeyword('true'); + $numericTruthyKeyword = new BooleanKeyword('1'); + + $this->assertEquals('true', $truthyKeyword->__toString()); + $this->assertTrue($truthyKeyword->castedValue()); + $this->assertEquals('1', $numericTruthyKeyword->__toString()); + $this->assertTrue($numericTruthyKeyword->castedValue()); + } + + public function testItCanCastValidFalsyKeywordsToABoolean(): void + { + $falsyKeyword = new BooleanKeyword('false'); + $numericFalsyKeyword = new BooleanKeyword('0'); + + $this->assertEquals('false', $falsyKeyword->__toString()); + $this->assertFalse($falsyKeyword->castedValue()); + $this->assertEquals('0', $numericFalsyKeyword->__toString()); + $this->assertFalse($numericFalsyKeyword->castedValue()); + } + + public function testItImplementsTheKeywordInterface(): void + { + $keyword = new BooleanKeyword('true'); + + $this->assertInstanceOf(Keyword::class, $keyword); + } +} diff --git a/src/Values/CastsToBoolean.php b/src/Values/CastsToBoolean.php new file mode 100644 index 0000000..3fe89b1 --- /dev/null +++ b/src/Values/CastsToBoolean.php @@ -0,0 +1,44 @@ +value = $value; + } + + /** + * @return static + */ + public static function fromString(string $value): self + { + return self::fromKeyword(new BooleanKeyword($value)); + } + + public function value(): bool + { + return $this->value; + } + + public function isTruthy(): bool + { + return $this->value; + } + + public function isFalsy(): bool + { + return ! $this->value; + } +} diff --git a/src/Values/CastsToBooleanTest.php b/src/Values/CastsToBooleanTest.php new file mode 100644 index 0000000..3bafdb7 --- /dev/null +++ b/src/Values/CastsToBooleanTest.php @@ -0,0 +1,66 @@ +assertInstanceOf(FakeCastsToBoolean::class, $truthy); + $this->assertInstanceOf(FakeCastsToBoolean::class, $falsy); + } + + public function testItCanReturnItsValue(): void + { + $truthy = new FakeCastsToBoolean(true); + $falsy = new FakeCastsToBoolean(false); + + $this->assertTrue($truthy->value()); + $this->assertFalse($falsy->value()); + } + + public function testItCanCheckIfItIsTruthy(): void + { + $truthy = new FakeCastsToBoolean(true); + $falsy = new FakeCastsToBoolean(false); + + $this->assertTrue($truthy->isTruthy()); + $this->assertFalse($falsy->isTruthy()); + } + + public function testItCanCheckIfItIsFalsy(): void + { + $truthy = new FakeCastsToBoolean(true); + $falsy = new FakeCastsToBoolean(false); + + $this->assertFalse($truthy->isFalsy()); + $this->assertTrue($falsy->isFalsy()); + } + + protected function keyword(): Keyword + { + return FakeKeyword::casted(true); + } + + protected function castsKeyword(): object + { + return FakeCastsToBoolean::withOriginalValue(); + } +} + +final class FakeCastsToBoolean +{ + use CastsToBoolean; + + public const ORIGINAL_VALUE = true; + + public static function withOriginalValue(): self + { + return new self(self::ORIGINAL_VALUE); + } +} From c323015391ead9570ee353403d8da30bee6afa30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Fri, 10 Jan 2020 23:49:31 +0100 Subject: [PATCH 03/16] Add new NullableKeyword --- src/Values/FakeKeyword.php | 10 +++--- src/Values/NullableKeyword.php | 55 ++++++++++++++++++++++++++++++ src/Values/NullableKeywordTest.php | 45 ++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 src/Values/NullableKeyword.php create mode 100644 src/Values/NullableKeywordTest.php diff --git a/src/Values/FakeKeyword.php b/src/Values/FakeKeyword.php index f27dbc4..d363f86 100644 --- a/src/Values/FakeKeyword.php +++ b/src/Values/FakeKeyword.php @@ -6,7 +6,9 @@ final class FakeKeyword implements Keyword { - private string $keyword; + public const STRING_VALUE = 'keyword'; + + private string $stringValue; /** * @var mixed @@ -15,18 +17,18 @@ final class FakeKeyword implements Keyword private function __construct($keyword, $castedValue) { + $this->stringValue = $keyword; $this->castedValue = $castedValue; - $this->keyword = $keyword; } - public static function casted($value, string $keyword = 'keyword'): self + public static function casted($value, string $keyword = self::STRING_VALUE): self { return new self($keyword, $value); } public function __toString(): string { - return $this->keyword; + return $this->stringValue; } public function castedValue() diff --git a/src/Values/NullableKeyword.php b/src/Values/NullableKeyword.php new file mode 100644 index 0000000..09b8b9a --- /dev/null +++ b/src/Values/NullableKeyword.php @@ -0,0 +1,55 @@ +guardAgainstInvalidDeferredKeyword($keyword = $deferredKeyword($keyword)); + + $this->deferredKeyword = $keyword; + } + } + + private function guardAgainstInvalidDeferredKeyword($keyword): void + { + if (! $keyword instanceof Keyword) { + throw new InvalidArgumentException( + 'Argument 2 of ['.self::class.'] must be a callable returning a ['.Keyword::class.'] instance.' + ); + } + } + + public function __toString(): string + { + if ($this->deferredKeyword) { + return (string) $this->deferredKeyword; + } + + return self::KEYWORD; + } + + public function castedValue() + { + if ($this->deferredKeyword) { + return $this->deferredKeyword->castedValue(); + } + + return null; + } + + public function deferredKeyword(): ?Keyword + { + return $this->deferredKeyword; + } +} diff --git a/src/Values/NullableKeywordTest.php b/src/Values/NullableKeywordTest.php new file mode 100644 index 0000000..329cb56 --- /dev/null +++ b/src/Values/NullableKeywordTest.php @@ -0,0 +1,45 @@ +expectException(InvalidArgumentException::class); + + new NullableKeyword('foo', fn () => 'invalid deferred keyword'); + } + + public function testItCanCastTheNullKeywordToTheNullValue(): void + { + $keyword = new NullableKeyword(self::KEYWORD, fn (...$arguments) => FakeKeyword::casted(...$arguments)); + + $this->assertEquals(self::KEYWORD, (string) $keyword); + $this->assertNull($keyword->castedValue()); + $this->assertNull($keyword->deferredKeyword()); + } + + public function testItUsesTheDeferredKeywordWhenTheKeywordValueIsNotNull(): void + { + $keyword = new NullableKeyword('foo', fn (...$arguments) => FakeKeyword::casted(...$arguments)); + + $this->assertEquals(FakeKeyword::STRING_VALUE, (string) $keyword); + $this->assertEquals('foo', $keyword->castedValue()); + $this->assertInstanceOf(FakeKeyword::class, $keyword->deferredKeyword()); + } + + public function testItImplementsTheKeywordInterface(): void + { + $keyword = new NullableKeyword('null', function () {}); + + $this->assertInstanceOf(Keyword::class, $keyword); + } +} From 04f61dc9e788a342d7f9dbc50bb42e45c6bb1867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Fri, 10 Jan 2020 23:58:03 +0100 Subject: [PATCH 04/16] Enable constructing nullable BooleanKeyword instances --- src/Values/BooleanKeyword.php | 3 ++- src/Values/BooleanKeywordTest.php | 16 ++++++++++++---- src/Values/ConstructsNullableKeyword.php | 11 +++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 src/Values/ConstructsNullableKeyword.php diff --git a/src/Values/BooleanKeyword.php b/src/Values/BooleanKeyword.php index 0a5de7b..a6d4663 100644 --- a/src/Values/BooleanKeyword.php +++ b/src/Values/BooleanKeyword.php @@ -6,11 +6,12 @@ use Distil\Exceptions\InvalidKeyword; -use function array_keys; use function array_key_exists; final class BooleanKeyword implements Keyword { + use ConstructsNullableKeyword; + public const CASTED_VALUES = [ 'true' => true, '1' => true, diff --git a/src/Values/BooleanKeywordTest.php b/src/Values/BooleanKeywordTest.php index f0f9ff2..cbde031 100644 --- a/src/Values/BooleanKeywordTest.php +++ b/src/Values/BooleanKeywordTest.php @@ -21,9 +21,9 @@ public function testItCanCastValidTruthyKeywordsToABoolean(): void $truthyKeyword = new BooleanKeyword('true'); $numericTruthyKeyword = new BooleanKeyword('1'); - $this->assertEquals('true', $truthyKeyword->__toString()); + $this->assertEquals('true', (string) $truthyKeyword); $this->assertTrue($truthyKeyword->castedValue()); - $this->assertEquals('1', $numericTruthyKeyword->__toString()); + $this->assertEquals('1', (string) $numericTruthyKeyword); $this->assertTrue($numericTruthyKeyword->castedValue()); } @@ -32,9 +32,9 @@ public function testItCanCastValidFalsyKeywordsToABoolean(): void $falsyKeyword = new BooleanKeyword('false'); $numericFalsyKeyword = new BooleanKeyword('0'); - $this->assertEquals('false', $falsyKeyword->__toString()); + $this->assertEquals('false', (string) $falsyKeyword); $this->assertFalse($falsyKeyword->castedValue()); - $this->assertEquals('0', $numericFalsyKeyword->__toString()); + $this->assertEquals('0', (string) $numericFalsyKeyword); $this->assertFalse($numericFalsyKeyword->castedValue()); } @@ -44,4 +44,12 @@ public function testItImplementsTheKeywordInterface(): void $this->assertInstanceOf(Keyword::class, $keyword); } + + public function testItCanConstructANullableInstance(): void + { + $keyword = BooleanKeyword::nullable('true'); + + $this->assertInstanceOf(NullableKeyword::class, $keyword); + $this->assertEquals(new BooleanKeyword('true'), $keyword->deferredKeyword()); + } } diff --git a/src/Values/ConstructsNullableKeyword.php b/src/Values/ConstructsNullableKeyword.php new file mode 100644 index 0000000..56ddc64 --- /dev/null +++ b/src/Values/ConstructsNullableKeyword.php @@ -0,0 +1,11 @@ + new self(...$arguments)); + } +} From 614abcc09d3fbb0fee0671af2ae17352251daab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sat, 11 Jan 2020 00:20:49 +0100 Subject: [PATCH 05/16] Add new CastsToBoolean trait and deprecate BooleanCriterion --- src/Exceptions/InvalidKeyword.php | 7 +++++ src/Types/IntegerCriterion.php | 39 ++++--------------------- src/Values/CastsToBooleanTest.php | 2 +- src/Values/CastsToInteger.php | 33 +++++++++++++++++++++ src/Values/CastsToIntegerTest.php | 44 ++++++++++++++++++++++++++++ src/Values/IntegerKeyword.php | 48 +++++++++++++++++++++++++++++++ src/Values/IntegerKeywordTest.php | 41 ++++++++++++++++++++++++++ 7 files changed, 179 insertions(+), 35 deletions(-) create mode 100644 src/Values/CastsToInteger.php create mode 100644 src/Values/CastsToIntegerTest.php create mode 100644 src/Values/IntegerKeyword.php create mode 100644 src/Values/IntegerKeywordTest.php diff --git a/src/Exceptions/InvalidKeyword.php b/src/Exceptions/InvalidKeyword.php index 3b338b4..46e496f 100644 --- a/src/Exceptions/InvalidKeyword.php +++ b/src/Exceptions/InvalidKeyword.php @@ -19,4 +19,11 @@ public static function cannotBeCastedToBoolean(string $keyword): self implode(', ', array_keys(BooleanKeyword::CASTED_VALUES)) ); } + + public static function cannotBeCastedToInteger(string $keyword): self + { + return new self( + "[$keyword] is not a valid integer keyword. It must be an string representation of an integer." + ); + } } diff --git a/src/Types/IntegerCriterion.php b/src/Types/IntegerCriterion.php index d7ab50a..02be2d9 100644 --- a/src/Types/IntegerCriterion.php +++ b/src/Types/IntegerCriterion.php @@ -4,42 +4,13 @@ use Distil\ActsAsCriteriaFactory; use Distil\Criterion; -use Distil\Exceptions\InvalidCriterionValue; -use Distil\Keywords\Keyword; -use Distil\Keywords\Value; +use Distil\Values\CastsToInteger; +/** + * @deprecated 1.0.0 Use the according traits instead. + */ abstract class IntegerCriterion implements Criterion { use ActsAsCriteriaFactory; - - private int $value; - - public function __construct(int $value) - { - $this->value = $value; - } - - /** - * @return static - */ - public static function fromString(string $value): self - { - $value = (new Keyword(static::class, $value))->value(); - - if (! is_numeric($value)) { - throw InvalidCriterionValue::expectedNumeric(static::class); - } - - return new static((int) $value); - } - - public function value(): int - { - return $this->value; - } - - public function __toString(): string - { - return (new Value($this, $this->value))->keyword() ?: (string) $this->value; - } + use CastsToInteger; } diff --git a/src/Values/CastsToBooleanTest.php b/src/Values/CastsToBooleanTest.php index 3bafdb7..7b50a35 100644 --- a/src/Values/CastsToBooleanTest.php +++ b/src/Values/CastsToBooleanTest.php @@ -6,7 +6,7 @@ final class CastsToBooleanTest extends CastsKeywordTestCase { - public function testItCanConstructWithATBooleanValue(): void + public function testItCanConstructWithABooleanValue(): void { $truthy = new FakeCastsToBoolean(true); $falsy = new FakeCastsToBoolean(false); diff --git a/src/Values/CastsToInteger.php b/src/Values/CastsToInteger.php new file mode 100644 index 0000000..ba35b32 --- /dev/null +++ b/src/Values/CastsToInteger.php @@ -0,0 +1,33 @@ +value = $value; + } + + public static function fromString(string $value): self + { + if (! is_numeric($value)) { + throw InvalidCriterionValue::expectedNumeric(static::class); + } + + return new static((int) $value); + } + + public function value(): int + { + return $this->value; + } +} diff --git a/src/Values/CastsToIntegerTest.php b/src/Values/CastsToIntegerTest.php new file mode 100644 index 0000000..4b9116a --- /dev/null +++ b/src/Values/CastsToIntegerTest.php @@ -0,0 +1,44 @@ +assertInstanceOf(FakeCastsToInteger::class, $instance); + } + + public function testItCanReturnItsValue(): void + { + $instance = new FakeCastsToInteger(20170728); + + $this->assertSame(20170728, $instance->value()); + } + + protected function keyword(): Keyword + { + return new IntegerKeyword('20170728'); + } + + protected function castsKeyword(): object + { + return FakeCastsToInteger::withOriginalValue(); + } +} + +final class FakeCastsToInteger +{ + use CastsToInteger; + + public const ORIGINAL_VALUE = 20170728; + + public static function withOriginalValue(): self + { + return new self(self::ORIGINAL_VALUE); + } +} diff --git a/src/Values/IntegerKeyword.php b/src/Values/IntegerKeyword.php new file mode 100644 index 0000000..822b6b8 --- /dev/null +++ b/src/Values/IntegerKeyword.php @@ -0,0 +1,48 @@ +guardAgainstInvalidValues($keyword); + + $this->stringValue = $keyword; + $this->castedValue = (int) $keyword; + } + + private function guardAgainstInvalidValues(string $keyword): void + { + if (! $this->isInt($keyword)) { + throw InvalidKeyword::cannotBeCastedToInteger($keyword); + } + } + + public function __toString(): string + { + return $this->stringValue; + } + + public function castedValue(): int + { + return $this->castedValue; + } + + private function isInt(string $keyword): bool + { + return is_numeric($keyword) && is_int($keyword + 0); + } +} diff --git a/src/Values/IntegerKeywordTest.php b/src/Values/IntegerKeywordTest.php new file mode 100644 index 0000000..d18dc9d --- /dev/null +++ b/src/Values/IntegerKeywordTest.php @@ -0,0 +1,41 @@ +expectException(InvalidKeyword::class); + + new IntegerKeyword('36.9'); + } + + public function testItCanCastValidIntegers(): void + { + $keyword = new IntegerKeyword('369'); + + $this->assertEquals('369', (string) $keyword); + $this->assertSame(369, $keyword->castedValue()); + } + + public function testItImplementsTheKeywordInterface(): void + { + $keyword = new IntegerKeyword('20070728'); + + $this->assertInstanceOf(Keyword::class, $keyword); + } + + public function testItCanConstructANullableInstance(): void + { + $keyword = IntegerKeyword::nullable('20070728'); + + $this->assertInstanceOf(NullableKeyword::class, $keyword); + $this->assertEquals(new IntegerKeyword('20070728'), $keyword->deferredKeyword()); + } +} From e4805dfcb137fe84940244b1baafd3aa908dfe95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sat, 11 Jan 2020 00:24:33 +0100 Subject: [PATCH 06/16] Rename CastsTo traits to ConstructFrom --- src/Types/BooleanCriterion.php | 4 +-- src/Types/IntegerCriterion.php | 4 +-- ...oBoolean.php => ConstructsFromBoolean.php} | 4 +-- ...Test.php => ConstructsFromBooleanTest.php} | 30 +++++++++---------- ...oInteger.php => ConstructsFromInteger.php} | 4 +-- ...Test.php => ConstructsFromIntegerTest.php} | 16 +++++----- ...Keywords.php => ConstructsFromKeyword.php} | 2 +- ...Test.php => ConstructsFromKeywordTest.php} | 6 ++-- ....php => ConstructsFromKeywordTestCase.php} | 14 ++++----- 9 files changed, 42 insertions(+), 42 deletions(-) rename src/Values/{CastsToBoolean.php => ConstructsFromBoolean.php} (91%) rename src/Values/{CastsToBooleanTest.php => ConstructsFromBooleanTest.php} (51%) rename src/Values/{CastsToInteger.php => ConstructsFromInteger.php} (89%) rename src/Values/{CastsToIntegerTest.php => ConstructsFromIntegerTest.php} (53%) rename src/Values/{CastsKeywords.php => ConstructsFromKeyword.php} (94%) rename src/Values/{CastsKeywordTest.php => ConstructsFromKeywordTest.php} (71%) rename src/Values/{CastsKeywordTestCase.php => ConstructsFromKeywordTestCase.php} (75%) diff --git a/src/Types/BooleanCriterion.php b/src/Types/BooleanCriterion.php index 24f9d78..807f229 100644 --- a/src/Types/BooleanCriterion.php +++ b/src/Types/BooleanCriterion.php @@ -4,7 +4,7 @@ use Distil\ActsAsCriteriaFactory; use Distil\Criterion; -use Distil\Values\CastsToBoolean; +use Distil\Values\ConstructsFromBoolean; /** * @deprecated 1.0.0 Use the according traits instead. @@ -12,5 +12,5 @@ abstract class BooleanCriterion implements Criterion { use ActsAsCriteriaFactory; - use CastsToBoolean; + use ConstructsFromBoolean; } diff --git a/src/Types/IntegerCriterion.php b/src/Types/IntegerCriterion.php index 02be2d9..9e79e31 100644 --- a/src/Types/IntegerCriterion.php +++ b/src/Types/IntegerCriterion.php @@ -4,7 +4,7 @@ use Distil\ActsAsCriteriaFactory; use Distil\Criterion; -use Distil\Values\CastsToInteger; +use Distil\Values\ConstructsFromInteger; /** * @deprecated 1.0.0 Use the according traits instead. @@ -12,5 +12,5 @@ abstract class IntegerCriterion implements Criterion { use ActsAsCriteriaFactory; - use CastsToInteger; + use ConstructsFromInteger; } diff --git a/src/Values/CastsToBoolean.php b/src/Values/ConstructsFromBoolean.php similarity index 91% rename from src/Values/CastsToBoolean.php rename to src/Values/ConstructsFromBoolean.php index 3fe89b1..896a90f 100644 --- a/src/Values/CastsToBoolean.php +++ b/src/Values/ConstructsFromBoolean.php @@ -8,9 +8,9 @@ use function in_array; -trait CastsToBoolean +trait ConstructsFromBoolean { - use CastsKeywords; + use ConstructsFromKeyword; private bool $value; diff --git a/src/Values/CastsToBooleanTest.php b/src/Values/ConstructsFromBooleanTest.php similarity index 51% rename from src/Values/CastsToBooleanTest.php rename to src/Values/ConstructsFromBooleanTest.php index 7b50a35..717428e 100644 --- a/src/Values/CastsToBooleanTest.php +++ b/src/Values/ConstructsFromBooleanTest.php @@ -4,21 +4,21 @@ namespace Distil\Values; -final class CastsToBooleanTest extends CastsKeywordTestCase +final class ConstructsFromBooleanTest extends ConstructsFromKeywordTestCase { public function testItCanConstructWithABooleanValue(): void { - $truthy = new FakeCastsToBoolean(true); - $falsy = new FakeCastsToBoolean(false); + $truthy = new FakeConstructsFromBoolean(true); + $falsy = new FakeConstructsFromBoolean(false); - $this->assertInstanceOf(FakeCastsToBoolean::class, $truthy); - $this->assertInstanceOf(FakeCastsToBoolean::class, $falsy); + $this->assertInstanceOf(FakeConstructsFromBoolean::class, $truthy); + $this->assertInstanceOf(FakeConstructsFromBoolean::class, $falsy); } public function testItCanReturnItsValue(): void { - $truthy = new FakeCastsToBoolean(true); - $falsy = new FakeCastsToBoolean(false); + $truthy = new FakeConstructsFromBoolean(true); + $falsy = new FakeConstructsFromBoolean(false); $this->assertTrue($truthy->value()); $this->assertFalse($falsy->value()); @@ -26,8 +26,8 @@ public function testItCanReturnItsValue(): void public function testItCanCheckIfItIsTruthy(): void { - $truthy = new FakeCastsToBoolean(true); - $falsy = new FakeCastsToBoolean(false); + $truthy = new FakeConstructsFromBoolean(true); + $falsy = new FakeConstructsFromBoolean(false); $this->assertTrue($truthy->isTruthy()); $this->assertFalse($falsy->isTruthy()); @@ -35,8 +35,8 @@ public function testItCanCheckIfItIsTruthy(): void public function testItCanCheckIfItIsFalsy(): void { - $truthy = new FakeCastsToBoolean(true); - $falsy = new FakeCastsToBoolean(false); + $truthy = new FakeConstructsFromBoolean(true); + $falsy = new FakeConstructsFromBoolean(false); $this->assertFalse($truthy->isFalsy()); $this->assertTrue($falsy->isFalsy()); @@ -47,15 +47,15 @@ protected function keyword(): Keyword return FakeKeyword::casted(true); } - protected function castsKeyword(): object + protected function constructsFromKeyword(): object { - return FakeCastsToBoolean::withOriginalValue(); + return FakeConstructsFromBoolean::withOriginalValue(); } } -final class FakeCastsToBoolean +final class FakeConstructsFromBoolean { - use CastsToBoolean; + use ConstructsFromBoolean; public const ORIGINAL_VALUE = true; diff --git a/src/Values/CastsToInteger.php b/src/Values/ConstructsFromInteger.php similarity index 89% rename from src/Values/CastsToInteger.php rename to src/Values/ConstructsFromInteger.php index ba35b32..712d5db 100644 --- a/src/Values/CastsToInteger.php +++ b/src/Values/ConstructsFromInteger.php @@ -6,9 +6,9 @@ use Distil\Exceptions\InvalidCriterionValue; -trait CastsToInteger +trait ConstructsFromInteger { - use CastsKeywords; + use ConstructsFromKeyword; private int $value; diff --git a/src/Values/CastsToIntegerTest.php b/src/Values/ConstructsFromIntegerTest.php similarity index 53% rename from src/Values/CastsToIntegerTest.php rename to src/Values/ConstructsFromIntegerTest.php index 4b9116a..765b003 100644 --- a/src/Values/CastsToIntegerTest.php +++ b/src/Values/ConstructsFromIntegerTest.php @@ -4,18 +4,18 @@ namespace Distil\Values; -final class CastsToIntegerTest extends CastsKeywordTestCase +final class ConstructsFromToIntegerTest extends ConstructsFromKeywordTestCase { public function testItCanConstructWithAnIntegerValue(): void { - $instance = new FakeCastsToInteger(20170728); + $instance = new FakeConstructsFromInteger(20170728); - $this->assertInstanceOf(FakeCastsToInteger::class, $instance); + $this->assertInstanceOf(FakeConstructsFromInteger::class, $instance); } public function testItCanReturnItsValue(): void { - $instance = new FakeCastsToInteger(20170728); + $instance = new FakeConstructsFromInteger(20170728); $this->assertSame(20170728, $instance->value()); } @@ -25,15 +25,15 @@ protected function keyword(): Keyword return new IntegerKeyword('20170728'); } - protected function castsKeyword(): object + protected function constructsFromKeyword(): object { - return FakeCastsToInteger::withOriginalValue(); + return FakeConstructsFromInteger::withOriginalValue(); } } -final class FakeCastsToInteger +final class FakeConstructsFromInteger { - use CastsToInteger; + use ConstructsFromInteger; public const ORIGINAL_VALUE = 20170728; diff --git a/src/Values/CastsKeywords.php b/src/Values/ConstructsFromKeyword.php similarity index 94% rename from src/Values/CastsKeywords.php rename to src/Values/ConstructsFromKeyword.php index d5227d4..83565b3 100644 --- a/src/Values/CastsKeywords.php +++ b/src/Values/ConstructsFromKeyword.php @@ -2,7 +2,7 @@ namespace Distil\Values; -trait CastsKeywords +trait ConstructsFromKeyword { private ?Keyword $keyword = null; diff --git a/src/Values/CastsKeywordTest.php b/src/Values/ConstructsFromKeywordTest.php similarity index 71% rename from src/Values/CastsKeywordTest.php rename to src/Values/ConstructsFromKeywordTest.php index 98db566..6d655fa 100644 --- a/src/Values/CastsKeywordTest.php +++ b/src/Values/ConstructsFromKeywordTest.php @@ -6,14 +6,14 @@ use PHPUnit\Framework\TestCase; -final class CastsKeywordTest extends CastsKeywordTestCase +final class ConstructsFromKeywordTest extends ConstructsFromKeywordTestCase { protected function keyword(): Keyword { return FakeKeyword::casted(FakeCastsKeyword::ORIGINAL_VALUE); } - protected function castsKeyword(): object + protected function constructsFromKeyword(): object { return new FakeCastsKeyword(); } @@ -21,7 +21,7 @@ protected function castsKeyword(): object final class FakeCastsKeyword { - use CastsKeywords; + use ConstructsFromKeyword; public const ORIGINAL_VALUE = 'foo'; diff --git a/src/Values/CastsKeywordTestCase.php b/src/Values/ConstructsFromKeywordTestCase.php similarity index 75% rename from src/Values/CastsKeywordTestCase.php rename to src/Values/ConstructsFromKeywordTestCase.php index cf7c3d1..559d3b8 100644 --- a/src/Values/CastsKeywordTestCase.php +++ b/src/Values/ConstructsFromKeywordTestCase.php @@ -8,15 +8,15 @@ use function get_class; -abstract class CastsKeywordTestCase extends TestCase +abstract class ConstructsFromKeywordTestCase extends TestCase { abstract protected function keyword(): Keyword; - abstract protected function castsKeyword(): object; + abstract protected function constructsFromKeyword(): object; public function testItCanConstructTheImplementingClassFromAKeyword(): void { $keyword = $this->keyword(); - $castsKeyword = $this->castsKeyword(); + $castsKeyword = $this->constructsFromKeyword(); $instance = $castsKeyword::fromKeyword($keyword); @@ -26,7 +26,7 @@ public function testItCanConstructTheImplementingClassFromAKeyword(): void public function testItCanReturnTheKeyword(): void { $keyword = $this->keyword(); - $castsKeyword = $this->castsKeyword(); + $castsKeyword = $this->constructsFromKeyword(); $instance = $castsKeyword::fromKeyword($keyword); @@ -35,7 +35,7 @@ public function testItCanReturnTheKeyword(): void public function testItReturnsNullWhenNotConstructedFromAKeyword(): void { - $castsKeyword = $this->castsKeyword(); + $castsKeyword = $this->constructsFromKeyword(); $this->assertNull($castsKeyword->keyword()); } @@ -43,7 +43,7 @@ public function testItReturnsNullWhenNotConstructedFromAKeyword(): void public function testItReturnsTheKeywordWhenCastingToAString(): void { $keyword = $this->keyword(); - $castsKeyword = $this->castsKeyword(); + $castsKeyword = $this->constructsFromKeyword(); $instance = $castsKeyword::fromKeyword($keyword); @@ -52,7 +52,7 @@ public function testItReturnsTheKeywordWhenCastingToAString(): void public function testItReturnsTheStringValueWhenCastingToAStringWhenNotConstructedFromAKeyword(): void { - $castsKeyword = $this->castsKeyword(); + $castsKeyword = $this->constructsFromKeyword(); $this->assertSame((string) $castsKeyword::ORIGINAL_VALUE, (string) $castsKeyword); } From bd1b02a0f6d65f7594284de6258b7b9c57945f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sat, 11 Jan 2020 12:04:43 +0100 Subject: [PATCH 07/16] Add new ConstructsFromString trait and deprecate StringCriterion --- src/Types/StringCriterion.php | 24 +++----------- src/Values/ConstructsFromString.php | 22 +++++++++++++ src/Values/ConstructsFromStringTest.php | 44 +++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 src/Values/ConstructsFromString.php create mode 100644 src/Values/ConstructsFromStringTest.php diff --git a/src/Types/StringCriterion.php b/src/Types/StringCriterion.php index ec55bda..fa0f942 100644 --- a/src/Types/StringCriterion.php +++ b/src/Types/StringCriterion.php @@ -4,27 +4,13 @@ use Distil\ActsAsCriteriaFactory; use Distil\Criterion; -use Distil\Keywords\Keyword; -use Distil\Keywords\Value; +use Distil\Values\ConstructsFromKeyword; +/** + * @deprecated 1.0.0 Use the according traits instead. + */ abstract class StringCriterion implements Criterion { use ActsAsCriteriaFactory; - - private string $value; - - public function __construct(string $value) - { - $this->value = (new Keyword(static::class, $value))->value(); - } - - public function value(): string - { - return $this->value; - } - - public function __toString(): string - { - return (new Value($this, $this->value))->keyword() ?: $this->value; - } + use ConstructsFromKeyword; } diff --git a/src/Values/ConstructsFromString.php b/src/Values/ConstructsFromString.php new file mode 100644 index 0000000..9539372 --- /dev/null +++ b/src/Values/ConstructsFromString.php @@ -0,0 +1,22 @@ +value = $value; + } + + public function value(): string + { + return $this->value; + } +} diff --git a/src/Values/ConstructsFromStringTest.php b/src/Values/ConstructsFromStringTest.php new file mode 100644 index 0000000..d3ffe16 --- /dev/null +++ b/src/Values/ConstructsFromStringTest.php @@ -0,0 +1,44 @@ +assertInstanceOf(FakeConstructsFromString::class, $instance); + } + + public function testItCanReturnItsValue(): void + { + $instance = new FakeConstructsFromString('foo'); + + $this->assertSame('foo', $instance->value()); + } + + protected function keyword(): Keyword + { + return FakeKeyword::casted('foo'); + } + + protected function constructsFromKeyword(): object + { + return FakeConstructsFromString::withOriginalValue(); + } +} + +final class FakeConstructsFromString +{ + use ConstructsFromString; + + public const ORIGINAL_VALUE = 'foo'; + + public static function withOriginalValue(): self + { + return new self(self::ORIGINAL_VALUE); + } +} From 0690fe15cb5d7cc03ba0d75c0f6df648b0509b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sat, 11 Jan 2020 12:08:01 +0100 Subject: [PATCH 08/16] Mark Fakes as internal These classes should not be used outside the package --- src/FakeCriterion.php | 3 +++ src/Values/FakeKeyword.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/FakeCriterion.php b/src/FakeCriterion.php index eac9026..98618ba 100644 --- a/src/FakeCriterion.php +++ b/src/FakeCriterion.php @@ -4,6 +4,9 @@ namespace Distil; +/** + * @internal + */ final class FakeCriterion implements Criterion { use ActsAsCriteriaFactory; diff --git a/src/Values/FakeKeyword.php b/src/Values/FakeKeyword.php index d363f86..b01af49 100644 --- a/src/Values/FakeKeyword.php +++ b/src/Values/FakeKeyword.php @@ -4,6 +4,9 @@ namespace Distil\Values; +/** + * @internal + */ final class FakeKeyword implements Keyword { public const STRING_VALUE = 'keyword'; From 9a726e03db41af15ca9fc545660e33d5315694a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sat, 11 Jan 2020 13:11:43 +0100 Subject: [PATCH 09/16] Add new ConstructsFromDateTime trait and deprecate DateTimeCriterion --- src/Exceptions/InvalidKeyword.php | 8 ++ src/Types/DateTimeCriterion.php | 51 ++----------- src/Values/ConstructsFromDateTime.php | 46 +++++++++++ src/Values/ConstructsFromDateTimeTest.php | 80 ++++++++++++++++++++ src/Values/ConstructsFromKeywordTestCase.php | 22 +++--- src/Values/DateTimeKeyword.php | 57 ++++++++++++++ src/Values/DateTimeKeywordTest.php | 63 +++++++++++++++ 7 files changed, 270 insertions(+), 57 deletions(-) create mode 100644 src/Values/ConstructsFromDateTime.php create mode 100644 src/Values/ConstructsFromDateTimeTest.php create mode 100644 src/Values/DateTimeKeyword.php create mode 100644 src/Values/DateTimeKeywordTest.php diff --git a/src/Exceptions/InvalidKeyword.php b/src/Exceptions/InvalidKeyword.php index 46e496f..e1855fa 100644 --- a/src/Exceptions/InvalidKeyword.php +++ b/src/Exceptions/InvalidKeyword.php @@ -26,4 +26,12 @@ public static function cannotBeCastedToInteger(string $keyword): self "[$keyword] is not a valid integer keyword. It must be an string representation of an integer." ); } + + public static function cannotBeCastedToDateTime(string $keyword): self + { + return new self( + "[$keyword] is not a valid DateTime keyword. It must be a date/time string ". + '(see https://www.php.net/manual/en/datetime.formats.php).' + ); + } } diff --git a/src/Types/DateTimeCriterion.php b/src/Types/DateTimeCriterion.php index 74af5e5..14eeaab 100644 --- a/src/Types/DateTimeCriterion.php +++ b/src/Types/DateTimeCriterion.php @@ -2,56 +2,15 @@ namespace Distil\Types; -use DateTime; -use DateTimeImmutable; -use DateTimeInterface; use Distil\ActsAsCriteriaFactory; use Distil\Criterion; -use Distil\Exceptions\InvalidCriterionValue; -use Distil\Keywords\Keyword; -use Distil\Keywords\Value; +use Distil\Values\ConstructsFromDateTime; +/** + * @deprecated 1.0.0 Use the according traits instead. + */ abstract class DateTimeCriterion implements Criterion { use ActsAsCriteriaFactory; - - private DateTimeInterface $value; - private string $format; - - public function __construct(DateTimeInterface $value, string $format = DateTime::ATOM) - { - $this->value = $value; - $this->format = $format; - } - - /** - * @return static - */ - public static function fromString(string $value, string $format = DateTime::ATOM): self - { - $value = (new Keyword(static::class, $value))->value(); - - if ($value instanceof DateTimeInterface) { - return new static($value, $format); - } elseif (strtotime($value)) { - return new static(new DateTimeImmutable($value), $format); - } - - throw InvalidCriterionValue::expectedTimeString(static::class); - } - - public function value(): DateTimeInterface - { - return $this->value; - } - - public function format(): string - { - return $this->format; - } - - public function __toString(): string - { - return (new Value($this, $this->value))->keyword() ?: $this->value->format($this->format); - } + use ConstructsFromDateTime; } diff --git a/src/Values/ConstructsFromDateTime.php b/src/Values/ConstructsFromDateTime.php new file mode 100644 index 0000000..790a019 --- /dev/null +++ b/src/Values/ConstructsFromDateTime.php @@ -0,0 +1,46 @@ +value = $value; + $this->format = $format; + } + + public function __toString(): string + { + if ($this->keyword) { + return (string) $this->keyword; + } + + return $this->value->format($this->format); + } + + /** + * @return static + */ + public static function fromString(string $value): self + { + return self::fromKeyword(new DateTimeKeyword($value)); + } + + public function value(): DateTimeInterface + { + return $this->value; + } + + public function format(): string + { + return $this->format; + } +} diff --git a/src/Values/ConstructsFromDateTimeTest.php b/src/Values/ConstructsFromDateTimeTest.php new file mode 100644 index 0000000..ce8a409 --- /dev/null +++ b/src/Values/ConstructsFromDateTimeTest.php @@ -0,0 +1,80 @@ +assertInstanceOf(FakeConstructsFromDateTime::class, $instance); + } + + public function testItCanReturnItsValue(): void + { + $instance = new FakeConstructsFromDateTime( + new DateTimeImmutable('today'), + ); + + $this->assertEquals(new DateTimeImmutable('today'), $instance->value()); + } + + public function testItCanReturnItsFormat(): void + { + $instance = new FakeConstructsFromDateTime( + new DateTimeImmutable('tomorrow'), + ); + + $this->assertSame(DateTimeInterface::ATOM, $instance->format()); + } + + public function testItCanConstructWithACustomFormat(): void + { + $instance = new FakeConstructsFromDateTime( + new DateTimeImmutable('tomorrow'), + 'Ymd', + ); + + $this->assertSame('Ymd', $instance->format()); + } + + public function testItReturnsTheFormattedValueWhenCastingToAStringWhenNotConstructedFromAKeyword(): void + { + $constructsFromKeyword = new FakeConstructsFromDateTime( + new DateTimeImmutable('2007-07-28 19:30:00'), + 'Y-m-d' + ); + + $this->assertSame('2007-07-28', (string) $constructsFromKeyword); + } + + protected function keyword(): Keyword + { + return new DateTimeKeyword('2007-07-28 19:30:00'); + } + + protected function constructsFromKeyword(): object + { + return FakeConstructsFromDateTime::withOriginalValue(); + } +} + +final class FakeConstructsFromDateTime +{ + use ConstructsFromDateTime; + + public const ORIGINAL_VALUE = '2007-07-28T19:30:00+00:00'; + + public static function withOriginalValue(): self + { + return new self(new DateTimeImmutable(self::ORIGINAL_VALUE)); + } +} diff --git a/src/Values/ConstructsFromKeywordTestCase.php b/src/Values/ConstructsFromKeywordTestCase.php index 559d3b8..72d01d2 100644 --- a/src/Values/ConstructsFromKeywordTestCase.php +++ b/src/Values/ConstructsFromKeywordTestCase.php @@ -16,44 +16,44 @@ abstract protected function constructsFromKeyword(): object; public function testItCanConstructTheImplementingClassFromAKeyword(): void { $keyword = $this->keyword(); - $castsKeyword = $this->constructsFromKeyword(); + $constructsFromKeyword = $this->constructsFromKeyword(); - $instance = $castsKeyword::fromKeyword($keyword); + $instance = $constructsFromKeyword::fromKeyword($keyword); - $this->assertInstanceOf(get_class($castsKeyword), $instance); + $this->assertInstanceOf(get_class($constructsFromKeyword), $instance); } public function testItCanReturnTheKeyword(): void { $keyword = $this->keyword(); - $castsKeyword = $this->constructsFromKeyword(); + $constructsFromKeyword = $this->constructsFromKeyword(); - $instance = $castsKeyword::fromKeyword($keyword); + $instance = $constructsFromKeyword::fromKeyword($keyword); $this->assertSame($keyword, $instance->keyword()); } public function testItReturnsNullWhenNotConstructedFromAKeyword(): void { - $castsKeyword = $this->constructsFromKeyword(); + $constructsFromKeyword = $this->constructsFromKeyword(); - $this->assertNull($castsKeyword->keyword()); + $this->assertNull($constructsFromKeyword->keyword()); } public function testItReturnsTheKeywordWhenCastingToAString(): void { $keyword = $this->keyword(); - $castsKeyword = $this->constructsFromKeyword(); + $constructsFromKeyword = $this->constructsFromKeyword(); - $instance = $castsKeyword::fromKeyword($keyword); + $instance = $constructsFromKeyword::fromKeyword($keyword); $this->assertSame((string) $keyword, (string) $instance); } public function testItReturnsTheStringValueWhenCastingToAStringWhenNotConstructedFromAKeyword(): void { - $castsKeyword = $this->constructsFromKeyword(); + $constructsFromKeyword = $this->constructsFromKeyword(); - $this->assertSame((string) $castsKeyword::ORIGINAL_VALUE, (string) $castsKeyword); + $this->assertSame((string) $constructsFromKeyword::ORIGINAL_VALUE, (string) $constructsFromKeyword); } } diff --git a/src/Values/DateTimeKeyword.php b/src/Values/DateTimeKeyword.php new file mode 100644 index 0000000..67fb01b --- /dev/null +++ b/src/Values/DateTimeKeyword.php @@ -0,0 +1,57 @@ +guardAgainstInvalidValues($keyword); + $parsedKeyword = $this->isTimestamp($keyword) ? "@$keyword" : $keyword; + + $this->stringValue = $keyword; + $this->castedValue = new DateTimeImmutable($parsedKeyword); + } + + private function guardAgainstInvalidValues(string $keyword): void + { + if (! $this->isValidKeyword($keyword)) { + throw InvalidKeyword::cannotBeCastedToDateTime($keyword); + } + } + + public function __toString(): string + { + return $this->stringValue; + } + + public function castedValue(): DateTimeInterface + { + return $this->castedValue; + } + + private function isValidKeyword(string $keyword): bool + { + return $this->isTimestamp($keyword) || strtotime($keyword) !== false; + } + + private function isTimestamp(string $keyword): bool + { + return is_numeric($keyword) && date_create("@$keyword") !== false; + } +} diff --git a/src/Values/DateTimeKeywordTest.php b/src/Values/DateTimeKeywordTest.php new file mode 100644 index 0000000..bf34327 --- /dev/null +++ b/src/Values/DateTimeKeywordTest.php @@ -0,0 +1,63 @@ +expectException(InvalidKeyword::class); + + new DateTimeKeyword('nonsense'); + } + + public function testItCanCastReadableDateTimeStrings(): void + { + $today = new DateTimeKeyword($todayString = 'today'); + $future = new DateTimeKeyword($futureString = 'tomorrow + 6days'); + + $this->assertEquals($todayString, (string) $today); + $this->assertEquals(new DateTimeImmutable($todayString), $today->castedValue()); + $this->assertEquals($futureString, (string) $future); + $this->assertEquals(new DateTimeImmutable($futureString), $future->castedValue()); + } + + public function testItCanCastTimestampStrings(): void + { + $timestamp = strtotime('2007-07-28 19:30:00'); + $keyword = new DateTimeKeyword((string) $timestamp); + + $this->assertEquals($timestamp, (string) $keyword); + $this->assertEquals(new DateTimeImmutable("@$timestamp"), $keyword->castedValue()); + } + + public function testItCanCastFormattedDateTimeStrings(): void + { + $formattedDateTimeString = '2007-07-28 19:30:00'; + $keyword = new DateTimeKeyword($formattedDateTimeString); + + $this->assertEquals($formattedDateTimeString, (string) $keyword); + $this->assertEquals(new DateTimeImmutable($formattedDateTimeString), $keyword->castedValue()); + } + + public function testItImplementsTheKeywordInterface(): void + { + $keyword = new DateTimeKeyword('today'); + + $this->assertInstanceOf(Keyword::class, $keyword); + } + + public function testItCanConstructANullableInstance(): void + { + $keyword = DateTimeKeyword::nullable('today'); + + $this->assertInstanceOf(NullableKeyword::class, $keyword); + $this->assertEquals(new DateTimeKeyword('today'), $keyword->deferredKeyword()); + } +} From d23df3c30aed689f442c7a2a81bd8421de68d261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sat, 11 Jan 2020 13:12:18 +0100 Subject: [PATCH 10/16] Fix styleci config --- .styleci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.styleci.yml b/.styleci.yml index 88b8e58..c32f2d0 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,12 +1,12 @@ preset: recommended - enabled: - not_operator_with_successor_space - phpdoc_no_empty_return - unalign_double_arrow - + - no_superfluous_phpdoc_tags disabled: - align_double_arrow + - alpha_ordered_imports + - no_blank_lines_between_imports - phpdoc_align - phpdoc_separation - - simplified_null_return From 5955cd1d7943c32bc063d6e4d8cf354b2996ce82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sat, 11 Jan 2020 13:23:15 +0100 Subject: [PATCH 11/16] StyleCI fixes --- src/CriteriaTest.php | 1 - src/Values/ConstructsFromBoolean.php | 4 ---- src/Values/ConstructsFromKeywordTest.php | 2 -- src/Values/ConstructsFromKeywordTestCase.php | 1 + src/Values/Keyword.php | 1 + src/Values/NullableKeywordTest.php | 2 +- 6 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/CriteriaTest.php b/src/CriteriaTest.php index 214b113..515b685 100644 --- a/src/CriteriaTest.php +++ b/src/CriteriaTest.php @@ -6,7 +6,6 @@ use Distil\Exceptions\CannotAddCriterion; use Distil\Exceptions\CannotGetCriterion; -use Distil\FakeCriterion; use PHPUnit\Framework\TestCase; use function array_values; diff --git a/src/Values/ConstructsFromBoolean.php b/src/Values/ConstructsFromBoolean.php index 896a90f..8429304 100644 --- a/src/Values/ConstructsFromBoolean.php +++ b/src/Values/ConstructsFromBoolean.php @@ -4,10 +4,6 @@ namespace Distil\Values; -use Distil\Exceptions\InvalidCriterionValue; - -use function in_array; - trait ConstructsFromBoolean { use ConstructsFromKeyword; diff --git a/src/Values/ConstructsFromKeywordTest.php b/src/Values/ConstructsFromKeywordTest.php index 6d655fa..ef37747 100644 --- a/src/Values/ConstructsFromKeywordTest.php +++ b/src/Values/ConstructsFromKeywordTest.php @@ -4,8 +4,6 @@ namespace Distil\Values; -use PHPUnit\Framework\TestCase; - final class ConstructsFromKeywordTest extends ConstructsFromKeywordTestCase { protected function keyword(): Keyword diff --git a/src/Values/ConstructsFromKeywordTestCase.php b/src/Values/ConstructsFromKeywordTestCase.php index 72d01d2..0d13f96 100644 --- a/src/Values/ConstructsFromKeywordTestCase.php +++ b/src/Values/ConstructsFromKeywordTestCase.php @@ -11,6 +11,7 @@ abstract class ConstructsFromKeywordTestCase extends TestCase { abstract protected function keyword(): Keyword; + abstract protected function constructsFromKeyword(): object; public function testItCanConstructTheImplementingClassFromAKeyword(): void diff --git a/src/Values/Keyword.php b/src/Values/Keyword.php index ccd8f12..7a48d49 100644 --- a/src/Values/Keyword.php +++ b/src/Values/Keyword.php @@ -7,5 +7,6 @@ interface Keyword { public function __toString(): string; + public function castedValue(); } diff --git a/src/Values/NullableKeywordTest.php b/src/Values/NullableKeywordTest.php index 329cb56..b400f64 100644 --- a/src/Values/NullableKeywordTest.php +++ b/src/Values/NullableKeywordTest.php @@ -38,7 +38,7 @@ public function testItUsesTheDeferredKeywordWhenTheKeywordValueIsNotNull(): void public function testItImplementsTheKeywordInterface(): void { - $keyword = new NullableKeyword('null', function () {}); + $keyword = new NullableKeyword('null', fn () => null); $this->assertInstanceOf(Keyword::class, $keyword); } From 97a018afac5f3d22a1e93c6a3920fdfadb96ff07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sun, 16 Aug 2020 19:12:45 +0200 Subject: [PATCH 12/16] Cleanup value tests with data providers --- src/Values/BooleanKeywordTest.php | 93 ++++++++++++++++------ src/Values/ConstructsFromList.php | 44 +++++++++++ src/Values/ConstructsNullableKeyword.php | 2 +- src/Values/DateTimeKeywordTest.php | 98 +++++++++++++++++------- src/Values/FakeKeyword.php | 4 +- src/Values/IntegerKeywordTest.php | 88 ++++++++++++++++++--- src/Values/NullableKeyword.php | 20 ++--- src/Values/NullableKeywordTest.php | 46 +++++++---- 8 files changed, 298 insertions(+), 97 deletions(-) create mode 100644 src/Values/ConstructsFromList.php diff --git a/src/Values/BooleanKeywordTest.php b/src/Values/BooleanKeywordTest.php index cbde031..469f077 100644 --- a/src/Values/BooleanKeywordTest.php +++ b/src/Values/BooleanKeywordTest.php @@ -9,47 +9,92 @@ final class BooleanKeywordTest extends TestCase { - public function testItFailsToInitializeWithAnInvalidValue(): void + /** + * @test + */ + public function it_fails_to_initialize_with_an_invalid_keyword(): void { - $this->expectException(InvalidKeyword::class); + $invalidValue = 'rubbish'; - new BooleanKeyword('rubbish'); + $this->expectExceptionObject(InvalidKeyword::cannotBeCastedToBoolean($invalidValue)); + + new BooleanKeyword($invalidValue); } - public function testItCanCastValidTruthyKeywordsToABoolean(): void + /** + * @test + */ + public function it_implements_the_keyword_interface(): void { - $truthyKeyword = new BooleanKeyword('true'); - $numericTruthyKeyword = new BooleanKeyword('1'); + $keyword = new BooleanKeyword('true'); - $this->assertEquals('true', (string) $truthyKeyword); - $this->assertTrue($truthyKeyword->castedValue()); - $this->assertEquals('1', (string) $numericTruthyKeyword); - $this->assertTrue($numericTruthyKeyword->castedValue()); + $this->assertInstanceOf(Keyword::class, $keyword); } - public function testItCanCastValidFalsyKeywordsToABoolean(): void + public function validStringValues(): array { - $falsyKeyword = new BooleanKeyword('false'); - $numericFalsyKeyword = new BooleanKeyword('0'); - - $this->assertEquals('false', (string) $falsyKeyword); - $this->assertFalse($falsyKeyword->castedValue()); - $this->assertEquals('0', (string) $numericFalsyKeyword); - $this->assertFalse($numericFalsyKeyword->castedValue()); + return [ + [ + 'string_value' => 'true', + 'expected_casted_value' => true, + ], + [ + 'string_value' => '1', + 'expected_casted_value' => true, + ], + [ + 'string_value' => 'false', + 'expected_casted_value' => false, + ], + [ + 'string_value' => '0', + 'expected_casted_value' => false, + ], + ]; } - public function testItImplementsTheKeywordInterface(): void + /** + * @test + * @dataProvider validStringValues + */ + public function it_can_return_its_casted_value(string $stringValue, bool $expectedCastedValue): void { - $keyword = new BooleanKeyword('true'); + $this->assertSame($expectedCastedValue, (new BooleanKeyword($stringValue))->castedValue()); + } - $this->assertInstanceOf(Keyword::class, $keyword); + /** + * @test + * @dataProvider validStringValues + */ + public function it_can_be_casted_to_a_string(string $keyword): void + { + $this->assertSame($keyword, (string) new BooleanKeyword($keyword)); } - public function testItCanConstructANullableInstance(): void + /** + * @test + */ + public function it_can_be_initialized_to_accept_nullable_values(): void { - $keyword = BooleanKeyword::nullable('true'); + $keyword = BooleanKeyword::nullable(NullableKeyword::VALUE); + + $this->assertInstanceOf(NullableKeyword::class, $keyword); + $this->assertSame(null, $keyword->castedValue()); + $this->assertNull($keyword->deferredKeyword()); + } + + /** + * @test + * @dataProvider validStringValues + */ + public function it_returns_the_deferred_keyword_when_accepting_nullable_values_but_the_value_is_not_nullable( + string $stringValue, + $expectedCastedValue + ): void { + $keyword = BooleanKeyword::nullable($stringValue); $this->assertInstanceOf(NullableKeyword::class, $keyword); - $this->assertEquals(new BooleanKeyword('true'), $keyword->deferredKeyword()); + $this->assertEquals($expectedCastedValue, (new BooleanKeyword($stringValue))->castedValue()); + $this->assertEquals(new BooleanKeyword($stringValue), $keyword->deferredKeyword()); } } diff --git a/src/Values/ConstructsFromList.php b/src/Values/ConstructsFromList.php new file mode 100644 index 0000000..abca6f7 --- /dev/null +++ b/src/Values/ConstructsFromList.php @@ -0,0 +1,44 @@ +guardAgainstMixedTypes(...$values); + + $this->value = $values; + } + + private function guardAgainstMixedTypes(...$values): void + { + $value = array_shift($values); + + array_reduce($values, [$this, 'compareValueTypes'], $value); + } + + private function compareValueTypes($current, $next) + { + $currentType = gettype($current); + $nextType = gettype($next); + + if ($currentType !== $nextType) { + throw InvalidCriterionValue::mixedValueTypes($currentType, $nextType); + } + + return $next; + } +} diff --git a/src/Values/ConstructsNullableKeyword.php b/src/Values/ConstructsNullableKeyword.php index 56ddc64..22a5a11 100644 --- a/src/Values/ConstructsNullableKeyword.php +++ b/src/Values/ConstructsNullableKeyword.php @@ -6,6 +6,6 @@ trait ConstructsNullableKeyword { public static function nullable(string $keyword): NullableKeyword { - return new NullableKeyword($keyword, fn (...$arguments) => new self(...$arguments)); + return new NullableKeyword($keyword, fn (string $keyword) => new self($keyword)); } } diff --git a/src/Values/DateTimeKeywordTest.php b/src/Values/DateTimeKeywordTest.php index bf34327..72c2633 100644 --- a/src/Values/DateTimeKeywordTest.php +++ b/src/Values/DateTimeKeywordTest.php @@ -8,56 +8,100 @@ use Distil\Exceptions\InvalidKeyword; use PHPUnit\Framework\TestCase; +use function strtotime; + final class DateTimeKeywordTest extends TestCase { - public function testItFailsToInitializeWithAnInvalidValue(): void + /** + * @test + */ + public function it_fails_to_initialize_with_an_invalid_value(): void { - $this->expectException(InvalidKeyword::class); + $keyword = 'nonsense'; + + $this->expectExceptionObject(InvalidKeyword::cannotBeCastedToDateTime($keyword)); - new DateTimeKeyword('nonsense'); + new DateTimeKeyword($keyword); } - public function testItCanCastReadableDateTimeStrings(): void + /** + * @test + */ + public function it_implements_the_keyword_interface(): void { - $today = new DateTimeKeyword($todayString = 'today'); - $future = new DateTimeKeyword($futureString = 'tomorrow + 6days'); + $keyword = new DateTimeKeyword('today'); - $this->assertEquals($todayString, (string) $today); - $this->assertEquals(new DateTimeImmutable($todayString), $today->castedValue()); - $this->assertEquals($futureString, (string) $future); - $this->assertEquals(new DateTimeImmutable($futureString), $future->castedValue()); + $this->assertInstanceOf(Keyword::class, $keyword); } - public function testItCanCastTimestampStrings(): void + public function validStringValues(): array { - $timestamp = strtotime('2007-07-28 19:30:00'); - $keyword = new DateTimeKeyword((string) $timestamp); - - $this->assertEquals($timestamp, (string) $keyword); - $this->assertEquals(new DateTimeImmutable("@$timestamp"), $keyword->castedValue()); + return [ + [ + 'string_value' => $today = 'today', + 'expected_casted_value' => new DateTimeImmutable($today), + ], + [ + 'string_value' => $tomorrow = 'today +1 days', + 'expected_casted_value' => new DateTimeImmutable($tomorrow), + ], + [ + 'string_value' => $timestamp = (string) strtotime('2007-07-28 19:30:00'), + 'expected_casted_value' => new DateTimeImmutable("@$timestamp"), + ], + [ + 'string_value' => $formatted = '2007-07-28 19:30:00', + 'expected_casted_value' => new DateTimeImmutable($formatted), + ], + ]; } - public function testItCanCastFormattedDateTimeStrings(): void + /** + * @test + * @dataProvider validStringValues + */ + public function it_can_return_its_casted_value(string $stringValue, DateTimeImmutable $expectedCastedValue): void { - $formattedDateTimeString = '2007-07-28 19:30:00'; - $keyword = new DateTimeKeyword($formattedDateTimeString); + $keyword = new DateTimeKeyword($stringValue); - $this->assertEquals($formattedDateTimeString, (string) $keyword); - $this->assertEquals(new DateTimeImmutable($formattedDateTimeString), $keyword->castedValue()); + $this->assertEquals($expectedCastedValue, $keyword->castedValue()); } - public function testItImplementsTheKeywordInterface(): void + /** + * @test + * @dataProvider validStringValues + */ + public function it_can_be_casted_to_a_string(string $stringValue): void { - $keyword = new DateTimeKeyword('today'); + $keyword = new DateTimeKeyword($stringValue); - $this->assertInstanceOf(Keyword::class, $keyword); + $this->assertEquals($stringValue, (string) $keyword); } - public function testItCanConstructANullableInstance(): void + /** + * @test + */ + public function it_can_be_initialized_to_accept_nullable_values(): void { - $keyword = DateTimeKeyword::nullable('today'); + $keyword = DateTimeKeyword::nullable(NullableKeyword::VALUE); + + $this->assertInstanceOf(NullableKeyword::class, $keyword); + $this->assertSame(null, $keyword->castedValue()); + $this->assertNull($keyword->deferredKeyword()); + } + + /** + * @test + * @dataProvider validStringValues + */ + public function it_returns_the_deferred_keyword_when_accepting_nullable_values_but_the_value_is_not_nullable( + string $stringValue, + $expectedCastedValue + ): void { + $keyword = DateTimeKeyword::nullable($stringValue); $this->assertInstanceOf(NullableKeyword::class, $keyword); - $this->assertEquals(new DateTimeKeyword('today'), $keyword->deferredKeyword()); + $this->assertEquals($expectedCastedValue, (new DateTimeKeyword($stringValue))->castedValue()); + $this->assertEquals(new DateTimeKeyword($stringValue), $keyword->deferredKeyword()); } } diff --git a/src/Values/FakeKeyword.php b/src/Values/FakeKeyword.php index b01af49..0ed254e 100644 --- a/src/Values/FakeKeyword.php +++ b/src/Values/FakeKeyword.php @@ -9,7 +9,7 @@ */ final class FakeKeyword implements Keyword { - public const STRING_VALUE = 'keyword'; + public const VALUE = 'keyword'; private string $stringValue; @@ -24,7 +24,7 @@ private function __construct($keyword, $castedValue) $this->castedValue = $castedValue; } - public static function casted($value, string $keyword = self::STRING_VALUE): self + public static function casted($value, string $keyword = self::VALUE): self { return new self($keyword, $value); } diff --git a/src/Values/IntegerKeywordTest.php b/src/Values/IntegerKeywordTest.php index d18dc9d..65173e2 100644 --- a/src/Values/IntegerKeywordTest.php +++ b/src/Values/IntegerKeywordTest.php @@ -9,33 +9,97 @@ final class IntegerKeywordTest extends TestCase { - public function testItFailsToInitializeWithAnInvalidValue(): void + public function invalidStringValues(): array { - $this->expectException(InvalidKeyword::class); - - new IntegerKeyword('36.9'); + return [ + ['36.9'], + ['36,9'], + ['nonsense'], + ['true'], + ]; } - public function testItCanCastValidIntegers(): void + /** + * @test + * @dataProvider invalidStringValues + */ + public function it_fails_to_initialize_with_an_invalid_value(string $stringValue): void { - $keyword = new IntegerKeyword('369'); + $this->expectException(InvalidKeyword::class); - $this->assertEquals('369', (string) $keyword); - $this->assertSame(369, $keyword->castedValue()); + new IntegerKeyword($stringValue); } - public function testItImplementsTheKeywordInterface(): void + /** + * @test + */ + public function it_implements_the_keyword_interface(): void { $keyword = new IntegerKeyword('20070728'); $this->assertInstanceOf(Keyword::class, $keyword); } - public function testItCanConstructANullableInstance(): void + public function validStringValues(): array + { + return [ + [ + 'string_value' => '369', + 'expected_casted_value' => 369, + ], + [ + 'string_value' => '0', + 'expected_casted_value' => 0, + ], + ]; + } + + /** + * @test + * @dataProvider validStringValues + */ + public function it_can_return_its_casted_value(string $stringValue, int $expectedCastedValue): void + { + $keyword = new IntegerKeyword($stringValue); + + $this->assertSame($expectedCastedValue, $keyword->castedValue()); + } + + /** + * @test + * @dataProvider validStringValues + */ + public function it_can_be_casted_to_a_string(string $stringValue): void + { + $keyword = new IntegerKeyword($stringValue); + + $this->assertEquals($stringValue, (string) $keyword); + } + + /** + * @test + */ + public function it_can_be_initialized_to_accept_nullable_values(): void { - $keyword = IntegerKeyword::nullable('20070728'); + $keyword = IntegerKeyword::nullable(NullableKeyword::VALUE); + + $this->assertInstanceOf(NullableKeyword::class, $keyword); + $this->assertSame(null, $keyword->castedValue()); + $this->assertNull($keyword->deferredKeyword()); + } + + /** + * @test + * @dataProvider validStringValues + */ + public function it_returns_the_deferred_keyword_when_accepting_nullable_values_but_the_value_is_not_nullable( + string $stringValue, + $expectedCastedValue + ): void { + $keyword = IntegerKeyword::nullable($stringValue); $this->assertInstanceOf(NullableKeyword::class, $keyword); - $this->assertEquals(new IntegerKeyword('20070728'), $keyword->deferredKeyword()); + $this->assertEquals($expectedCastedValue, (new IntegerKeyword($stringValue))->castedValue()); + $this->assertEquals(new IntegerKeyword($stringValue), $keyword->deferredKeyword()); } } diff --git a/src/Values/NullableKeyword.php b/src/Values/NullableKeyword.php index 09b8b9a..5d1a461 100644 --- a/src/Values/NullableKeyword.php +++ b/src/Values/NullableKeyword.php @@ -4,30 +4,22 @@ namespace Distil\Values; -use InvalidArgumentException; - final class NullableKeyword implements Keyword { - public const KEYWORD = 'null'; + public const VALUE = 'null'; private ?Keyword $deferredKeyword = null; public function __construct(string $keyword, callable $deferredKeyword) { - if ($keyword !== self::KEYWORD) { - $this->guardAgainstInvalidDeferredKeyword($keyword = $deferredKeyword($keyword)); - - $this->deferredKeyword = $keyword; + if (! $this->isNullValue($keyword)) { + $this->deferredKeyword = $deferredKeyword($keyword); } } - private function guardAgainstInvalidDeferredKeyword($keyword): void + private function isNullValue(string $keyword): bool { - if (! $keyword instanceof Keyword) { - throw new InvalidArgumentException( - 'Argument 2 of ['.self::class.'] must be a callable returning a ['.Keyword::class.'] instance.' - ); - } + return $keyword === self::VALUE; } public function __toString(): string @@ -36,7 +28,7 @@ public function __toString(): string return (string) $this->deferredKeyword; } - return self::KEYWORD; + return self::VALUE; } public function castedValue() diff --git a/src/Values/NullableKeywordTest.php b/src/Values/NullableKeywordTest.php index b400f64..30cc710 100644 --- a/src/Values/NullableKeywordTest.php +++ b/src/Values/NullableKeywordTest.php @@ -4,42 +4,54 @@ namespace Distil\Values; -use InvalidArgumentException; use PHPUnit\Framework\TestCase; +use TypeError; final class NullableKeywordTest extends TestCase { - private const KEYWORD = 'null'; + private const EXPECTED_VALUE = 'null'; - public function testItFailsWithAnInvalidKeywordFactory(): void + /** + * @test + */ + public function it_fails_with_an_invalid_keyword_factory(): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException(TypeError::class); new NullableKeyword('foo', fn () => 'invalid deferred keyword'); } - public function testItCanCastTheNullKeywordToTheNullValue(): void + /** + * @test + */ + public function it_implements_the_keyword_interface(): void { - $keyword = new NullableKeyword(self::KEYWORD, fn (...$arguments) => FakeKeyword::casted(...$arguments)); + $keyword = new NullableKeyword('null', fn () => null); - $this->assertEquals(self::KEYWORD, (string) $keyword); - $this->assertNull($keyword->castedValue()); - $this->assertNull($keyword->deferredKeyword()); + $this->assertInstanceOf(Keyword::class, $keyword); } - public function testItUsesTheDeferredKeywordWhenTheKeywordValueIsNotNull(): void + /** + * @test + */ + public function it_can_cast_the_null_keyword_to_the_null_value(): void { - $keyword = new NullableKeyword('foo', fn (...$arguments) => FakeKeyword::casted(...$arguments)); + $keyword = new NullableKeyword(self::EXPECTED_VALUE, fn (string $keyword) => FakeKeyword::casted($keyword)); - $this->assertEquals(FakeKeyword::STRING_VALUE, (string) $keyword); - $this->assertEquals('foo', $keyword->castedValue()); - $this->assertInstanceOf(FakeKeyword::class, $keyword->deferredKeyword()); + $this->assertSame(self::EXPECTED_VALUE, (string) $keyword); + $this->assertNull($keyword->castedValue()); + $this->assertNull($keyword->deferredKeyword()); } - public function testItImplementsTheKeywordInterface(): void + /** + * @test + */ + public function it_uses_the_deferred_keyword_when_the_keyword_value_is_not_null(): void { - $keyword = new NullableKeyword('null', fn () => null); + $keyword = new NullableKeyword('foo', fn (string $keyword) => FakeKeyword::casted($keyword)); - $this->assertInstanceOf(Keyword::class, $keyword); + $this->assertSame(FakeKeyword::VALUE, (string) $keyword); + $this->assertSame('foo', $keyword->castedValue()); + $this->assertInstanceOf(FakeKeyword::class, $keyword->deferredKeyword()); } } From 2c2b8daa1362490b13deba3531b9352bb89d3fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sun, 16 Aug 2020 19:31:02 +0200 Subject: [PATCH 13/16] Undo deprecation of Criterion type This should be refactored in a different PR --- src/Types/BooleanCriterion.php | 41 ++++++++-- src/Types/DateTimeCriterion.php | 45 +++++++++-- src/Types/IntegerCriterion.php | 31 ++++++-- src/Types/StringCriterion.php | 3 - src/Values/ConstructsFromBoolean.php | 40 ---------- src/Values/ConstructsFromBooleanTest.php | 66 ---------------- src/Values/ConstructsFromDateTime.php | 46 ----------- src/Values/ConstructsFromDateTimeTest.php | 80 -------------------- src/Values/ConstructsFromInteger.php | 33 -------- src/Values/ConstructsFromIntegerTest.php | 44 ----------- src/Values/ConstructsFromKeywordTest.php | 63 +++++++++++++-- src/Values/ConstructsFromKeywordTestCase.php | 60 --------------- src/Values/ConstructsFromList.php | 44 ----------- src/Values/ConstructsFromString.php | 22 ------ src/Values/ConstructsFromStringTest.php | 44 ----------- 15 files changed, 160 insertions(+), 502 deletions(-) delete mode 100644 src/Values/ConstructsFromBoolean.php delete mode 100644 src/Values/ConstructsFromBooleanTest.php delete mode 100644 src/Values/ConstructsFromDateTime.php delete mode 100644 src/Values/ConstructsFromDateTimeTest.php delete mode 100644 src/Values/ConstructsFromInteger.php delete mode 100644 src/Values/ConstructsFromIntegerTest.php delete mode 100644 src/Values/ConstructsFromKeywordTestCase.php delete mode 100644 src/Values/ConstructsFromList.php delete mode 100644 src/Values/ConstructsFromString.php delete mode 100644 src/Values/ConstructsFromStringTest.php diff --git a/src/Types/BooleanCriterion.php b/src/Types/BooleanCriterion.php index 807f229..38dac3b 100644 --- a/src/Types/BooleanCriterion.php +++ b/src/Types/BooleanCriterion.php @@ -4,13 +4,44 @@ use Distil\ActsAsCriteriaFactory; use Distil\Criterion; -use Distil\Values\ConstructsFromBoolean; +use Distil\Values\BooleanKeyword; +use Distil\Values\ConstructsFromKeyword; -/** - * @deprecated 1.0.0 Use the according traits instead. - */ abstract class BooleanCriterion implements Criterion { use ActsAsCriteriaFactory; - use ConstructsFromBoolean; + use ConstructsFromKeyword; + + public const KEYWORD_TRUE = 'true'; + public const KEYWORD_FALSE = 'false'; + + private bool $value; + + public function __construct(bool $value) + { + $this->value = $value; + } + + /** + * @return static + */ + public static function fromString(string $value): self + { + return self::fromKeyword(new BooleanKeyword($value)); + } + + public function value(): bool + { + return $this->value; + } + + public function isTruthy(): bool + { + return $this->value; + } + + public function isFalsy(): bool + { + return ! $this->value; + } } diff --git a/src/Types/DateTimeCriterion.php b/src/Types/DateTimeCriterion.php index 14eeaab..d5d65e8 100644 --- a/src/Types/DateTimeCriterion.php +++ b/src/Types/DateTimeCriterion.php @@ -2,15 +2,50 @@ namespace Distil\Types; +use DateTimeInterface; use Distil\ActsAsCriteriaFactory; use Distil\Criterion; -use Distil\Values\ConstructsFromDateTime; +use Distil\Values\ConstructsFromKeyword; +use Distil\Values\DateTimeKeyword; -/** - * @deprecated 1.0.0 Use the according traits instead. - */ abstract class DateTimeCriterion implements Criterion { use ActsAsCriteriaFactory; - use ConstructsFromDateTime; + use ConstructsFromKeyword; + + private DateTimeInterface $value; + private string $format; + + public function __construct(DateTimeInterface $value, string $format = DateTimeInterface::ATOM) + { + $this->value = $value; + $this->format = $format; + } + + public function __toString(): string + { + if ($this->keyword) { + return (string) $this->keyword; + } + + return $this->value->format($this->format); + } + + /** + * @return static + */ + public static function fromString(string $value): self + { + return self::fromKeyword(new DateTimeKeyword($value)); + } + + public function value(): DateTimeInterface + { + return $this->value; + } + + public function format(): string + { + return $this->format; + } } diff --git a/src/Types/IntegerCriterion.php b/src/Types/IntegerCriterion.php index 9e79e31..91bfef1 100644 --- a/src/Types/IntegerCriterion.php +++ b/src/Types/IntegerCriterion.php @@ -4,13 +4,34 @@ use Distil\ActsAsCriteriaFactory; use Distil\Criterion; -use Distil\Values\ConstructsFromInteger; +use Distil\Exceptions\InvalidCriterionValue; +use Distil\Values\ConstructsFromKeyword; + +use function is_numeric; -/** - * @deprecated 1.0.0 Use the according traits instead. - */ abstract class IntegerCriterion implements Criterion { use ActsAsCriteriaFactory; - use ConstructsFromInteger; + use ConstructsFromKeyword; + + private int $value; + + public function __construct(int $value) + { + $this->value = $value; + } + + public static function fromString(string $value): self + { + if (! is_numeric($value)) { + throw InvalidCriterionValue::expectedNumeric(static::class); + } + + return new static((int) $value); + } + + public function value(): int + { + return $this->value; + } } diff --git a/src/Types/StringCriterion.php b/src/Types/StringCriterion.php index fa0f942..6c9787a 100644 --- a/src/Types/StringCriterion.php +++ b/src/Types/StringCriterion.php @@ -6,9 +6,6 @@ use Distil\Criterion; use Distil\Values\ConstructsFromKeyword; -/** - * @deprecated 1.0.0 Use the according traits instead. - */ abstract class StringCriterion implements Criterion { use ActsAsCriteriaFactory; diff --git a/src/Values/ConstructsFromBoolean.php b/src/Values/ConstructsFromBoolean.php deleted file mode 100644 index 8429304..0000000 --- a/src/Values/ConstructsFromBoolean.php +++ /dev/null @@ -1,40 +0,0 @@ -value = $value; - } - - /** - * @return static - */ - public static function fromString(string $value): self - { - return self::fromKeyword(new BooleanKeyword($value)); - } - - public function value(): bool - { - return $this->value; - } - - public function isTruthy(): bool - { - return $this->value; - } - - public function isFalsy(): bool - { - return ! $this->value; - } -} diff --git a/src/Values/ConstructsFromBooleanTest.php b/src/Values/ConstructsFromBooleanTest.php deleted file mode 100644 index 717428e..0000000 --- a/src/Values/ConstructsFromBooleanTest.php +++ /dev/null @@ -1,66 +0,0 @@ -assertInstanceOf(FakeConstructsFromBoolean::class, $truthy); - $this->assertInstanceOf(FakeConstructsFromBoolean::class, $falsy); - } - - public function testItCanReturnItsValue(): void - { - $truthy = new FakeConstructsFromBoolean(true); - $falsy = new FakeConstructsFromBoolean(false); - - $this->assertTrue($truthy->value()); - $this->assertFalse($falsy->value()); - } - - public function testItCanCheckIfItIsTruthy(): void - { - $truthy = new FakeConstructsFromBoolean(true); - $falsy = new FakeConstructsFromBoolean(false); - - $this->assertTrue($truthy->isTruthy()); - $this->assertFalse($falsy->isTruthy()); - } - - public function testItCanCheckIfItIsFalsy(): void - { - $truthy = new FakeConstructsFromBoolean(true); - $falsy = new FakeConstructsFromBoolean(false); - - $this->assertFalse($truthy->isFalsy()); - $this->assertTrue($falsy->isFalsy()); - } - - protected function keyword(): Keyword - { - return FakeKeyword::casted(true); - } - - protected function constructsFromKeyword(): object - { - return FakeConstructsFromBoolean::withOriginalValue(); - } -} - -final class FakeConstructsFromBoolean -{ - use ConstructsFromBoolean; - - public const ORIGINAL_VALUE = true; - - public static function withOriginalValue(): self - { - return new self(self::ORIGINAL_VALUE); - } -} diff --git a/src/Values/ConstructsFromDateTime.php b/src/Values/ConstructsFromDateTime.php deleted file mode 100644 index 790a019..0000000 --- a/src/Values/ConstructsFromDateTime.php +++ /dev/null @@ -1,46 +0,0 @@ -value = $value; - $this->format = $format; - } - - public function __toString(): string - { - if ($this->keyword) { - return (string) $this->keyword; - } - - return $this->value->format($this->format); - } - - /** - * @return static - */ - public static function fromString(string $value): self - { - return self::fromKeyword(new DateTimeKeyword($value)); - } - - public function value(): DateTimeInterface - { - return $this->value; - } - - public function format(): string - { - return $this->format; - } -} diff --git a/src/Values/ConstructsFromDateTimeTest.php b/src/Values/ConstructsFromDateTimeTest.php deleted file mode 100644 index ce8a409..0000000 --- a/src/Values/ConstructsFromDateTimeTest.php +++ /dev/null @@ -1,80 +0,0 @@ -assertInstanceOf(FakeConstructsFromDateTime::class, $instance); - } - - public function testItCanReturnItsValue(): void - { - $instance = new FakeConstructsFromDateTime( - new DateTimeImmutable('today'), - ); - - $this->assertEquals(new DateTimeImmutable('today'), $instance->value()); - } - - public function testItCanReturnItsFormat(): void - { - $instance = new FakeConstructsFromDateTime( - new DateTimeImmutable('tomorrow'), - ); - - $this->assertSame(DateTimeInterface::ATOM, $instance->format()); - } - - public function testItCanConstructWithACustomFormat(): void - { - $instance = new FakeConstructsFromDateTime( - new DateTimeImmutable('tomorrow'), - 'Ymd', - ); - - $this->assertSame('Ymd', $instance->format()); - } - - public function testItReturnsTheFormattedValueWhenCastingToAStringWhenNotConstructedFromAKeyword(): void - { - $constructsFromKeyword = new FakeConstructsFromDateTime( - new DateTimeImmutable('2007-07-28 19:30:00'), - 'Y-m-d' - ); - - $this->assertSame('2007-07-28', (string) $constructsFromKeyword); - } - - protected function keyword(): Keyword - { - return new DateTimeKeyword('2007-07-28 19:30:00'); - } - - protected function constructsFromKeyword(): object - { - return FakeConstructsFromDateTime::withOriginalValue(); - } -} - -final class FakeConstructsFromDateTime -{ - use ConstructsFromDateTime; - - public const ORIGINAL_VALUE = '2007-07-28T19:30:00+00:00'; - - public static function withOriginalValue(): self - { - return new self(new DateTimeImmutable(self::ORIGINAL_VALUE)); - } -} diff --git a/src/Values/ConstructsFromInteger.php b/src/Values/ConstructsFromInteger.php deleted file mode 100644 index 712d5db..0000000 --- a/src/Values/ConstructsFromInteger.php +++ /dev/null @@ -1,33 +0,0 @@ -value = $value; - } - - public static function fromString(string $value): self - { - if (! is_numeric($value)) { - throw InvalidCriterionValue::expectedNumeric(static::class); - } - - return new static((int) $value); - } - - public function value(): int - { - return $this->value; - } -} diff --git a/src/Values/ConstructsFromIntegerTest.php b/src/Values/ConstructsFromIntegerTest.php deleted file mode 100644 index 765b003..0000000 --- a/src/Values/ConstructsFromIntegerTest.php +++ /dev/null @@ -1,44 +0,0 @@ -assertInstanceOf(FakeConstructsFromInteger::class, $instance); - } - - public function testItCanReturnItsValue(): void - { - $instance = new FakeConstructsFromInteger(20170728); - - $this->assertSame(20170728, $instance->value()); - } - - protected function keyword(): Keyword - { - return new IntegerKeyword('20170728'); - } - - protected function constructsFromKeyword(): object - { - return FakeConstructsFromInteger::withOriginalValue(); - } -} - -final class FakeConstructsFromInteger -{ - use ConstructsFromInteger; - - public const ORIGINAL_VALUE = 20170728; - - public static function withOriginalValue(): self - { - return new self(self::ORIGINAL_VALUE); - } -} diff --git a/src/Values/ConstructsFromKeywordTest.php b/src/Values/ConstructsFromKeywordTest.php index ef37747..62855f4 100644 --- a/src/Values/ConstructsFromKeywordTest.php +++ b/src/Values/ConstructsFromKeywordTest.php @@ -4,16 +4,69 @@ namespace Distil\Values; -final class ConstructsFromKeywordTest extends ConstructsFromKeywordTestCase +use PHPUnit\Framework\TestCase; + +use function get_class; + +final class ConstructsFromKeywordTest extends TestCase { - protected function keyword(): Keyword + /** + * @test + */ + public function it_can_construct_the_implementing_class_from_a_keyword(): void + { + $keyword = FakeKeyword::casted(FakeCastsKeyword::ORIGINAL_VALUE); + $constructsFromKeyword = new FakeCastsKeyword(); + + $instance = $constructsFromKeyword::fromKeyword($keyword); + + $this->assertInstanceOf(get_class($constructsFromKeyword), $instance); + } + + /** + * @test + */ + public function it_can_return_the_keyword(): void { - return FakeKeyword::casted(FakeCastsKeyword::ORIGINAL_VALUE); + $keyword = FakeKeyword::casted(FakeCastsKeyword::ORIGINAL_VALUE); + $constructsFromKeyword = new FakeCastsKeyword(); + + $instance = $constructsFromKeyword::fromKeyword($keyword); + + $this->assertSame($keyword, $instance->keyword()); } - protected function constructsFromKeyword(): object + /** + * @test + */ + public function it_returns_null_when_not_constructed_from_a_keyword(): void { - return new FakeCastsKeyword(); + $constructsFromKeyword = new FakeCastsKeyword(); + + $this->assertNull($constructsFromKeyword->keyword()); + } + + /** + * @test + */ + public function it_returns_the_keyword_when_casting_to_a_string(): void + { + $keyword = FakeKeyword::casted(FakeCastsKeyword::ORIGINAL_VALUE); + $constructsFromKeyword = new FakeCastsKeyword(); + + $instance = $constructsFromKeyword::fromKeyword($keyword); + + $this->assertSame((string) $keyword, (string) $instance); + } + + /** + * @test + */ + public function it_returns_the_string_value_when_casting_to_a_string_when_not_constructed_from_a_keyword(): void + { + $constructsFromKeyword = new FakeCastsKeyword(); + + $this->assertSame((string) $constructsFromKeyword::ORIGINAL_VALUE, (string) $constructsFromKeyword); } } diff --git a/src/Values/ConstructsFromKeywordTestCase.php b/src/Values/ConstructsFromKeywordTestCase.php deleted file mode 100644 index 0d13f96..0000000 --- a/src/Values/ConstructsFromKeywordTestCase.php +++ /dev/null @@ -1,60 +0,0 @@ -keyword(); - $constructsFromKeyword = $this->constructsFromKeyword(); - - $instance = $constructsFromKeyword::fromKeyword($keyword); - - $this->assertInstanceOf(get_class($constructsFromKeyword), $instance); - } - - public function testItCanReturnTheKeyword(): void - { - $keyword = $this->keyword(); - $constructsFromKeyword = $this->constructsFromKeyword(); - - $instance = $constructsFromKeyword::fromKeyword($keyword); - - $this->assertSame($keyword, $instance->keyword()); - } - - public function testItReturnsNullWhenNotConstructedFromAKeyword(): void - { - $constructsFromKeyword = $this->constructsFromKeyword(); - - $this->assertNull($constructsFromKeyword->keyword()); - } - - public function testItReturnsTheKeywordWhenCastingToAString(): void - { - $keyword = $this->keyword(); - $constructsFromKeyword = $this->constructsFromKeyword(); - - $instance = $constructsFromKeyword::fromKeyword($keyword); - - $this->assertSame((string) $keyword, (string) $instance); - } - - public function testItReturnsTheStringValueWhenCastingToAStringWhenNotConstructedFromAKeyword(): void - { - $constructsFromKeyword = $this->constructsFromKeyword(); - - $this->assertSame((string) $constructsFromKeyword::ORIGINAL_VALUE, (string) $constructsFromKeyword); - } -} diff --git a/src/Values/ConstructsFromList.php b/src/Values/ConstructsFromList.php deleted file mode 100644 index abca6f7..0000000 --- a/src/Values/ConstructsFromList.php +++ /dev/null @@ -1,44 +0,0 @@ -guardAgainstMixedTypes(...$values); - - $this->value = $values; - } - - private function guardAgainstMixedTypes(...$values): void - { - $value = array_shift($values); - - array_reduce($values, [$this, 'compareValueTypes'], $value); - } - - private function compareValueTypes($current, $next) - { - $currentType = gettype($current); - $nextType = gettype($next); - - if ($currentType !== $nextType) { - throw InvalidCriterionValue::mixedValueTypes($currentType, $nextType); - } - - return $next; - } -} diff --git a/src/Values/ConstructsFromString.php b/src/Values/ConstructsFromString.php deleted file mode 100644 index 9539372..0000000 --- a/src/Values/ConstructsFromString.php +++ /dev/null @@ -1,22 +0,0 @@ -value = $value; - } - - public function value(): string - { - return $this->value; - } -} diff --git a/src/Values/ConstructsFromStringTest.php b/src/Values/ConstructsFromStringTest.php deleted file mode 100644 index d3ffe16..0000000 --- a/src/Values/ConstructsFromStringTest.php +++ /dev/null @@ -1,44 +0,0 @@ -assertInstanceOf(FakeConstructsFromString::class, $instance); - } - - public function testItCanReturnItsValue(): void - { - $instance = new FakeConstructsFromString('foo'); - - $this->assertSame('foo', $instance->value()); - } - - protected function keyword(): Keyword - { - return FakeKeyword::casted('foo'); - } - - protected function constructsFromKeyword(): object - { - return FakeConstructsFromString::withOriginalValue(); - } -} - -final class FakeConstructsFromString -{ - use ConstructsFromString; - - public const ORIGINAL_VALUE = 'foo'; - - public static function withOriginalValue(): self - { - return new self(self::ORIGINAL_VALUE); - } -} From 7d7cb3cca0a5a9339bca9a8fd4f2775c8d3216f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Sun, 16 Aug 2020 19:33:07 +0200 Subject: [PATCH 14/16] Use pretty print for phpunit output --- composer.json | 3 ++- phpunit.xml | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index fbfc6fd..b062202 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ "php": "^7.4" }, "require-dev": { - "phpunit/phpunit": "^8" + "phpunit/phpunit": "^8", + "sempro/phpunit-pretty-print": "^1.2" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml index fd87600..063c68a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,5 +1,9 @@ - + src/ From 312f445d17b483d1553a2c3ee44a14943887300c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Wed, 19 Aug 2020 12:37:11 +0200 Subject: [PATCH 15/16] Use PSR12 preset on StyleCI --- .styleci.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.styleci.yml b/.styleci.yml index c32f2d0..6c847ba 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -1,12 +1,5 @@ -preset: recommended +preset: psr12 enabled: - not_operator_with_successor_space - phpdoc_no_empty_return - unalign_double_arrow - - no_superfluous_phpdoc_tags -disabled: - - align_double_arrow - - alpha_ordered_imports - - no_blank_lines_between_imports - - phpdoc_align - - phpdoc_separation From af0390d518c25c4d3ff4ed5ebed915710189c2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Pelhate?= Date: Wed, 19 Aug 2020 12:49:41 +0200 Subject: [PATCH 16/16] Fix codestyle violation --- src/Exceptions/InvalidKeyword.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Exceptions/InvalidKeyword.php b/src/Exceptions/InvalidKeyword.php index e1855fa..8f36277 100644 --- a/src/Exceptions/InvalidKeyword.php +++ b/src/Exceptions/InvalidKeyword.php @@ -10,12 +10,12 @@ use function array_keys; use function implode; -class InvalidKeyword extends RuntimeException +final class InvalidKeyword extends RuntimeException { public static function cannotBeCastedToBoolean(string $keyword): self { return new self( - "[$keyword] is not a valid boolean keyword. Valid keywords are: ". + "[$keyword] is not a valid boolean keyword. Valid keywords are: " . implode(', ', array_keys(BooleanKeyword::CASTED_VALUES)) ); } @@ -30,7 +30,7 @@ public static function cannotBeCastedToInteger(string $keyword): self public static function cannotBeCastedToDateTime(string $keyword): self { return new self( - "[$keyword] is not a valid DateTime keyword. It must be a date/time string ". + "[$keyword] is not a valid DateTime keyword. It must be a date/time string " . '(see https://www.php.net/manual/en/datetime.formats.php).' ); }