Skip to content

Commit

Permalink
misc: introduce functions container to wrap definition handling
Browse files Browse the repository at this point in the history
  • Loading branch information
romm committed Mar 17, 2022
1 parent fdef930 commit fd11177
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 126 deletions.
20 changes: 20 additions & 0 deletions src/Definition/Exception/CallbackNotFound.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Definition\Exception;

use CuyZ\Valinor\Definition\FunctionDefinition;
use RuntimeException;

/** @internal */
final class CallbackNotFound extends RuntimeException
{
public function __construct(FunctionDefinition $function)
{
parent::__construct(
"The callback associated to `{$function->signature()}` could not be found.",
1647523495
);
}
}
22 changes: 22 additions & 0 deletions src/Definition/Exception/FunctionNotFound.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Definition\Exception;

use RuntimeException;

/** @internal */
final class FunctionNotFound extends RuntimeException
{
/**
* @param string|int $key
*/
public function __construct($key)
{
parent::__construct(
"The function `$key` was not found.",
1647523444
);
}
}
89 changes: 89 additions & 0 deletions src/Definition/FunctionsContainer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace CuyZ\Valinor\Definition;

use CuyZ\Valinor\Definition\Exception\CallbackNotFound;
use CuyZ\Valinor\Definition\Exception\FunctionNotFound;
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
use IteratorAggregate;
use Traversable;

use function array_keys;

/**
* @internal
*
* @implements IteratorAggregate<FunctionDefinition>
*/
final class FunctionsContainer implements IteratorAggregate
{
private FunctionDefinitionRepository $functionDefinitionRepository;

/** @var array<callable> */
private array $callables;

/** @var array<array{definition: FunctionDefinition, callback: callable}> */
private array $functions = [];

/**
* @param array<callable> $callables
*/
public function __construct(FunctionDefinitionRepository $functionDefinitionRepository, array $callables)
{
$this->functionDefinitionRepository = $functionDefinitionRepository;
$this->callables = $callables;
}

/**
* @param string|int $key
*/
public function has($key): bool
{
return isset($this->callables[$key]);
}

/**
* @param string|int $key
*/
public function get($key): FunctionDefinition
{
if (! $this->has($key)) {
throw new FunctionNotFound($key);
}

return $this->function($key)['definition'];
}

public function callback(FunctionDefinition $function): callable
{
foreach ($this->functions as $data) {
if ($function === $data['definition']) {
return $data['callback'];
}
}

throw new CallbackNotFound($function);
}

public function getIterator(): Traversable
{
foreach (array_keys($this->callables) as $key) {
yield $key => $this->function($key)['definition'];
}
}

/**
* @param string|int $key
* @return array{definition: FunctionDefinition, callback: callable}
*/
private function function($key): array
{
/** @infection-ignore-all */
return $this->functions[$key] ??= [
'callback' => $this->callables[$key],
'definition' => $this->functionDefinitionRepository->for($this->callables[$key]),
];
}
}
19 changes: 13 additions & 6 deletions src/Library/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use CuyZ\Valinor\Cache\VersionedCache;
use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Definition\FunctionDefinition;
use CuyZ\Valinor\Definition\FunctionsContainer;
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Definition\Repository\Cache\CacheClassDefinitionRepository;
use CuyZ\Valinor\Definition\Repository\Cache\CacheFunctionDefinitionRepository;
Expand Down Expand Up @@ -124,17 +125,21 @@ public function __construct(Settings $settings)

$builder = new InterfaceNodeBuilder(
$builder,
$this->get(FunctionDefinitionRepository::class),
new FunctionsContainer(
$this->get(FunctionDefinitionRepository::class),
$settings->interfaceMapping
),
$this->get(TypeParser::class),
$settings->interfaceMapping,
);

$builder = new CasterProxyNodeBuilder($builder);
$builder = new VisitorNodeBuilder($builder, $settings->nodeVisitors);
$builder = new ValueAlteringNodeBuilder(
$builder,
$this->get(FunctionDefinitionRepository::class),
$settings->valueModifier
new FunctionsContainer(
$this->get(FunctionDefinitionRepository::class),
$settings->valueModifier
)
);
$builder = new ShellVisitorNodeBuilder($builder, $this->get(ShellVisitor::class));

Expand All @@ -150,9 +155,11 @@ public function __construct(Settings $settings)

$factory = new ObjectBindingBuilderFactory(
$factory,
$this->get(FunctionDefinitionRepository::class),
$this->get(ObjectBuilderFilterer::class),
$settings->objectBinding,
new FunctionsContainer(
$this->get(FunctionDefinitionRepository::class),
$settings->objectBinding
)
);

return new AttributeObjectBuilderFactory($factory);
Expand Down
3 changes: 3 additions & 0 deletions src/Library/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace CuyZ\Valinor\Library;

use CuyZ\Valinor\Mapper\Tree\Node;
use DateTimeImmutable;
use DateTimeInterface;

use function sys_get_temp_dir;

