PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.9.0
Defender Security – Malware Scanner, Login Security & Firewall v5.9.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / lib / packages / DI / ContainerBuilder.php

ContainerBuilder.php in Defender Security – Malware Scanner, Login Security & Firewall 5.9.0, at lib/packages/DI/ContainerBuilder.php

337 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace WPMU_DEV\Defender\Vendor\DI;
6
7 use WPMU_DEV\Defender\Vendor\DI\Compiler\Compiler;
8 use WPMU_DEV\Defender\Vendor\DI\Definition\Source\AttributeBasedAutowiring;
9 use WPMU_DEV\Defender\Vendor\DI\Definition\Source\DefinitionArray;
10 use WPMU_DEV\Defender\Vendor\DI\Definition\Source\DefinitionFile;
11 use WPMU_DEV\Defender\Vendor\DI\Definition\Source\DefinitionSource;
12 use WPMU_DEV\Defender\Vendor\DI\Definition\Source\NoAutowiring;
13 use WPMU_DEV\Defender\Vendor\DI\Definition\Source\ReflectionBasedAutowiring;
14 use WPMU_DEV\Defender\Vendor\DI\Definition\Source\SourceCache;
15 use WPMU_DEV\Defender\Vendor\DI\Definition\Source\SourceChain;
16 use WPMU_DEV\Defender\Vendor\DI\Proxy\NativeProxyFactory;
17 use WPMU_DEV\Defender\Vendor\DI\Proxy\ProxyFactory;
18 use InvalidArgumentException;
19 use WPMU_DEV\Defender\Vendor\Psr\Container\ContainerInterface;
20
21 /**
22 * Helper to create and configure a Container.
23 *
24 * With the default options, the container created is appropriate for the development environment.
25 *
26 * Example:
27 *
28 * $builder = new ContainerBuilder();
29 * $container = $builder->build();
30 *
31 * @api
32 *
33 * @since 3.2
34 * @author Matthieu Napoli <[email protected]>
35 *
36 * @psalm-template ContainerClass of Container
37 */
38 class ContainerBuilder
39 {
40 /**
41 * Name of the container class, used to create the container.
42 * @var class-string<Container>
43 * @psalm-var class-string<ContainerClass>
44 */
45 private string $containerClass;
46
47 /**
48 * Name of the container parent class, used on compiled container.
49 * @var class-string<Container>
50 * @psalm-var class-string<ContainerClass>
51 */
52 private string $containerParentClass;
53
54 private bool $useAutowiring = true;
55
56 private bool $useAttributes = false;
57
58 /**
59 * If set, write the proxies to disk in this directory to improve performances.
60 */
61 private ?string $proxyDirectory = null;
62
63 /**
64 * If PHP-WPMU_DEV\Defender\Vendor\DI is wrapped in another container, this references the wrapper.
65 */
66 private ?ContainerInterface $wrapperContainer = null;
67
68 /**
69 * @var DefinitionSource[]|string[]|array[]
70 */
71 private array $definitionSources = [];
72
73 /**
74 * Whether the container has already been built.
75 */
76 private bool $locked = false;
77
78 private ?string $compileToDirectory = null;
79
80 private bool $sourceCache = false;
81
82 protected string $sourceCacheNamespace = '';
83
84 /**
85 * @param class-string<Container> $containerClass Name of the container class, used to create the container.
86 * @psalm-param class-string<ContainerClass> $containerClass
87 */
88 public function __construct(string $containerClass = Container::class)
89 {
90 $this->containerClass = $containerClass;
91 }
92
93 /**
94 * Build and return a container.
95 *
96 * @return Container
97 * @psalm-return ContainerClass
98 */
99 public function build()
100 {
101 $sources = array_reverse($this->definitionSources);
102
103 if ($this->useAttributes) {
104 $autowiring = new AttributeBasedAutowiring;
105 $sources[] = $autowiring;
106 } elseif ($this->useAutowiring) {
107 $autowiring = new ReflectionBasedAutowiring;
108 $sources[] = $autowiring;
109 } else {
110 $autowiring = new NoAutowiring;
111 }
112
113 $sources = array_map(function ($definitions) use ($autowiring) {
114 if (is_string($definitions)) {
115 // File
116 return new DefinitionFile($definitions, $autowiring);
117 }
118 if (is_array($definitions)) {
119 return new DefinitionArray($definitions, $autowiring);
120 }
121
122 return $definitions;
123 }, $sources);
124 $source = new SourceChain($sources);
125
126 // Mutable definition source
127 $source->setMutableDefinitionSource(new DefinitionArray([], $autowiring));
128
129 if ($this->sourceCache) {
130 if (!SourceCache::isSupported()) {
131 throw new \Exception('APCu is not enabled, PHP-WPMU_DEV\Defender\Vendor\DI cannot use it as a cache');
132 }
133 // Wrap the source with the cache decorator
134 $source = new SourceCache($source, $this->sourceCacheNamespace);
135 }
136
137 $proxyFactory = (\PHP_VERSION_ID >= 80400)
138 ? new NativeProxyFactory()
139 : new ProxyFactory($this->proxyDirectory);
140
141 $this->locked = true;
142
143 $containerClass = $this->containerClass;
144
145 if ($this->compileToDirectory) {
146 $compiler = new Compiler($proxyFactory);
147 $compiledContainerFile = $compiler->compile(
148 $source,
149 $this->compileToDirectory,
150 $containerClass,
151 $this->containerParentClass,
152 $this->useAutowiring
153 );
154 // Only load the file if it hasn't been already loaded
155 // (the container can be created multiple times in the same process)
156 if (!class_exists($containerClass, false)) {
157 require $compiledContainerFile;
158 }
159 }
160
161 return new $containerClass($source, $proxyFactory, $this->wrapperContainer);
162 }
163
164 /**
165 * Compile the container for optimum performances.
166 *
167 * Be aware that the container is compiled once and never updated!
168 *
169 * Therefore:
170 *
171 * - in production you should clear that directory every time you deploy
172 * - in development you should not compile the container
173 *
174 * @see https://php-di.org/doc/performances.html
175 *
176 * @psalm-template T of CompiledContainer
177 *
178 * @param string $directory Directory in which to put the compiled container.
179 * @param string $containerClass Name of the compiled class. Customize only if necessary.
180 * @param class-string<Container> $containerParentClass Name of the compiled container parent class. Customize only if necessary.
181 * @psalm-param class-string<T> $containerParentClass
182 *
183 * @psalm-return self<T>
184 */
185 public function enableCompilation(
186 string $directory,
187 string $containerClass = 'CompiledContainer',
188 string $containerParentClass = CompiledContainer::class,
189 ) : self {
190 $this->ensureNotLocked();
191
192 $this->compileToDirectory = $directory;
193 $this->containerClass = $containerClass;
194 $this->containerParentClass = $containerParentClass;
195
196 return $this;
197 }
198
199 /**
200 * Enable or disable the use of autowiring to guess injections.
201 *
202 * Enabled by default.
203 *
204 * @return $this
205 */
206 public function useAutowiring(bool $bool) : self
207 {
208 $this->ensureNotLocked();
209
210 $this->useAutowiring = $bool;
211
212 return $this;
213 }
214
215 /**
216 * Enable or disable the use of PHP 8 attributes to configure injections.
217 *
218 * Disabled by default.
219 *
220 * @return $this
221 */
222 public function useAttributes(bool $bool) : self
223 {
224 $this->ensureNotLocked();
225
226 $this->useAttributes = $bool;
227
228 return $this;
229 }
230
231 /**
232 * Configure the proxy generation.
233 *
234 * For dev environment, use `writeProxiesToFile(false)` (default configuration)
235 * For production environment, use `writeProxiesToFile(true, 'tmp/proxies')`
236 *
237 * @see https://php-di.org/doc/lazy-injection.html
238 *
239 * @param bool $writeToFile If true, write the proxies to disk to improve performances
240 * @param string|null $proxyDirectory Directory where to write the proxies
241 * @return $this
242 * @throws InvalidArgumentException when writeToFile is set to true and the proxy directory is null
243 */
244 public function writeProxiesToFile(bool $writeToFile, ?string $proxyDirectory = null) : self
245 {
246 $this->ensureNotLocked();
247
248 if ($writeToFile && $proxyDirectory === null) {
249 throw new InvalidArgumentException(
250 'The proxy directory must be specified if you want to write proxies on disk'
251 );
252 }
253 $this->proxyDirectory = $writeToFile ? $proxyDirectory : null;
254
255 return $this;
256 }
257
258 /**
259 * If PHP-DI's container is wrapped by another container, we can
260 * set this so that PHP-WPMU_DEV\Defender\Vendor\DI will use the wrapper rather than itself for building objects.
261 *
262 * @return $this
263 */
264 public function wrapContainer(ContainerInterface $otherContainer) : self
265 {
266 $this->ensureNotLocked();
267
268 $this->wrapperContainer = $otherContainer;
269
270 return $this;
271 }
272
273 /**
274 * Add definitions to the container.
275 *
276 * @param string|array|DefinitionSource ...$definitions Can be an array of definitions, the
277 * name of a file containing definitions
278 * or a DefinitionSource object.
279 * @return $this
280 */
281 public function addDefinitions(string|array|DefinitionSource ...$definitions) : self
282 {
283 $this->ensureNotLocked();
284
285 foreach ($definitions as $definition) {
286 $this->definitionSources[] = $definition;
287 }
288
289 return $this;
290 }
291
292 /**
293 * Enables the use of APCu to cache definitions.
294 *
295 * You must have APCu enabled to use it.
296 *
297 * Before using this feature, you should try these steps first:
298 * - enable compilation if not already done (see `enableCompilation()`)
299 * - if you use autowiring or attributes, add all the classes you are using into your configuration so that
300 * PHP-WPMU_DEV\Defender\Vendor\DI knows about them and compiles them
301 * Once this is done, you can try to optimize performances further with APCu. It can also be useful if you use
302 * `Container::make()` instead of `get()` (`make()` calls cannot be compiled so they are not optimized).
303 *
304 * Remember to clear APCu on each deploy else your application will have a stale cache. Do not enable the cache
305 * in development environment: any change you will make to the code will be ignored because of the cache.
306 *
307 * @see https://php-di.org/doc/performances.html
308 *
309 * @param string $cacheNamespace use unique namespace per container when sharing a single APC memory pool to prevent cache collisions
310 * @return $this
311 */
312 public function enableDefinitionCache(string $cacheNamespace = '') : self
313 {
314 $this->ensureNotLocked();
315
316 $this->sourceCache = true;
317 $this->sourceCacheNamespace = $cacheNamespace;
318
319 return $this;
320 }
321
322 /**
323 * Are we building a compiled container?
324 */
325 public function isCompilationEnabled() : bool
326 {
327 return (bool) $this->compileToDirectory;
328 }
329
330 private function ensureNotLocked() : void
331 {
332 if ($this->locked) {
333 throw new \LogicException('The ContainerBuilder cannot be modified after the container has been built');
334 }
335 }
336 }
337