Test Failed
Push — main ( 6ea8e5...17888d )
by Chema
03:03
created

FactoryManager::markAsFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Container;
6
7
use Closure;
8
use Gacela\Container\Exception\ContainerException;
9
use SplObjectStorage;
10
11
use function count;
12
use function is_array;
13
use function is_callable;
14
use function is_object;
15
16
/**
17
 * Manages factory instances, protected closures, and service extensions.
18
 */
19
final class FactoryManager
20
{
21
    private SplObjectStorage $factoryInstances;
22
23
    private SplObjectStorage $protectedInstances;
24
25
    private ?string $currentlyExtending = null;
26
27
    /**
28
     * @param array<string, list<Closure>> $instancesToExtend
29
     */
30
    public function __construct(
31
        private array $instancesToExtend = [],
32
    ) {
33
        $this->factoryInstances = new SplObjectStorage();
34
        $this->protectedInstances = new SplObjectStorage();
35
    }
36
37
    /**
38
     * Mark a closure as a factory (always creates new instances).
39
     */
40
    public function markAsFactory(Closure $instance): void
41
    {
42
        $this->factoryInstances->attach($instance);
43
    }
44
45
    /**
46
     * Mark a closure as protected (won't be invoked by container).
47
     */
48
    public function markAsProtected(Closure $instance): void
49
    {
50
        $this->protectedInstances->attach($instance);
51
    }
52
53
    /**
54
     * Check if an instance is marked as a factory.
55
     */
56
    public function isFactory(mixed $instance): bool
57
    {
58
        return is_object($instance) && isset($this->factoryInstances[$instance]);
59
    }
60
61
    /**
62
     * Check if an instance is protected.
63
     */
64
    public function isProtected(mixed $instance): bool
65
    {
66
        return is_object($instance) && isset($this->protectedInstances[$instance]);
67
    }
68
69
    /**
70
     * Schedule an extension to be applied when the service is set.
71
     */
72
    public function scheduleExtension(string $id, Closure $instance): void
73
    {
74
        $this->instancesToExtend[$id][] = $instance;
75
    }
76
77
    /**
78
     * Check if there are pending extensions for a service.
79
     */
80
    public function hasPendingExtensions(string $id): bool
81
    {
82
        return isset($this->instancesToExtend[$id]) && count($this->instancesToExtend[$id]) > 0;
83
    }
84
85
    /**
86
     * Get pending extensions for a service.
87
     *
88
     * @return list<Closure>
89
     */
90
    public function getPendingExtensions(string $id): array
91
    {
92
        return $this->instancesToExtend[$id] ?? [];
93
    }
94
95
    /**
96
     * Clear pending extensions for a service.
97
     */
98
    public function clearPendingExtensions(string $id): void
99
    {
100
        unset($this->instancesToExtend[$id]);
101
    }
102
103
    /**
104
     * Set the service ID currently being extended.
105
     */
106
    public function setCurrentlyExtending(?string $id): void
107
    {
108
        $this->currentlyExtending = $id;
109
    }
110
111
    /**
112
     * Check if we're currently extending a specific service.
113
     */
114
    public function isCurrentlyExtending(string $id): bool
115
    {
116
        return $this->currentlyExtending === $id;
117
    }
118
119
    /**
120
     * Transfer factory status from one instance to another.
121
     * Used when extending a factory service.
122
     */
123
    public function transferFactoryStatus(mixed $from, mixed $to): void
124
    {
125
        if (is_object($from) && isset($this->factoryInstances[$from])) {
126
            $this->factoryInstances->detach($from);
127
            if (is_object($to)) {
128
                $this->factoryInstances->attach($to);
129
            }
130
        }
131
    }
132
133
    /**
134
     * Generate an extended instance wrapper.
135
     *
136
     * @psalm-suppress MissingClosureReturnType,MixedAssignment
137
     */
138
    public function generateExtendedInstance(Closure $instance, mixed $factory, Container $container): Closure
0 ignored issues
show
Unused Code introduced by
The parameter $container is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

138
    public function generateExtendedInstance(Closure $instance, mixed $factory, /** @scrutinizer ignore-unused */ Container $container): Closure

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
139
    {
140
        if (is_callable($factory)) {
141
            return static function (Container $c) use ($instance, $factory) {
142
                $result = $factory($c);
143
144
                return $instance($result, $c) ?? $result;
145
            };
146
        }
147
148
        if (is_object($factory) || is_array($factory)) {
149
            return static fn (Container $c) => $instance($factory, $c) ?? $factory;
150
        }
151
152
        throw ContainerException::instanceNotExtendable();
153
    }
154
}
155