mirror of
https://github.com/danog/Valinor.git
synced 2024-11-26 20:24:40 +01:00
feat: improve value altering API
This commit is contained in:
parent
380961247e
commit
422e6a8b27
@ -9,14 +9,19 @@ use CuyZ\Valinor\Cache\Compiled\CompiledPhpFileCache;
|
||||
use CuyZ\Valinor\Cache\RuntimeCache;
|
||||
use CuyZ\Valinor\Cache\VersionedCache;
|
||||
use CuyZ\Valinor\Definition\ClassDefinition;
|
||||
use CuyZ\Valinor\Definition\FunctionDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Cache\CacheClassDefinitionRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Cache\CacheFunctionDefinitionRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\ClassDefinitionCompiler;
|
||||
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\FunctionDefinitionCompiler;
|
||||
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\CombinedAttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\DoctrineAnnotationsRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\NativeAttributesRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\ReflectionClassDefinitionRepository;
|
||||
use CuyZ\Valinor\Definition\Repository\Reflection\ReflectionFunctionDefinitionRepository;
|
||||
use CuyZ\Valinor\Mapper\Object\Factory\AttributeObjectBuilderFactory;
|
||||
use CuyZ\Valinor\Mapper\Object\Factory\ConstructorObjectBuilderFactory;
|
||||
use CuyZ\Valinor\Mapper\Object\Factory\DateTimeObjectBuilderFactory;
|
||||
@ -127,7 +132,11 @@ final class Container
|
||||
|
||||
$builder = new CasterProxyNodeBuilder($builder);
|
||||
$builder = new VisitorNodeBuilder($builder, $settings->nodeVisitors);
|
||||
$builder = new ValueAlteringNodeBuilder($builder, $settings->valueModifier);
|
||||
$builder = new ValueAlteringNodeBuilder(
|
||||
$builder,
|
||||
$this->get(FunctionDefinitionRepository::class),
|
||||
$settings->valueModifier
|
||||
);
|
||||
$builder = new ShellVisitorNodeBuilder($builder, $this->get(ShellVisitor::class));
|
||||
|
||||
return new ErrorCatcherNodeBuilder($builder);
|
||||
@ -158,6 +167,19 @@ final class Container
|
||||
return new CacheClassDefinitionRepository($repository, $cache);
|
||||
},
|
||||
|
||||
FunctionDefinitionRepository::class => function () use ($settings): FunctionDefinitionRepository {
|
||||
$repository = new ReflectionFunctionDefinitionRepository(
|
||||
$this->get(TypeParserFactory::class),
|
||||
$this->get(AttributesRepository::class),
|
||||
);
|
||||
|
||||
/** @var CacheInterface<FunctionDefinition> $cache */
|
||||
$cache = new CompiledPhpFileCache($settings->cacheDir, new FunctionDefinitionCompiler());
|
||||
$cache = $this->wrapCache($cache);
|
||||
|
||||
return new CacheFunctionDefinitionRepository($repository, $cache);
|
||||
},
|
||||
|
||||
AttributesRepository::class => function () use ($settings): AttributesRepository {
|
||||
if (! $settings->enableLegacyDoctrineAnnotations) {
|
||||
return new NativeAttributesRepository();
|
||||
|
@ -18,10 +18,7 @@ final class Settings
|
||||
/** @var array<string, callable(mixed): object> */
|
||||
public array $objectBinding = [];
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @var array<string, array<callable(T): T>>
|
||||
*/
|
||||
/** @var list<callable> */
|
||||
public array $valueModifier = [];
|
||||
|
||||
/** @var array<callable(Node): void> */
|
||||
|
@ -4,28 +4,35 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Definition\FunctionDefinition;
|
||||
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
/** @internal */
|
||||
final class ValueAlteringNodeBuilder implements NodeBuilder
|
||||
{
|
||||
private NodeBuilder $delegate;
|
||||
|
||||
/** @var array<string, array<callable(T): T>> */
|
||||
private array $valueModifiers;
|
||||
private FunctionDefinitionRepository $functionDefinitionRepository;
|
||||
|
||||
/** @var list<callable> */
|
||||
private array $callbacks;
|
||||
|
||||
/** @var list<FunctionDefinition> */
|
||||
private array $functions;
|
||||
|
||||
/**
|
||||
* @param array<string, array<callable(T): T>> $valueModifiers
|
||||
* @param list<callable> $callbacks
|
||||
*/
|
||||
public function __construct(NodeBuilder $delegate, array $valueModifiers)
|
||||
{
|
||||
public function __construct(
|
||||
NodeBuilder $delegate,
|
||||
FunctionDefinitionRepository $functionDefinitionRepository,
|
||||
array $callbacks
|
||||
) {
|
||||
$this->delegate = $delegate;
|
||||
$this->valueModifiers = $valueModifiers;
|
||||
$this->functionDefinitionRepository = $functionDefinitionRepository;
|
||||
$this->callbacks = $callbacks;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
@ -36,15 +43,38 @@ final class ValueAlteringNodeBuilder implements NodeBuilder
|
||||
return $node;
|
||||
}
|
||||
|
||||
/** @var T $value */
|
||||
$value = $node->value();
|
||||
$type = (string)$node->type();
|
||||
$type = $node->type();
|
||||
|
||||
foreach ($this->valueModifiers[$type] ?? [] as $valueModifier) {
|
||||
$value = $valueModifier($value);
|
||||
$node = $node->withValue($value);
|
||||
foreach ($this->functions() as $key => $function) {
|
||||
$parameters = $function->parameters();
|
||||
|
||||
if (count($parameters) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($parameters->at(0)->type()->matches($type)) {
|
||||
$value = ($this->callbacks[$key])($value);
|
||||
$node = $node->withValue($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FunctionDefinition[]
|
||||
*/
|
||||
private function functions(): array
|
||||
{
|
||||
if (! isset($this->functions)) {
|
||||
$this->functions = [];
|
||||
|
||||
foreach ($this->callbacks as $key => $callback) {
|
||||
$this->functions[$key] = $this->functionDefinitionRepository->for($callback);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->functions;
|
||||
}
|
||||
}
|
||||
|
@ -66,27 +66,8 @@ final class MapperBuilder
|
||||
*/
|
||||
public function alter(callable $callback): self
|
||||
{
|
||||
$reflection = Reflection::ofCallable($callback);
|
||||
|
||||
if ($reflection->getNumberOfParameters() === 0) {
|
||||
throw new LogicException('One parameter is required for this callable.');
|
||||
}
|
||||
|
||||
$parameter = $reflection->getParameters()[0];
|
||||
$nativeType = $parameter->getType();
|
||||
$typeFromDocBlock = Reflection::docBlockType($parameter);
|
||||
|
||||
if ($typeFromDocBlock) {
|
||||
$type = $typeFromDocBlock;
|
||||
} elseif ($nativeType) {
|
||||
$type = Reflection::flattenType($nativeType);
|
||||
} else {
|
||||
throw new LogicException('No type was found for the parameter of this callable.');
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
$clone->settings->valueModifier[$type] ??= [];
|
||||
$clone->settings->valueModifier[$type][] = $callback;
|
||||
$clone->settings->valueModifier[] = $callback;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ final class ValueAlteringMappingTest extends IntegrationTest
|
||||
{
|
||||
try {
|
||||
$result = $this->mapperBuilder
|
||||
->alter(fn () => 'bar')
|
||||
->alter(fn (string $value) => strtolower($value))
|
||||
->alter(fn (string $value) => strtoupper($value))
|
||||
->alter(/** @param string $value */ fn ($value) => $value . '!')
|
||||
|
@ -54,22 +54,4 @@ final class MapperBuilderTest extends TestCase
|
||||
$this->mapperBuilder->bind(static function () {
|
||||
});
|
||||
}
|
||||
|
||||
public function test_alter_with_callable_with_no_parameter_throws_exception(): void
|
||||
{
|
||||
$this->expectException(LogicException::class);
|
||||
$this->expectExceptionMessage('One parameter is required for this callable.');
|
||||
|
||||
$this->mapperBuilder->alter(static function (): void {
|
||||
});
|
||||
}
|
||||
|
||||
public function test_alter_with_callable_with_parameter_with_no_type_throws_exception(): void
|
||||
{
|
||||
$this->expectException(LogicException::class);
|
||||
$this->expectExceptionMessage('No type was found for the parameter of this callable.');
|
||||
|
||||
$this->mapperBuilder->alter(static function ($foo): void {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user