Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
CallableFactory
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
3 / 3
6
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __invoke
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getArguments
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3declare(strict_types=1);
4
5/**
6 * This file is part of php-fast-forward/container.
7 *
8 * This source file is subject to the license bundled
9 * with this source code in the file LICENSE.
10 *
11 * @copyright Copyright (c) 2025-2026 Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
12 * @license   https://opensource.org/licenses/MIT MIT License
13 *
14 * @see       https://github.com/php-fast-forward/container
15 * @see       https://github.com/php-fast-forward
16 * @see       https://datatracker.ietf.org/doc/html/rfc2119
17 */
18
19namespace FastForward\Container\Factory;
20
21use Closure;
22use ReflectionFunction;
23use ReflectionType;
24use FastForward\Container\Exception\RuntimeException;
25use Psr\Container\ContainerInterface;
26
27/**
28 * A factory that wraps a user-provided callable and executes it when invoked.
29 *
30 * The callable MUST accept a PSR-11 ContainerInterface as its first and only argument.
31 * This factory SHALL be used when the construction logic must be fully delegated to a closure.
32 *
33 * This class allows dynamic resolution of services using the container context.
34 */
35  class CallableFactory implements FactoryInterface
36{
37    /**
38     * @var Closure The user-defined factory callable.
39     *              This callable MUST accept a ContainerInterface and return a service instance.
40     */
41    private Closure $callable;
42
43    /**
44     * Constructs a CallableFactory instance.
45     *
46     * @param callable $callable a callable that returns a service instance and accepts a container
47     */
48    public function __construct(callable $callable)
49    {
50        $this->callable = $callable(...);
51    }
52
53    /**
54     * Invokes the factory to create a service.
55     *
56     * @param ContainerInterface $container the PSR-11 container for dependency resolution
57     *
58     * @return mixed the constructed service instance
59     */
60    public function __invoke(ContainerInterface $container): mixed
61    {
62        $arguments = $this->getArguments($container, new ReflectionFunction($this->callable));
63
64        return \call_user_func_array($this->callable, $arguments);
65    }
66
67    /**
68     * Retrieves the arguments for the callable from the container.
69     *
70     * @param ContainerInterface $container the PSR-11 container for dependency resolution
71     * @param ReflectionFunction $function the reflection function of the callable
72     *
73     * @return array the resolved arguments for the callable
74     */
75    private function getArguments(ContainerInterface $container, ReflectionFunction $function): array
76    {
77        $arguments = [];
78
79        foreach ($function->getParameters() as $parameter) {
80            if (! $parameter->getType() instanceof ReflectionType || $parameter->getType()->isBuiltin()) {
81                throw RuntimeException::forInvalidParameterType($parameter->getName());
82            }
83
84            $className   = $parameter->getType()
85                ->getName();
86            $arguments[] = $container->get($className);
87        }
88
89        return $arguments;
90    }
91}