Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
InvokableFactory
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
2 / 2
4
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%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
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 Psr\Container\ContainerExceptionInterface;
22use Psr\Container\ContainerInterface;
23use Psr\Container\NotFoundExceptionInterface;
24
25/**
26 * Factory responsible for instantiating a class with constructor arguments.
27 *
28 * This class MUST be used when a service should be created using a known class name
29 * and optionally injected with fixed arguments.
30 *
31 * It SHALL invoke the constructor directly using the spread operator and provided arguments.
32 * This factory is suitable for services that do not require container-based dependency injection.
33 */
34  class InvokableFactory implements FactoryInterface
35{
36    /**
37     * @var array<int, mixed> The list of arguments to pass to the class constructor.
38     *                        This MAY be empty if the constructor takes no arguments.
39     */
40    private array $arguments;
41
42    /**
43     * Constructs the InvokableFactory with a target class and optional constructor arguments.
44     *
45     * This constructor MUST receive a valid, instantiable class name. Any variadic arguments
46     * provided SHALL be passed to the class constructor during instantiation. If an argument
47     * is a string and matches a service ID in the container, it SHALL be resolved from the container.
48     *
49     * @param string $class the fully qualified class name to be instantiated
50     * @param mixed ...$arguments A variadic list of constructor arguments.
51     */
52    public function __construct(
53        private string $class,
54        mixed ...$arguments
55    ) {
56        $this->arguments = $arguments;
57    }
58
59    /**
60     * Creates an instance of the class with the provided arguments.
61     *
62     * Arguments that are strings and match a known service ID in the container
63     * SHALL be replaced with the corresponding container-resolved services.
64     *
65     * It MUST return a new instance of the class with the resolved arguments.
66     * This method SHALL be invoked by the container when the service is requested.
67     *
68     * @param ContainerInterface $container The PSR-11 compliant container providing dependencies
69     *
70     * @return mixed The created service instance
71     *
72     * @throws ContainerExceptionInterface thrown if an error occurs during service instantiation
73     * @throws NotFoundExceptionInterface thrown if a required service ID is not found in the container
74     */
75    public function __invoke(ContainerInterface $container): mixed
76    {
77        $arguments = array_map(
78            static fn($argument) => \is_string($argument) && $container->has($argument)
79                ? $container->get($argument)
80                : $argument,
81            $this->arguments
82        );
83
84        return new ($this->class)(...$arguments);
85    }
86}