Skip to content

Commit

Permalink
feat: improve value altering API
Browse files Browse the repository at this point in the history
  • Loading branch information
romm committed Feb 19, 2022
1 parent 3809612 commit 422e6a8
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 59 deletions.
24 changes: 23 additions & 1 deletion src/Library/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@
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;
Expand Down Expand Up @@ -127,7 +132,11 @@ public function __construct(Settings $settings)

$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);
Expand Down Expand Up @@ -158,6 +167,19 @@ public function __construct(Settings $settings)
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();
Expand Down
5 changes: 1 addition & 4 deletions src/Library/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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> */
Expand Down
62 changes: 46 additions & 16 deletions src/Mapper/Tree/Builder/ValueAlteringNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,35 @@

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
Expand All @@ -36,15 +43,38 @@ public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
return $node;
}

/** @var T $value */
$value = $node->value();
$type = (string)$node->type();
$type = $node->type();

foreach ($this->functions() as $key => $function) {
$parameters = $function->parameters();

foreach ($this->valueModifiers[$type] ?? [] as $valueModifier) {
$value = $valueModifier($value);
$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 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;
}
}
21 changes: 1 addition & 20 deletions src/MapperBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,8 @@ public function bind(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->settings->valueModifier[$type] ??= [];
$clone->settings->valueModifier[$type][] = $callback;
$clone->settings->valueModifier[] = $callback;

return $clone;
}
Expand Down
1 change: 1 addition & 0 deletions tests/Integration/Mapping/ValueAlteringMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function test_alter_string_alters_value(): void
{
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 . '!')
Expand Down
18 changes: 0 additions & 18 deletions tests/Unit/MapperBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,4 @@ public function test_bind_with_callable_with_no_return_type_throws_exception():
$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 {
});
}
}

0 comments on commit 422e6a8

Please sign in to comment.