PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.8.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.8.0
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Dependencies / DI / ContainerBuilder.php

ContainerBuilder.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.8.0, at includes/Dependencies/DI/ContainerBuilder.php

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