diff --git a/src/InterfaceResolver.php b/src/InterfaceResolver.php new file mode 100644 index 00000000..4e10a4b4 --- /dev/null +++ b/src/InterfaceResolver.php @@ -0,0 +1,25 @@ +|null $props + * @return class-string|null + */ + public function resolve(string $interface, ?array $props): ?string; + + /** + * @return string[] + */ + public function getResolverProps(string $interface): array; + + /** + * @return array + */ + public function transform(object $input, callable $next): array; +} diff --git a/src/Library/Container.php b/src/Library/Container.php index 488f9d0d..68caf4a7 100644 --- a/src/Library/Container.php +++ b/src/Library/Container.php @@ -128,6 +128,7 @@ public function __construct(Settings $settings) $this->get(FunctionDefinitionRepository::class), $settings->customConstructors ), + $settings->interfaceResolver, ); $builder = new CasterProxyNodeBuilder($builder); diff --git a/src/Library/Settings.php b/src/Library/Settings.php index 028556c2..50cb39ee 100644 --- a/src/Library/Settings.php +++ b/src/Library/Settings.php @@ -4,6 +4,7 @@ namespace CuyZ\Valinor\Library; +use CuyZ\Valinor\InterfaceResolver; use CuyZ\Valinor\Mapper\Object\Constructor; use CuyZ\Valinor\Mapper\Object\DynamicConstructor; use CuyZ\Valinor\Mapper\Tree\Message\ErrorMessage; @@ -59,6 +60,8 @@ final class Settings /** @var array */ public array $transformerAttributes = []; + public ?InterfaceResolver $interfaceResolver = null; + public function __construct() { $this->inferredMapping[DateTimeInterface::class] = static fn () => DateTimeImmutable::class; diff --git a/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php b/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php index 17b5a449..263f6ed4 100644 --- a/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php +++ b/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php @@ -6,6 +6,7 @@ use CuyZ\Valinor\Definition\FunctionsContainer; use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository; +use CuyZ\Valinor\InterfaceResolver; use CuyZ\Valinor\Mapper\Object\Arguments; use CuyZ\Valinor\Mapper\Object\ArgumentsValues; use CuyZ\Valinor\Mapper\Object\Exception\InvalidSource; @@ -27,6 +28,7 @@ public function __construct( private ObjectImplementations $implementations, private ClassDefinitionRepository $classDefinitionRepository, private FunctionsContainer $constructors, + private ?InterfaceResolver $interfaceResolver, ) {} public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode @@ -53,6 +55,21 @@ public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode if (! $this->implementations->has($className)) { if ($type instanceof InterfaceType || $this->classDefinitionRepository->for($type)->isAbstract) { + if ($this->interfaceResolver) { + $value = $shell->value(); + if (is_array($value)) { + $resolver_props = []; + foreach($this->interfaceResolver->getResolverProps($className) as $prop) { + $resolver_props[$prop] = $value[$prop]; + unset($value[$prop]); + } + $resolvedClassName = $this->interfaceResolver->resolve($className, $resolver_props); + if ($resolvedClassName !== null) { + $shell = $shell->withType(new NativeClassType($resolvedClassName))->withValue($value); + return $this->delegate->build($shell, $rootBuilder); + } + } + } throw new CannotResolveObjectType($className); } diff --git a/src/MapperBuilder.php b/src/MapperBuilder.php index 48ffecc2..274762a4 100644 --- a/src/MapperBuilder.php +++ b/src/MapperBuilder.php @@ -530,6 +530,15 @@ public function registerTransformer(callable|string $transformer, int $priority return $clone; } + public function withInterfaceResolver(InterfaceResolver $resolver): self + { + + $clone = clone $this; + $clone->settings->interfaceResolver = $resolver; + + return $clone->registerTransformer($resolver->transform(...)); + } + /** * Warms up the injected cache implementation with the provided class names. *