diff --git a/src/Constraint/AccessorPairConstraint.php b/src/Constraint/AccessorPairConstraint.php index 7402021..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; @@ -283,6 +284,14 @@ protected function getTestValues(ReflectionMethod $method, ReflectionParameter $ $resolver = new TypehintResolver($method); $typehint = $resolver->getParamTypehint($parameter); + $valueProvider = $this->config->getValueProvider(); + if ($valueProvider !== null && $typehint instanceof Object_) { + $value = $valueProvider(ltrim((string)$typehint->getFqsen(), '\\')); + 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..5663db4 100644 --- a/src/Constraint/ConstraintConfig.php +++ b/src/Constraint/ConstraintConfig.php @@ -23,6 +23,9 @@ class ConstraintConfig /** @var null|callable(): mixed[] */ private $constructorCallback = null; + /** @var null|(callable(class-string): ?object) */ + private $valueProvider = null; + public function hasAccessorPairCheck(): bool { return $this->assertAccessorPair; @@ -129,4 +132,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 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): ?object $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..042b086 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; @@ -101,7 +102,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 +119,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 +195,14 @@ public function testIntersectionClassProperty(): void static::assertAccessorPairs(IntersectionClassProperty::class); } + public function testFinalClassWithCustomValueProvider(): void + { + $config = new ConstraintConfig(); + $config->setValueProvider(static fn(string $class) => $class === FinalClass::class ? new FinalClass() : 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; + } +}