PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.5.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.5.2
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 3.5.2, at includes/Dependencies/DI/ContainerBuilder.php

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