Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Core often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Core, and based on these observations, apply Extract Interface, too.
1 | <?php declare(strict_types=1); |
||
41 | class Core implements SystemInterface |
||
42 | { |
||
43 | /** @var ContainerInterface */ |
||
44 | protected $container; |
||
45 | |||
46 | /** @var ClassMetadata[] */ |
||
47 | protected $metadataCollection = []; |
||
48 | |||
49 | /** @var ContainerBuilderInterface */ |
||
50 | protected $builder; |
||
51 | |||
52 | /** @var string Current system environment */ |
||
53 | protected $environment; |
||
54 | |||
55 | /* Rendering models */ |
||
56 | /** @deprecated Standard algorithm for view rendering */ |
||
57 | const RENDER_STANDART = 1; |
||
58 | /** @deprecated View rendering algorithm from array of view variables */ |
||
59 | const RENDER_VARIABLE = 3; |
||
60 | |||
61 | /** @deprecated @var ResourceMap Current web-application resource map */ |
||
62 | public $map; |
||
63 | |||
64 | protected $classes = []; |
||
65 | |||
66 | /** @deprecated @var string Path to current web-application */ |
||
67 | public $system_path = __SAMSON_CWD__; |
||
68 | /** @deprecated @var string View path loading mode */ |
||
69 | public $render_mode = self::RENDER_STANDART; |
||
70 | /** @var Module Pointer to current active module */ |
||
71 | protected $active = null; |
||
72 | /** @var bool Flag for outputting layout template, used for asynchronous requests */ |
||
73 | protected $async = false; |
||
74 | /** @var string Path to main system template */ |
||
75 | protected $template_path = __SAMSON_DEFAULT_TEMPLATE; |
||
76 | |||
77 | /** @return \Container Get system container */ |
||
78 | public function getContainer() |
||
79 | { |
||
80 | return $this->container; |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * Core constructor. |
||
85 | * |
||
86 | * @param ContainerBuilderInterface $builder Container builder |
||
87 | * @param ResourceMap|null $map system resources |
||
88 | * @InjectArgument(builder="\samsonframework\container\ContainerBuilderInterface") |
||
89 | * @InjectArgument(builder="\samsonframework\core\ResourceInterface") |
||
90 | */ |
||
91 | public function __construct(ContainerBuilderInterface $builder, ResourceMap $map) |
||
92 | { |
||
93 | $this->builder = $builder; |
||
94 | // Get correct web-application path |
||
95 | $this->system_path = __SAMSON_CWD__; |
||
96 | |||
97 | // Get web-application resource map |
||
98 | $this->map = ResourceMap::get($this->system_path, false, array('src/')); |
||
99 | |||
100 | // Temporary add template worker |
||
101 | $this->subscribe('core.rendered', array($this, 'generateTemplate')); |
||
102 | |||
103 | // Fire core creation event |
||
104 | Event::fire('core.created', array(&$this)); |
||
105 | |||
106 | // Signal core configure event |
||
107 | Event::signal('core.configure', array($this->system_path . __SAMSON_CONFIG_PATH)); |
||
108 | } |
||
109 | |||
110 | 1 | /** |
|
111 | * Generic wrap for Event system subscription. |
||
112 | * @see \samson\core\\samsonphp\event\Event::subscribe() |
||
113 | * |
||
114 | * @param string $key Event identifier |
||
115 | * @param callable $handler Event handler |
||
116 | * @param array $params Event parameters |
||
117 | * |
||
118 | * @return $this Chaining |
||
119 | */ |
||
120 | public function subscribe($key, $handler, $params = array()) |
||
121 | 1 | { |
|
122 | Event::subscribe($key, $handler, $params); |
||
123 | |||
124 | return $this; |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * Change current system working environment or receive |
||
129 | * current system enviroment if no arguments are passed. |
||
130 | * |
||
131 | * @param string $environment Environment identifier |
||
132 | * |
||
133 | * TODO: Function has two different logics - needs to be changed! |
||
134 | * @return $this|string Chaining or current system environment |
||
135 | */ |
||
136 | public function environment($environment = Scheme::BASE) |
||
137 | { |
||
138 | if (func_num_args() !== 0) { |
||
139 | $this->environment = $environment; |
||
140 | |||
141 | // Signal core environment change |
||
142 | Event::signal('core.environment.change', array($environment, &$this)); |
||
143 | return $this; |
||
144 | } |
||
145 | |||
146 | return $this->environment; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Generate special response header triggering caching mechanisms |
||
151 | * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour) |
||
152 | * @param string $accessibility Cache-control accessibility value(default public) |
||
153 | */ |
||
154 | public function cached($cacheLife = 3600, $accessibility = 'public') |
||
155 | { |
||
156 | static $cached; |
||
157 | // Protect sending cached headers once |
||
158 | if (!isset($cached) or $cached !== true) { |
||
159 | header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife)); |
||
160 | header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife); |
||
161 | header('Pragma: cache'); |
||
162 | |||
163 | $cached = true; |
||
164 | } |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * Set asynchronous mode. |
||
169 | * This mode will not output template and will just path everything that |
||
170 | * was outputted to client. |
||
171 | * |
||
172 | * @param bool $async True to switch to asynchronous output mode |
||
173 | * |
||
174 | * @return $this Chaining |
||
175 | */ |
||
176 | public function async($async) |
||
177 | { |
||
178 | $this->async = $async; |
||
179 | |||
180 | return $this; |
||
181 | } |
||
182 | |||
183 | /** @see iCore::path() */ |
||
184 | public function path($path = null) |
||
185 | { |
||
186 | // Если передан аргумент |
||
187 | if (func_num_args()) { |
||
188 | // Сформируем новый относительный путь к главному шаблону системы |
||
189 | $this->template_path = $path . $this->template_path; |
||
190 | |||
191 | // Сохраним относительный путь к Веб-приложению |
||
192 | $this->system_path = $path; |
||
193 | |||
194 | // Продолжил цепирование |
||
195 | return $this; |
||
196 | } |
||
197 | |||
198 | // Вернем текущее значение |
||
199 | return $this->system_path; |
||
200 | } |
||
201 | |||
202 | /** @see iModule::active() */ |
||
203 | public function &active(&$module = null) |
||
204 | { |
||
205 | // Сохраним старый текущий модуль |
||
206 | $old = &$this->active; |
||
207 | |||
208 | // Если передано значение модуля для установки как текущий - проверим и установим его |
||
209 | if (isset($module)) { |
||
210 | $this->active = &$module; |
||
211 | } |
||
212 | |||
213 | // Вернем значение текущего модуля |
||
214 | return $old; |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * Retrieve module instance by identifier. |
||
219 | * |
||
220 | * @param string|null $module Module identifier |
||
221 | * |
||
222 | * @return null|Module Found or active module |
||
223 | */ |
||
224 | public function &module($module = null) |
||
225 | { |
||
226 | $return = null; |
||
227 | |||
228 | // Ничего не передано - вернем текущуй модуль системы |
||
229 | if (!isset($module) && isset($this->active)) { |
||
230 | $return = &$this->active; |
||
231 | } elseif (is_object($module)) { |
||
232 | $return = &$module; |
||
233 | } elseif (is_string($module)) { |
||
234 | $return = $this->container->get($module); |
||
235 | } |
||
236 | |||
237 | // Ничего не получилось вернем ошибку |
||
238 | if ($return === null) { |
||
239 | e('Не возможно получить модуль(##) системы', E_SAMSON_CORE_ERROR, array($module)); |
||
240 | } |
||
241 | |||
242 | return $return; |
||
243 | } |
||
244 | |||
245 | /** |
||
246 | * Unload module from core. |
||
247 | * |
||
248 | * @param string $moduleID Module identifier |
||
249 | */ |
||
250 | public function unload($moduleID) |
||
251 | { |
||
252 | if (isset($this->module_stack[$moduleID])) { |
||
253 | unset($this->module_stack[$moduleID]); |
||
254 | } |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Insert generic html template tags and data |
||
259 | * |
||
260 | * @param string $templateHtml Generated HTML |
||
261 | * |
||
262 | * @deprecated Must be moved to a new HTML output object |
||
263 | * @return mixed Changed HTML template |
||
264 | 2 | */ |
|
265 | public function generateTemplate(&$templateHtml) |
||
266 | { |
||
267 | // Добавим путь к ресурсам для браузера |
||
268 | $headHtml = "\n" . '<base href="' . url()->base() . '">'; |
||
269 | // Добавим отметку времени для JavaScript |
||
270 | $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>'; |
||
271 | 2 | ||
272 | // Добавим поддержку HTML для старых IE |
||
273 | $headHtml .= "\n" . '<!--[if lt IE 9]>'; |
||
274 | 2 | $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>'; |
|
275 | $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>'; |
||
276 | $headHtml .= "\n" . '<![endif]-->'; |
||
277 | |||
278 | 2 | // Выполним вставку главного тега <base> от которого зависят все ссылки документа |
|
279 | // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS |
||
280 | 2 | $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml); |
|
281 | |||
282 | 2 | // Вставим указатель JavaScript ресурсы в конец HTML документа |
|
283 | 2 | $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml); |
|
284 | |||
285 | return $templateHtml; |
||
286 | 2 | } |
|
287 | |||
288 | 2 | /** |
|
289 | * Start SamsonPHP framework. |
||
290 | 2 | * |
|
291 | * @param string $default Default module identifier |
||
292 | * |
||
293 | * @throws ViewPathNotFound |
||
294 | */ |
||
295 | 2 | public function start($default) |
|
296 | { |
||
297 | // TODO: Change ExternalModule::init() signature |
||
298 | // Fire core started event |
||
299 | Event::fire('core.started'); |
||
300 | |||
301 | // TODO: Does not see why it should be here |
||
302 | // Set main template path |
||
303 | $this->template($this->template_path); |
||
304 | |||
305 | // Security layer |
||
306 | $securityResult = true; |
||
307 | // Fire core security event |
||
308 | Event::fire('core.security', array(&$this, &$securityResult)); |
||
309 | |||
310 | /** @var mixed $result External route controller action result */ |
||
311 | $result = false; |
||
312 | |||
313 | // If we have passed security application layer |
||
314 | if ($securityResult) { |
||
315 | // Fire core routing event - go to routing application layer |
||
316 | Event::signal('core.routing', array(&$this, &$result, $default)); |
||
317 | } |
||
318 | |||
319 | // If no one has passed back routing callback |
||
320 | 2 | if (!isset($result) || $result === false) { |
|
321 | // Fire core e404 - routing failed event |
||
322 | $result = Event::signal('core.e404', array(url()->module, url()->method)); |
||
323 | 2 | } |
|
324 | |||
325 | // Response |
||
326 | 2 | $output = ''; |
|
327 | |||
328 | // If this is not asynchronous response and controller has been executed |
||
329 | 2 | if (!$this->async && ($result !== false)) { |
|
330 | // Store module data |
||
331 | $data = $this->active->toView(); |
||
332 | 2 | ||
333 | // Render main template |
||
334 | $output = $this->render($this->template_path, $data); |
||
335 | 2 | ||
336 | // Fire after render event |
||
337 | Event::fire('core.rendered', array(&$output)); |
||
338 | 2 | } |
|
339 | |||
340 | // Output results to client |
||
341 | echo $output; |
||
342 | |||
343 | // Fire ended event |
||
344 | Event::fire('core.ended', array(&$output)); |
||
345 | } |
||
346 | |||
347 | /** @see iCore::template() */ |
||
348 | public function template( $template = NULL, $absolutePath = false ) |
||
349 | { |
||
350 | // Если передан аргумент |
||
351 | if( func_num_args() ){ |
||
352 | 7 | $this->template_path = ($absolutePath)?$template:$this->active->path().$template; |
|
353 | } |
||
354 | 7 | ||
355 | // Аргументы не переданы - вернем текущий путь к шаблону системы |
||
356 | 7 | return $this->template_path; |
|
357 | } |
||
358 | |||
359 | /** |
||
360 | * Render file to a buffer. |
||
361 | * |
||
362 | * @param string $view Path to file |
||
363 | * @param array $data Collection of variables to path to file |
||
364 | * |
||
365 | * @return string Rendered file contents |
||
366 | * @throws ViewPathNotFound |
||
367 | */ |
||
368 | public function render($view, $data = array()) |
||
369 | { |
||
370 | // TODO: Make rendering as external system, to split up these 3 rendering options |
||
371 | |||
372 | // Объявить ассоциативный массив переменных в данном контексте |
||
373 | if (is_array($data)) { |
||
374 | extract($data); |
||
375 | } |
||
376 | |||
377 | // Начать вывод в буффер |
||
378 | ob_start(); |
||
379 | |||
380 | // Path to another template view, by default we are using default template folder path, |
||
381 | // for meeting first condition |
||
382 | $templateView = $view; |
||
383 | |||
384 | if (locale() != SamsonLocale::DEF) { |
||
385 | // Modify standard view path with another template |
||
386 | $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView); |
||
387 | } |
||
388 | |||
389 | // Depending on core view rendering model |
||
390 | switch ($this->render_mode) { |
||
391 | // Standard algorithm for view rendering |
||
392 | case self::RENDER_STANDART: |
||
393 | // Trying to find another template path, by default it's an default template path |
||
394 | if (file_exists($templateView)) { |
||
395 | include($templateView); |
||
396 | } elseif (file_exists($view)) { |
||
397 | // If another template wasn't found - we will use default template path |
||
398 | include($view); |
||
399 | } else { // Error no template view was found |
||
400 | throw(new ViewPathNotFound($view)); |
||
401 | } |
||
402 | break; |
||
403 | 2 | ||
404 | // View rendering algorithm from array of view variables |
||
405 | case self::RENDER_VARIABLE: |
||
406 | 2 | // Collection of views |
|
407 | $views = &$GLOBALS['__compressor_files']; |
||
408 | // Trying to find another template path, by default it's an default template path |
||
409 | 2 | if (isset($views[$templateView])) { |
|
410 | 2 | eval(' ?>' . $views[$templateView] . '<?php '); |
|
411 | 2 | } elseif (isset($views[$view])) { |
|
412 | // If another template wasn't found - we will use default template path |
||
413 | eval(' ?>' . $views[$view] . '<?php '); |
||
414 | 2 | } else { // Error no template view was found |
|
415 | throw(new ViewPathNotFound($view)); |
||
416 | } |
||
417 | break; |
||
418 | } |
||
419 | |||
420 | // Получим данные из буффера вывода |
||
421 | $html = ob_get_contents(); |
||
422 | |||
423 | // Очистим буффер |
||
424 | ob_end_clean(); |
||
425 | |||
426 | // Fire core render event |
||
427 | Event::fire('core.render', array(&$html, &$data, &$this->active)); |
||
428 | |||
429 | ////elapsed('End rendering '.$__view); |
||
430 | return $html; |
||
431 | } |
||
432 | |||
433 | //[PHPCOMPRESSOR(remove,start)] |
||
434 | |||
435 | /** |
||
436 | * Load system from composer.json |
||
437 | * @param string $dependencyFilePath Path to dependencies file |
||
438 | * @return $this Chaining |
||
439 | */ |
||
440 | public function composer($dependencyFilePath = null) |
||
441 | { |
||
442 | $composerModules = array(); |
||
443 | |||
444 | Event::fire( |
||
445 | 'core.composer.create', |
||
446 | array( |
||
447 | &$composerModules, |
||
448 | isset($dependencyFilePath) ? $dependencyFilePath : $this->system_path, |
||
449 | array( |
||
450 | 'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/', 'samsonjavascript/'), |
||
451 | 'ignoreKey' => 'samson_module_ignore', |
||
452 | 'includeKey' => 'samson_module_include' |
||
453 | ) |
||
454 | ) |
||
455 | ); |
||
456 | |||
457 | $modulesToLoad = []; |
||
458 | $preClasses = [ |
||
459 | str_replace(['\\', '/'], '_', __CLASS__) => __CLASS__, |
||
460 | str_replace(['\\', '/'], '_', ResourceMap::class) => ResourceMap::class |
||
461 | ]; |
||
462 | // $this->classes = array_merge($this->classes, $preClasses); |
||
463 | $preModules = []; |
||
464 | |||
465 | // Iterate requirements |
||
466 | foreach ($composerModules as $requirement => $parameters) { |
||
467 | $modulePath = __SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement; |
||
468 | $moduleName = $this->load($modulePath, |
||
469 | array_merge( |
||
470 | is_array($parameters) ? $parameters : array($parameters), |
||
471 | array('module_id' => $requirement) |
||
472 | )); |
||
473 | if (array_key_exists('samsonframework_precontainer', $parameters)) { |
||
474 | $preModules[$moduleName] = $parameters; |
||
475 | foreach (ResourceMap::get($modulePath)->classes as $className) { |
||
476 | //$preClasses[str_replace(['\\', '/'], '_', $className)] = $className; |
||
477 | } |
||
478 | } |
||
479 | $modulesToLoad[$moduleName] = $parameters; |
||
480 | } |
||
481 | |||
482 | // $metadata = new ClassMetadata(); |
||
483 | // $metadata->className = get_class($this); |
||
484 | // $metadata->name = 'core'; |
||
485 | // $metadata->scopes[] = Builder::SCOPE_SERVICES; |
||
486 | // $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata); |
||
487 | // $metadata->methodsMetadata['__construct']->dependencies['map'] = 'resource_map'; |
||
488 | // $this->metadataCollection[$metadata->name] = $metadata; |
||
489 | |||
490 | // $metadata = new ClassMetadata(); |
||
491 | // $metadata->className = ResourceMap::class; |
||
492 | // $metadata->name = 'resource_map'; |
||
493 | // $metadata->scopes[] = Builder::SCOPE_SERVICES; |
||
494 | // $this->metadataCollection[$metadata->name] = $metadata; |
||
495 | |||
496 | // Create local module and set it as active |
||
497 | $this->createMetadata(VirtualModule::class, 'local', $this->system_path); |
||
498 | |||
499 | // TODO: This should be changed to one single logic |
||
500 | // Require all local module model files |
||
501 | foreach ($this->map->models as $model) { |
||
502 | // TODO: Why have to require once? |
||
503 | require_once($model); |
||
504 | } |
||
505 | |||
506 | // Create all local modules |
||
507 | foreach ($this->map->controllers as $controller) { |
||
508 | // Require class into PHP |
||
509 | require($controller); |
||
510 | |||
511 | //new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php')); |
||
512 | |||
513 | $this->createMetadata(VirtualModule::class, basename($controller, '.php'), $this->system_path); |
||
514 | } |
||
515 | |||
516 | $localModulesPath = '../src'; |
||
517 | ResourceMap::get('cache'); |
||
518 | // TODO: Nested modules relation |
||
519 | for ($i = 0; $i < 2; $i++) { |
||
520 | $resourceMap = ResourceMap::get($localModulesPath); |
||
521 | |||
522 | foreach ($resourceMap->modules as $moduleFile) { |
||
523 | $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]); |
||
524 | $modulePath = explode('/', $modulePath); |
||
525 | $modulePath = $localModulesPath . '/' . $modulePath[1]; |
||
526 | $moduleName = $this->load($modulePath, $parameters); |
||
527 | $modulesToLoad[$moduleName] = $parameters; |
||
528 | } |
||
529 | } |
||
530 | |||
531 | $preMetadataCollection = []; |
||
532 | foreach ($preModules as $moduleName => $parameters) { |
||
533 | $preMetadataCollection[$moduleName] = $this->metadataCollection[$moduleName]; |
||
534 | } |
||
535 | //$preMetadataCollection['core'] = $this->metadataCollection['core']; |
||
536 | //$preMetadataCollection['resource_map'] = $this->metadataCollection['resource_map']; |
||
537 | |||
538 | $preContainer = $this->loadMetadata($preMetadataCollection, $preModules, $preClasses, 'ContainerPreLoad'); |
||
539 | /** @var ContainerInterface container */ |
||
540 | $this->container = $this->loadMetadata($this->metadataCollection, $modulesToLoad, $this->classes, 'Container', $preContainer); |
||
541 | |||
542 | $this->active = $this->container->getLocal(); |
||
543 | |||
544 | return $this; |
||
545 | } |
||
546 | |||
547 | /** |
||
548 | * @param ClassMetadata[] $metadataCollection |
||
549 | * @param array $modulesToLoad |
||
550 | * @param array $classes |
||
551 | * @param string $containerName |
||
552 | * @param ContainerInterface $parentContainer |
||
553 | * @return ContainerInterface |
||
554 | */ |
||
555 | protected function loadMetadata(array $metadataCollection, array $modulesToLoad, array $classes, $containerName = 'Container', ContainerInterface $parentContainer = null) : ContainerInterface |
||
556 | { |
||
557 | static $implementsByAlias; |
||
558 | 7 | static $serviceAliasesByClass; |
|
559 | |||
560 | // Load annotation and parse classes |
||
561 | 7 | new Injectable(); |
|
562 | new InjectArgument(['var' => 'type']); |
||
563 | new Service(['value' => '']); |
||
564 | 7 | ||
565 | $reader = new AnnotationReader(); |
||
566 | $resolver = new AnnotationResolver( |
||
567 | 7 | new AnnotationClassResolver($reader), |
|
568 | new AnnotationPropertyResolver($reader), |
||
569 | new AnnotationMethodResolver($reader) |
||
570 | 7 | ); |
|
571 | $annotationCollector = new AnnotationMetadataCollector($resolver); |
||
572 | $metadataCollection = $annotationCollector->collect($classes, $metadataCollection); |
||
573 | 7 | ||
574 | // Regroup classes metadata by class name instead of alias |
||
575 | $classesMetadata = []; |
||
576 | 7 | foreach ($metadataCollection as $alias => $classMetadata) { |
|
577 | 7 | $classesMetadata[$classMetadata->className] = $classMetadata; |
|
578 | } |
||
579 | |||
580 | // Gather all interface implementations |
||
581 | $implementsByAlias = $implementsByAlias ?? []; |
||
582 | foreach (get_declared_classes() as $class) { |
||
583 | $classImplements = class_implements($class); |
||
584 | foreach (get_declared_interfaces() as $interface) { |
||
585 | if (in_array($interface, $classImplements, true)) { |
||
586 | if (array_key_exists($class, $classesMetadata)) { |
||
587 | $implementsByAlias[strtolower('\\'.$interface)][] = $classesMetadata[$class]->name; |
||
588 | } |
||
589 | } |
||
590 | } |
||
591 | } |
||
592 | |||
593 | // Gather all class implementations |
||
594 | $serviceAliasesByClass = $serviceAliasesByClass ?? []; |
||
595 | foreach (get_declared_classes() as $class) { |
||
596 | if (array_key_exists($class, $classesMetadata)) { |
||
597 | $serviceAliasesByClass[strtolower('\\' . $class)][] = $classesMetadata[$class]->name; |
||
598 | } |
||
599 | } |
||
600 | |||
601 | /** |
||
602 | * TODO: now we need to implement not forcing to load fixed dependencies into modules |
||
603 | * to give ability to change constructors and inject old variable into properties |
||
604 | * and them after refactoring remove them. With this we can only specify needed dependencies |
||
605 | * in new modules, and still have old ones working. |
||
606 | */ |
||
607 | |||
608 | foreach ($metadataCollection as $alias => $metadata) { |
||
609 | View Code Duplication | foreach ($metadata->propertiesMetadata as $property => $propertyMetadata) { |
|
610 | $dependency = strtolower($propertyMetadata->dependency); |
||
611 | if (array_key_exists($dependency, $implementsByAlias)) { |
||
612 | $propertyMetadata->dependency = $implementsByAlias[$dependency][0]; |
||
613 | } elseif (array_key_exists($dependency, $serviceAliasesByClass)) { |
||
614 | $propertyMetadata->dependency = $serviceAliasesByClass[$dependency][0]; |
||
615 | } else { |
||
616 | |||
617 | } |
||
618 | } |
||
619 | foreach ($metadata->methodsMetadata as $method => $methodMetadata) { |
||
620 | View Code Duplication | foreach ($methodMetadata->dependencies as $argument => $dependency) { |
|
621 | $dependency = strtolower($dependency); |
||
622 | if (array_key_exists($dependency, $implementsByAlias)) { |
||
623 | $methodMetadata->dependencies[$argument] = $implementsByAlias[$dependency][0]; |
||
624 | //$methodMetadata->parametersMetadata[$argument]->dependency = $implementsByAlias[$dependency][0]; |
||
625 | } elseif (array_key_exists($dependency, $serviceAliasesByClass)) { |
||
626 | $methodMetadata->dependencies[$argument] = $serviceAliasesByClass[$dependency][0]; |
||
627 | //$methodMetadata->parametersMetadata[$argument]->dependency = $serviceAliasesByClass[$dependency][0]; |
||
628 | } else { |
||
629 | |||
630 | } |
||
631 | } |
||
632 | } |
||
633 | } |
||
634 | |||
635 | // Generate XML configs |
||
636 | (new XMLBuilder())->buildXMLConfig($metadataCollection, getcwd().'/cache/config_'); |
||
637 | |||
638 | // Load container class |
||
639 | $containerPath = $this->path().'www/cache/' . $containerName . '.php'; |
||
640 | file_put_contents($containerPath, $this->builder->build($metadataCollection, $containerName, '', $parentContainer)); |
||
641 | require_once($containerPath); |
||
642 | |||
643 | // Inject current core into container |
||
644 | /** @var ContainerInterface $container */ |
||
645 | $this->container = new $containerName(); |
||
646 | $containerReflection = new \ReflectionClass(get_class($this->container)); |
||
647 | $serviceProperty = $containerReflection->getProperty(Builder::DI_FUNCTION_SERVICES); |
||
648 | $serviceProperty->setAccessible(true); |
||
649 | $containerServices = $serviceProperty->getValue($this->container); |
||
650 | $containerServices['core'] = $this; |
||
651 | $serviceProperty->setValue($this->container, $containerServices); |
||
652 | $serviceProperty->setAccessible(false); |
||
653 | if ($parentContainer !== null) { |
||
654 | $this->container->delegate($parentContainer); |
||
655 | } |
||
656 | |||
657 | foreach ($modulesToLoad as $identifier => $parameters) { |
||
658 | $instance = $this->container->get($identifier); |
||
659 | |||
660 | // Set composer parameters |
||
661 | $instance->composerParameters = $parameters; |
||
662 | |||
663 | // TODO: Change event signature to single approach |
||
664 | // Fire core module load event |
||
665 | Event::fire('core.module_loaded', [$identifier, &$instance]); |
||
666 | |||
667 | // Signal core module configure event |
||
668 | Event::signal('core.module.configure', [&$instance, $identifier]); |
||
669 | |||
670 | if ($instance instanceof PreparableInterface) { |
||
671 | // Call module preparation handler |
||
672 | if (!$instance->prepare()) { |
||
673 | //throw new \Exception($identifier.' - Module preparation stage failed'); |
||
674 | } |
||
675 | } |
||
676 | |||
677 | // Try to set module parent module |
||
678 | $instance->parent = $this->getClassParentModule(get_parent_class($instance)); |
||
679 | } |
||
680 | |||
681 | return $this->container; |
||
682 | } |
||
683 | |||
684 | /** |
||
685 | * Find parent module by OOP class inheritance. |
||
686 | * |
||
687 | * @param string $className Class name for searching parent modules |
||
688 | * @param array $ignoredClasses Collection of ignored classes |
||
689 | * |
||
690 | * @return null|mixed Parent service instance if present |
||
691 | */ |
||
692 | protected function getClassParentModule( |
||
708 | |||
709 | /** |
||
710 | * Load module from path to core. |
||
711 | * |
||
712 | * @param string $path Path for module loading |
||
713 | * @param array $parameters Collection of loading parameters |
||
714 | * |
||
715 | * @return string module name |
||
716 | * @throws \samsonphp\core\exception\CannotLoadModule |
||
717 | */ |
||
718 | public function load($path, $parameters = array()) |
||
719 | { |
||
720 | $name = ''; |
||
721 | // Check path |
||
722 | if (file_exists($path)) { |
||
723 | |||
724 | /** @var ResourceMap $resourceMap Gather all resources from path */ |
||
769 | //[PHPCOMPRESSOR(remove,end)] |
||
770 | |||
771 | /** Магический метод для десериализации объекта */ |
||
772 | public function __wakeup() |
||
776 | |||
777 | /** Магический метод для сериализации объекта */ |
||
778 | public function __sleep() |
||
782 | |||
783 | protected function createMetadata($class, $name, $path, $scope = 'module') |
||
820 | } |
||
821 |
Let’s assume that you have a directory layout like this:
and let’s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: