• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eliashaeussler / cache-warmup / 19528059382

20 Nov 2025 06:39AM UTC coverage: 90.01% (-0.05%) from 90.061%
19528059382

Pull #520

github

eliashaeussler
[FEATURE] Add support for PHP 8.5
Pull Request #520: [FEATURE] Add support for PHP 8.5

1784 of 1982 relevant lines covered (90.01%)

10.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.56
/src/DependencyInjection/Container.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the Composer package "eliashaeussler/cache-warmup".
7
 *
8
 * Copyright (C) 2020-2025 Elias Häußler <elias@haeussler.dev>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace EliasHaeussler\CacheWarmup\DependencyInjection;
25

26
use EliasHaeussler\CacheWarmup\Exception;
27
use ReflectionClass;
28
use ReflectionException;
29
use ReflectionNamedType;
30
use ReflectionParameter;
31

32
use function class_exists;
33
use function in_array;
34

35
/**
36
 * Container.
37
 *
38
 * @author Elias Häußler <elias@haeussler.dev>
39
 * @license GPL-3.0-or-later
40
 *
41
 * @internal
42
 */
43
final class Container
44
{
45
    /**
46
     * @var array<class-string, object>
47
     */
48
    private array $serviceBag = [];
49

50
    /**
51
     * @var array<class-string, true>
52
     */
53
    private array $currentlyBuilding = [];
54

55
    /**
56
     * @template T of object
57
     *
58
     * @param class-string<T> $className
59
     *
60
     * @return T
61
     *
62
     * @throws Exception\ClassCannotBeReflected
63
     * @throws Exception\ClassConstructorIsInaccessible
64
     * @throws Exception\ClassDoesNotExist
65
     * @throws Exception\ParameterCannotBeAutowired
66
     * @throws Exception\RecursionInServiceCreation
67
     */
68
    public function get(string $className): object
13✔
69
    {
70
        /* @phpstan-ignore return.type */
71
        return $this->serviceBag[$className] ?? $this->serviceBag[$className] = $this->constructNewService($className);
13✔
72
    }
73

74
    /**
75
     * @param class-string $className
76
     */
77
    public function has(string $className): bool
5✔
78
    {
79
        try {
80
            $this->get($className);
5✔
81
        } catch (Exception\ClassCannotBeReflected|Exception\ClassConstructorIsInaccessible|Exception\ClassDoesNotExist|Exception\ParameterCannotBeAutowired) {
4✔
82
            return false;
3✔
83
        }
84

85
        return true;
2✔
86
    }
87

88
    /**
89
     * @template T of object
90
     *
91
     * @param class-string<T> $className
92
     * @param T               $service
93
     */
94
    public function set(string $className, object $service): self
4✔
95
    {
96
        if (!isset($this->serviceBag[$className])) {
4✔
97
            $this->serviceBag[$className] = $service;
4✔
98
        }
99

100
        if ($className !== $service::class) {
4✔
101
            $this->serviceBag[$service::class] = $service;
2✔
102
        }
103

104
        return $this;
4✔
105
    }
106

107
    /**
108
     * @template T of object
109
     *
110
     * @param class-string<T> $className
111
     *
112
     * @return T
113
     *
114
     * @throws Exception\ClassCannotBeReflected
115
     * @throws Exception\ClassConstructorIsInaccessible
116
     * @throws Exception\ClassDoesNotExist
117
     * @throws Exception\ParameterCannotBeAutowired
118
     * @throws Exception\RecursionInServiceCreation
119
     */
120
    private function constructNewService(string $className): object
11✔
121
    {
122
        if (isset($this->currentlyBuilding[$className])) {
11✔
123
            throw new Exception\RecursionInServiceCreation($className);
1✔
124
        }
125

126
        if (!class_exists($className)) {
11✔
127
            throw new Exception\ClassDoesNotExist($className);
3✔
128
        }
129

130
        // Flag to avoid recursion
131
        $this->currentlyBuilding[$className] = true;
10✔
132

133
        try {
134
            $reflection = new ReflectionClass($className);
10✔
135
            $constructor = $reflection->getConstructor();
10✔
136

137
            // Return empty instance if no constructor exists
138
            if (null === $constructor) {
10✔
139
                return $reflection->newInstance();
2✔
140
            }
141

142
            // Throw exception on private constructor
143
            if (!$constructor->isPublic()) {
8✔
144
                throw new Exception\ClassConstructorIsInaccessible($className);
1✔
145
            }
146

147
            // Autowire constructor parameters
148
            $parameters = [];
7✔
149
            foreach ($constructor->getParameters() as $parameter) {
7✔
150
                $parameters[] = $this->autowireParameter($parameter, $className);
7✔
151
            }
152

153
            return $reflection->newInstanceArgs($parameters);
4✔
154
        } catch (ReflectionException $exception) {
4✔
155
            throw new Exception\ClassCannotBeReflected($className, $exception);
×
156
        } finally {
157
            // Free from recursion check
158
            unset($this->currentlyBuilding[$className]);
10✔
159
        }
160
    }
161

162
    /**
163
     * @param class-string $className
164
     *
165
     * @throws Exception\ClassCannotBeReflected
166
     * @throws Exception\ClassConstructorIsInaccessible
167
     * @throws Exception\ClassDoesNotExist
168
     * @throws Exception\ParameterCannotBeAutowired
169
     * @throws Exception\RecursionInServiceCreation
170
     * @throws ReflectionException
171
     */
172
    private function autowireParameter(ReflectionParameter $parameter, string $className): mixed
7✔
173
    {
174
        $type = $parameter->getType();
7✔
175

176
        if ($type instanceof ReflectionNamedType && !$type->isBuiltin()) {
7✔
177
            $name = $type->getName();
4✔
178

179
            // Resolve self-references
180
            if (in_array($name, ['self', 'static'], true)) {
4✔
181
                $name = $className;
×
182
            }
183

184
            /* @phpstan-ignore argument.type */
185
            if ($this->has($name)) {
4✔
186
                /* @phpstan-ignore argument.type, argument.templateType */
187
                return $this->get($type->getName());
1✔
188
            }
189
        }
190

191
        if ($parameter->isOptional()) {
5✔
192
            return $parameter->getDefaultValue();
2✔
193
        }
194

195
        if ($parameter->allowsNull()) {
3✔
196
            return null;
1✔
197
        }
198

199
        throw new Exception\ParameterCannotBeAutowired($className, $parameter->getName());
2✔
200
    }
201
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc