From d9b918eb25978ffba2e6bf4a82fb113410c5db9e Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Mon, 16 Sep 2024 13:03:39 +0200 Subject: [PATCH 1/4] Add custom value provider --- src/Constraint/AccessorPairConstraint.php | 8 +++++ src/Constraint/ConstraintConfig.php | 24 ++++++++++++++ .../Integration/AccessorPairAsserterTest.php | 19 ++++++++++-- tests/Integration/data/manual/FinalClass.php | 31 +++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 tests/Integration/data/manual/FinalClass.php diff --git a/src/Constraint/AccessorPairConstraint.php b/src/Constraint/AccessorPairConstraint.php index 7d8b6be..1a685af 100644 --- a/src/Constraint/AccessorPairConstraint.php +++ b/src/Constraint/AccessorPairConstraint.php @@ -281,6 +281,14 @@ protected function getTestValues(ReflectionMethod $method, ReflectionParameter $ $resolver = new TypehintResolver($method); $typehint = $resolver->getParamTypehint($parameter); + $valueProvider = $this->config->getValueProvider(); + if ($valueProvider !== null) { + $value = $valueProvider($typehint); + if ($value !== null) { + return [$value]; + } + } + return $this->valueProviderFactory->getProvider($typehint)->getValues(); } diff --git a/src/Constraint/ConstraintConfig.php b/src/Constraint/ConstraintConfig.php index 3e391d1..1f37f71 100644 --- a/src/Constraint/ConstraintConfig.php +++ b/src/Constraint/ConstraintConfig.php @@ -3,6 +3,8 @@ namespace DigitalRevolution\AccessorPairConstraint\Constraint; +use phpDocumentor\Reflection\Type; + class ConstraintConfig { /** @var bool */ @@ -23,6 +25,9 @@ class ConstraintConfig /** @var null|callable(): mixed[] */ private $constructorCallback = null; + /** @var null|(callable(Type): mixed) */ + private $valueProvider = null; + public function hasAccessorPairCheck(): bool { return $this->assertAccessorPair; @@ -129,4 +134,23 @@ public function setConstructorCallback(callable $callback): self return $this; } + + public function getValueProvider(): ?callable + { + return $this->valueProvider; + } + + /** + * Callback function to allow for a custom value provider. For instance for final classes. The argument + * is the typehint of the value, the return value should be the provided value. Return null + * to skip the value provider and use the default value providers. + * + * @param callable(Type): mixed $valueProvider + */ + public function setValueProvider(callable $valueProvider): self + { + $this->valueProvider = $valueProvider; + + return $this; + } } diff --git a/tests/Integration/AccessorPairAsserterTest.php b/tests/Integration/AccessorPairAsserterTest.php index 8d50464..8eaaa0d 100644 --- a/tests/Integration/AccessorPairAsserterTest.php +++ b/tests/Integration/AccessorPairAsserterTest.php @@ -6,6 +6,7 @@ use DigitalRevolution\AccessorPairConstraint\AccessorPairAsserter; use DigitalRevolution\AccessorPairConstraint\Constraint\ConstraintConfig; use DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\manual\CustomConstructorParameters; +use DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\manual\FinalClass; use DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\manual\IntersectionClassProperty; use DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\manual\IntersectionInterfaceProperty; use DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\manual\SetterTransformer; @@ -13,6 +14,8 @@ use DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\manual\UnionProperty; use DigitalRevolution\AccessorPairConstraint\Tests\TestCase; use Generator; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\Types\Object_; use PHPUnit\Framework\ExpectationFailedException; use ReflectionException; use TypeError; @@ -101,7 +104,6 @@ public function testMatchesSuccessInitialStateWithDefaultMethod(object $class): /** * When turning off the propertyDefaultCheck, we can safely pass classes we know will fail the constraint - * * @dataProvider failureInitialStateDataProvider */ public function testExcludingInitialStateCheck(object $class): void @@ -119,7 +121,6 @@ public function testMatchesSuccessConstructorPair(object $class): void /** * When turning off the constructorPairCheck, we can safely pass classes we know will fail the constraint - * * @dataProvider failureConstructorDataProvider */ public function testExcludingConstructorPair(object $class): void @@ -196,6 +197,20 @@ public function testIntersectionClassProperty(): void static::assertAccessorPairs(IntersectionClassProperty::class); } + public function testFinalClassWithCustomValueProvider(): void + { + $config = new ConstraintConfig(); + $config->setValueProvider(static function (Type $type) { + if ($type instanceof Object_ && (string)$type->getFqsen() !== FinalClass::class) { + return new FinalClass(); + } + + return null; + }); + + static::assertAccessorPairs(FinalClass::class, $config); + } + /** * @return Generator> * @throws ReflectionException diff --git a/tests/Integration/data/manual/FinalClass.php b/tests/Integration/data/manual/FinalClass.php new file mode 100644 index 0000000..538a028 --- /dev/null +++ b/tests/Integration/data/manual/FinalClass.php @@ -0,0 +1,31 @@ +intValue; + } + + public function setIntValue(int $intValue): void + { + $this->intValue = $intValue; + } + + public function getProperty(): FinalClass + { + return $this->property; + } + + public function setProperty(FinalClass $property): void + { + $this->property = $property; + } +} From bfad4a1ad84fe8614b8e4ed94034e7ecea449602 Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Mon, 16 Sep 2024 14:45:39 +0200 Subject: [PATCH 2/4] Pass class-string instead of `Type` --- src/Constraint/AccessorPairConstraint.php | 5 +++-- src/Constraint/ConstraintConfig.php | 6 +++--- tests/Integration/AccessorPairAsserterTest.php | 8 +------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Constraint/AccessorPairConstraint.php b/src/Constraint/AccessorPairConstraint.php index 5859f73..d9ec592 100644 --- a/src/Constraint/AccessorPairConstraint.php +++ b/src/Constraint/AccessorPairConstraint.php @@ -13,6 +13,7 @@ use Doctrine\Inflector\InflectorFactory; use Exception; use LogicException; +use phpDocumentor\Reflection\Types\Object_; use PHPUnit\Framework\Constraint\Constraint; use ReflectionClass; use ReflectionMethod; @@ -284,8 +285,8 @@ protected function getTestValues(ReflectionMethod $method, ReflectionParameter $ $typehint = $resolver->getParamTypehint($parameter); $valueProvider = $this->config->getValueProvider(); - if ($valueProvider !== null) { - $value = $valueProvider($typehint); + if ($valueProvider !== null && $typehint instanceof Object_) { + $value = $valueProvider(ltrim((string)$typehint->getFqsen(), '\\')); if ($value !== null) { return [$value]; } diff --git a/src/Constraint/ConstraintConfig.php b/src/Constraint/ConstraintConfig.php index 1f37f71..e087981 100644 --- a/src/Constraint/ConstraintConfig.php +++ b/src/Constraint/ConstraintConfig.php @@ -25,7 +25,7 @@ class ConstraintConfig /** @var null|callable(): mixed[] */ private $constructorCallback = null; - /** @var null|(callable(Type): mixed) */ + /** @var null|(callable(class-string): mixed) */ private $valueProvider = null; public function hasAccessorPairCheck(): bool @@ -142,10 +142,10 @@ public function getValueProvider(): ?callable /** * Callback function to allow for a custom value provider. For instance for final classes. The argument - * is the typehint of the value, the return value should be the provided value. Return null + * is the class-string of the value, the return value should be the provided value. Return null * to skip the value provider and use the default value providers. * - * @param callable(Type): mixed $valueProvider + * @param callable(class-string): mixed $valueProvider */ public function setValueProvider(callable $valueProvider): self { diff --git a/tests/Integration/AccessorPairAsserterTest.php b/tests/Integration/AccessorPairAsserterTest.php index 8eaaa0d..d220ed7 100644 --- a/tests/Integration/AccessorPairAsserterTest.php +++ b/tests/Integration/AccessorPairAsserterTest.php @@ -200,13 +200,7 @@ public function testIntersectionClassProperty(): void public function testFinalClassWithCustomValueProvider(): void { $config = new ConstraintConfig(); - $config->setValueProvider(static function (Type $type) { - if ($type instanceof Object_ && (string)$type->getFqsen() !== FinalClass::class) { - return new FinalClass(); - } - - return null; - }); + $config->setValueProvider(static fn(string $class) => $class === FinalClass::class ? new FinalClass() : null); static::assertAccessorPairs(FinalClass::class, $config); } From dcb9b4679f5946b831577a80747bd4b9a95b4e51 Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Mon, 16 Sep 2024 14:47:50 +0200 Subject: [PATCH 3/4] Pass class-string instead of `Type` --- src/Constraint/ConstraintConfig.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Constraint/ConstraintConfig.php b/src/Constraint/ConstraintConfig.php index e087981..06d7d63 100644 --- a/src/Constraint/ConstraintConfig.php +++ b/src/Constraint/ConstraintConfig.php @@ -25,7 +25,7 @@ class ConstraintConfig /** @var null|callable(): mixed[] */ private $constructorCallback = null; - /** @var null|(callable(class-string): mixed) */ + /** @var null|(callable(class-string): ?object) */ private $valueProvider = null; public function hasAccessorPairCheck(): bool @@ -145,7 +145,7 @@ public function getValueProvider(): ?callable * is the class-string of the value, the return value should be the provided value. Return null * to skip the value provider and use the default value providers. * - * @param callable(class-string): mixed $valueProvider + * @param callable(class-string): ?object $valueProvider */ public function setValueProvider(callable $valueProvider): self { From a0e4daccefcca3fbcdc7f9dcd638ca70622b0293 Mon Sep 17 00:00:00 2001 From: Frank Dekker Date: Mon, 16 Sep 2024 17:06:37 +0200 Subject: [PATCH 4/4] Cleanup unused imports --- src/Constraint/ConstraintConfig.php | 2 -- tests/Integration/AccessorPairAsserterTest.php | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/Constraint/ConstraintConfig.php b/src/Constraint/ConstraintConfig.php index 06d7d63..5663db4 100644 --- a/src/Constraint/ConstraintConfig.php +++ b/src/Constraint/ConstraintConfig.php @@ -3,8 +3,6 @@ namespace DigitalRevolution\AccessorPairConstraint\Constraint; -use phpDocumentor\Reflection\Type; - class ConstraintConfig { /** @var bool */ diff --git a/tests/Integration/AccessorPairAsserterTest.php b/tests/Integration/AccessorPairAsserterTest.php index d220ed7..042b086 100644 --- a/tests/Integration/AccessorPairAsserterTest.php +++ b/tests/Integration/AccessorPairAsserterTest.php @@ -14,8 +14,6 @@ use DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\manual\UnionProperty; use DigitalRevolution\AccessorPairConstraint\Tests\TestCase; use Generator; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\Types\Object_; use PHPUnit\Framework\ExpectationFailedException; use ReflectionException; use TypeError;