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\RuntimeCache;
|
||||||
use CuyZ\Valinor\Cache\VersionedCache;
|
use CuyZ\Valinor\Cache\VersionedCache;
|
||||||
use CuyZ\Valinor\Definition\ClassDefinition;
|
use CuyZ\Valinor\Definition\ClassDefinition;
|
||||||
|
use CuyZ\Valinor\Definition\FunctionDefinition;
|
||||||
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
|
||||||
use CuyZ\Valinor\Definition\Repository\Cache\CacheClassDefinitionRepository;
|
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\ClassDefinitionCompiler;
|
||||||
|
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\FunctionDefinitionCompiler;
|
||||||
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
|
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\CombinedAttributesRepository;
|
||||||
use CuyZ\Valinor\Definition\Repository\Reflection\DoctrineAnnotationsRepository;
|
use CuyZ\Valinor\Definition\Repository\Reflection\DoctrineAnnotationsRepository;
|
||||||
use CuyZ\Valinor\Definition\Repository\Reflection\NativeAttributesRepository;
|
use CuyZ\Valinor\Definition\Repository\Reflection\NativeAttributesRepository;
|
||||||
use CuyZ\Valinor\Definition\Repository\Reflection\ReflectionClassDefinitionRepository;
|
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\AttributeObjectBuilderFactory;
|
||||||
use CuyZ\Valinor\Mapper\Object\Factory\ConstructorObjectBuilderFactory;
|
use CuyZ\Valinor\Mapper\Object\Factory\ConstructorObjectBuilderFactory;
|
||||||
use CuyZ\Valinor\Mapper\Object\Factory\DateTimeObjectBuilderFactory;
|
use CuyZ\Valinor\Mapper\Object\Factory\DateTimeObjectBuilderFactory;
|
||||||
@ -127,7 +132,11 @@ final class Container
|
|||||||
|
|
||||||
$builder = new CasterProxyNodeBuilder($builder);
|
$builder = new CasterProxyNodeBuilder($builder);
|
||||||
$builder = new VisitorNodeBuilder($builder, $settings->nodeVisitors);
|
$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));
|
$builder = new ShellVisitorNodeBuilder($builder, $this->get(ShellVisitor::class));
|
||||||
|
|
||||||
return new ErrorCatcherNodeBuilder($builder);
|
return new ErrorCatcherNodeBuilder($builder);
|
||||||
@ -158,6 +167,19 @@ final class Container
|
|||||||
return new CacheClassDefinitionRepository($repository, $cache);
|
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 {
|
AttributesRepository::class => function () use ($settings): AttributesRepository {
|
||||||
if (! $settings->enableLegacyDoctrineAnnotations) {
|
if (! $settings->enableLegacyDoctrineAnnotations) {
|
||||||
return new NativeAttributesRepository();
|
return new NativeAttributesRepository();
|
||||||
|
@ -18,10 +18,7 @@ final class Settings
|
|||||||
/** @var array<string, callable(mixed): object> */
|
/** @var array<string, callable(mixed): object> */
|
||||||
public array $objectBinding = [];
|
public array $objectBinding = [];
|
||||||
|
|
||||||
/**
|
/** @var list<callable> */
|
||||||
* @template T
|
|
||||||
* @var array<string, array<callable(T): T>>
|
|
||||||
*/
|
|
||||||
public array $valueModifier = [];
|
public array $valueModifier = [];
|
||||||
|
|
||||||
/** @var array<callable(Node): void> */
|
/** @var array<callable(Node): void> */
|
||||||
|
@ -4,28 +4,35 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
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\Node;
|
||||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||||
|
|
||||||
/**
|
/** @internal */
|
||||||
* @internal
|
|
||||||
*
|
|
||||||
* @template T
|
|
||||||
*/
|
|
||||||
final class ValueAlteringNodeBuilder implements NodeBuilder
|
final class ValueAlteringNodeBuilder implements NodeBuilder
|
||||||
{
|
{
|
||||||
private NodeBuilder $delegate;
|
private NodeBuilder $delegate;
|
||||||
|
|
||||||
/** @var array<string, array<callable(T): T>> */
|
private FunctionDefinitionRepository $functionDefinitionRepository;
|
||||||
private array $valueModifiers;
|
|
||||||
|
/** @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->delegate = $delegate;
|
||||||
$this->valueModifiers = $valueModifiers;
|
$this->functionDefinitionRepository = $functionDefinitionRepository;
|
||||||
|
$this->callbacks = $callbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||||
@ -36,15 +43,38 @@ final class ValueAlteringNodeBuilder implements NodeBuilder
|
|||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var T $value */
|
|
||||||
$value = $node->value();
|
$value = $node->value();
|
||||||
$type = (string)$node->type();
|
$type = $node->type();
|
||||||
|
|
||||||
foreach ($this->valueModifiers[$type] ?? [] as $valueModifier) {
|
foreach ($this->functions() as $key => $function) {
|
||||||
$value = $valueModifier($value);
|
$parameters = $function->parameters();
|
||||||
$node = $node->withValue($value);
|
|
||||||
|
if (count($parameters) === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parameters->at(0)->type()->matches($type)) {
|
||||||
|
$value = ($this->callbacks[$key])($value);
|
||||||
|
$node = $node->withValue($value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $node;
|
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
|
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 = clone $this;
|
||||||
$clone->settings->valueModifier[$type] ??= [];
|
$clone->settings->valueModifier[] = $callback;
|
||||||
$clone->settings->valueModifier[$type][] = $callback;
|
|
||||||
|
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ final class ValueAlteringMappingTest extends IntegrationTest
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$result = $this->mapperBuilder
|
$result = $this->mapperBuilder
|
||||||
|
->alter(fn () => 'bar')
|
||||||
->alter(fn (string $value) => strtolower($value))
|
->alter(fn (string $value) => strtolower($value))
|
||||||
->alter(fn (string $value) => strtoupper($value))
|
->alter(fn (string $value) => strtoupper($value))
|
||||||
->alter(/** @param string $value */ fn ($value) => $value . '!')
|
->alter(/** @param string $value */ fn ($value) => $value . '!')
|
||||||
|
@ -54,22 +54,4 @@ final class MapperBuilderTest extends TestCase
|
|||||||
$this->mapperBuilder->bind(static function () {
|
$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