Skip to content

Commit

Permalink
Merge pull request #61 from frankdekker/Add-custom-value-provider-for…
Browse files Browse the repository at this point in the history
…-types

Add custom value provider for types
  • Loading branch information
frankdekker authored Sep 16, 2024
2 parents f967dfc + a0e4dac commit 973706c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 2 deletions.
9 changes: 9 additions & 0 deletions src/Constraint/AccessorPairConstraint.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}

Expand Down
22 changes: 22 additions & 0 deletions src/Constraint/ConstraintConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <code>null</code>
* 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;
}
}
11 changes: 9 additions & 2 deletions tests/Integration/AccessorPairAsserterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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<string, array<object>>
* @throws ReflectionException
Expand Down
31 changes: 31 additions & 0 deletions tests/Integration/data/manual/FinalClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace DigitalRevolution\AccessorPairConstraint\Tests\Integration\data\manual;

final class FinalClass
{
private int $intValue;
private FinalClass $property;

public function getIntValue(): int
{
return $this->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;
}
}

0 comments on commit 973706c

Please sign in to comment.