Expand All @@ -30,5 +32,6 @@ final class Settings
public function __construct()
{
$this->cacheDir = sys_get_temp_dir();
$this->interfaceMapping[DateTimeInterface::class] = static fn () => DateTimeImmutable::class;
}
}
40 changes: 6 additions & 34 deletions src/Mapper/Object/Factory/ObjectBindingBuilderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
namespace CuyZ\Valinor\Mapper\Object\Factory;

use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Definition\FunctionDefinition;
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
use CuyZ\Valinor\Definition\FunctionsContainer;
use CuyZ\Valinor\Mapper\Object\CallbackObjectBuilder;
use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
use CuyZ\Valinor\Mapper\Object\ObjectBuilderFilterer;
Expand All @@ -16,38 +15,27 @@ final class ObjectBindingBuilderFactory implements ObjectBuilderFactory
{
private ObjectBuilderFactory $delegate;

private FunctionDefinitionRepository $functionDefinitionRepository;

private ObjectBuilderFilterer $objectBuilderFilterer;

/** @var list<callable> */
private array $callbacks;

/** @var list<FunctionDefinition> */
private array $functions;
private FunctionsContainer $functions;

/**
* @param list<callable> $callbacks
*/
public function __construct(
ObjectBuilderFactory $delegate,
FunctionDefinitionRepository $functionDefinitionRepository,
ObjectBuilderFilterer $objectBuilderFilterer,
array $callbacks
FunctionsContainer $functions
) {
$this->delegate = $delegate;
$this->functionDefinitionRepository = $functionDefinitionRepository;
$this->objectBuilderFilterer = $objectBuilderFilterer;
$this->callbacks = $callbacks;
$this->functions = $functions;
}

public function for(ClassDefinition $class, $source): ObjectBuilder
{
$builders = [];

foreach ($this->functions() as $key => $function) {
foreach ($this->functions as $function) {
if ($function->returnType()->matches($class->type())) {
$builders[] = new CallbackObjectBuilder($function, $this->callbacks[$key]);
$builders[] = new CallbackObjectBuilder($function, $this->functions->callback($function));
}
}

Expand All @@ -57,20 +45,4 @@ public function for(ClassDefinition $class, $source): ObjectBuilder

return $this->objectBuilderFilterer->filter($source, ...$builders);
}

/**
* @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;
}
}
40 changes: 11 additions & 29 deletions src/Mapper/Tree/Builder/InterfaceNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace CuyZ\Valinor\Mapper\Tree\Builder;

use CuyZ\Valinor\Definition\FunctionDefinition;
use CuyZ\Valinor\Definition\FunctionsContainer;
use CuyZ\Valinor\Definition\Parameters;
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
use CuyZ\Valinor\Mapper\Object\Exception\InvalidSourceForInterface;
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidInterfaceResolverReturnType;
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidTypeResolvedForInterface;
Expand All @@ -18,40 +18,22 @@
use CuyZ\Valinor\Type\Resolver\Exception\CannotResolveTypeFromInterface;
use CuyZ\Valinor\Type\Types\ClassType;
use CuyZ\Valinor\Type\Types\InterfaceType;
use DateTimeImmutable;
use DateTimeInterface;
use Exception;

/** @internal */
final class InterfaceNodeBuilder implements NodeBuilder
{
private NodeBuilder $delegate;

private TypeParser $typeParser;

private FunctionDefinitionRepository $functionDefinitionRepository;
private FunctionsContainer $functions;

/** @var array<interface-string, callable> */
private array $callbacks;

/** @var array<interface-string, FunctionDefinition> */
private array $functions;
private TypeParser $typeParser;

/**
* @param array<interface-string, callable> $interfaceMapping
*/
public function __construct(
NodeBuilder $delegate,
FunctionDefinitionRepository $functionDefinitionRepository,
TypeParser $typeParser,
array $interfaceMapping
) {
public function __construct(NodeBuilder $delegate, FunctionsContainer $functions, TypeParser $typeParser)
{
$this->delegate = $delegate;
$this->functionDefinitionRepository = $functionDefinitionRepository;
$this->functions = $functions;
$this->typeParser = $typeParser;

$this->callbacks = $interfaceMapping;
$this->callbacks[DateTimeInterface::class] ??= static fn () => DateTimeImmutable::class;
}

public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
Expand All @@ -64,14 +46,14 @@ public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node

$interfaceName = $type->className();

if (! isset($this->callbacks[$interfaceName])) {
if (! $this->functions->has($interfaceName)) {
throw new CannotResolveTypeFromInterface($interfaceName);
}

/** @infection-ignore-all */
$this->functions[$interfaceName] ??= $this->functionDefinitionRepository->for($this->callbacks[$interfaceName]);
$function = $this->functions->get($interfaceName);
$callback = $this->functions->callback($function);

$children = $this->children($shell, $this->functions[$interfaceName], $rootBuilder);
$children = $this->children($shell, $function, $rootBuilder);
$arguments = [];

foreach ($children as $child) {
Expand All @@ -83,7 +65,7 @@ public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
}

try {
$signature = ($this->callbacks[$interfaceName])(...$arguments);
$signature = $callback(...$arguments);
} catch (Exception $exception) {
throw ThrowableMessage::from($exception);
}
Expand Down
Loading

0 comments on commit fd11177

Please sign in to comment.