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 |
||
30 | class Core implements SystemInterface |
||
31 | { |
||
32 | /* Rendering models */ |
||
33 | /** Standard algorithm for view rendering */ |
||
34 | const RENDER_STANDART = 1; |
||
35 | /** View rendering algorithm from array of view variables */ |
||
36 | const RENDER_VARIABLE = 3; |
||
37 | |||
38 | /** @var ResourceMap Current web-application resource map */ |
||
39 | public $map; |
||
40 | |||
41 | /** @var ContainerInterface */ |
||
42 | protected $container; |
||
43 | |||
44 | |||
45 | /** @var string Path to current web-application */ |
||
46 | public $system_path = __SAMSON_CWD__; |
||
47 | /** @var string View path loading mode */ |
||
48 | public $render_mode = self::RENDER_STANDART; |
||
49 | /** @var Module Pointer to current active module */ |
||
50 | protected $active = null; |
||
51 | /** @var bool Flag for outputting layout template, used for asynchronous requests */ |
||
52 | protected $async = false; |
||
53 | /** @var string Path to main system template */ |
||
54 | protected $template_path = __SAMSON_DEFAULT_TEMPLATE; |
||
55 | /** @var string Current system environment */ |
||
56 | protected $environment; |
||
57 | |||
58 | protected $metadataCollection = []; |
||
59 | |||
60 | /** |
||
61 | * Core constructor. |
||
62 | * |
||
63 | * @param ResourceMap|null $map system resources |
||
64 | */ |
||
65 | public function __construct(ResourceMap $map = null) |
||
94 | |||
95 | /** |
||
96 | * Generic wrap for Event system subscription. |
||
97 | * @see \samson\core\\samsonphp\event\Event::subscribe() |
||
98 | * |
||
99 | * @param string $key Event identifier |
||
100 | * @param callable $handler Event handler |
||
101 | * @param array $params Event parameters |
||
102 | * |
||
103 | * @return $this Chaining |
||
104 | */ |
||
105 | public function subscribe($key, $handler, $params = array()) |
||
111 | |||
112 | /** |
||
113 | * Change current system working environment or receive |
||
114 | * current system enviroment if no arguments are passed. |
||
115 | * |
||
116 | * @param string $environment Environment identifier |
||
117 | * |
||
118 | * TODO: Function has two different logics - needs to be changed! |
||
119 | * @return $this|string Chaining or current system environment |
||
120 | */ |
||
121 | 1 | public function environment($environment = Scheme::BASE) |
|
133 | |||
134 | /** |
||
135 | * Generate special response header triggering caching mechanisms |
||
136 | * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour) |
||
137 | * @param string $accessibility Cache-control accessibility value(default public) |
||
138 | */ |
||
139 | public function cached($cacheLife = 3600, $accessibility = 'public') |
||
151 | |||
152 | /** |
||
153 | * Set asynchronous mode. |
||
154 | * This mode will not output template and will just path everything that |
||
155 | * was outputted to client. |
||
156 | * |
||
157 | * @param bool $async True to switch to asynchronous output mode |
||
158 | * |
||
159 | * @return $this Chaining |
||
160 | */ |
||
161 | public function async($async) |
||
167 | |||
168 | /** @see iCore::path() */ |
||
169 | public function path($path = null) |
||
186 | |||
187 | /** @see iModule::active() */ |
||
188 | public function &active(iModule &$module = null) |
||
201 | |||
202 | /** |
||
203 | * Retrieve module instance by identifier. |
||
204 | * |
||
205 | * @param string|null $module Module identifier |
||
206 | * |
||
207 | * @return null|Module Found or active module |
||
208 | */ |
||
209 | public function &module($module = null) |
||
229 | |||
230 | /** |
||
231 | * Unload module from core. |
||
232 | * |
||
233 | * @param string $moduleID Module identifier |
||
234 | */ |
||
235 | public function unload($moduleID) |
||
241 | |||
242 | /** |
||
243 | * Insert generic html template tags and data |
||
244 | * |
||
245 | * @param string $templateHtml Generated HTML |
||
246 | * |
||
247 | * @deprecated Must be moved to a new HTML output object |
||
248 | * @return mixed Changed HTML template |
||
249 | */ |
||
250 | public function generateTemplate(&$templateHtml) |
||
272 | |||
273 | /** |
||
274 | 2 | * Start SamsonPHP framework. |
|
275 | * |
||
276 | * @param string $default Default module identifier |
||
277 | * |
||
278 | 2 | * @throws ViewPathNotFound |
|
279 | */ |
||
280 | 2 | public function start($default) |
|
331 | |||
332 | 2 | /** @see iCore::template() */ |
|
333 | public function template( $template = NULL, $absolutePath = false ) |
||
343 | |||
344 | /** |
||
345 | * Render file to a buffer. |
||
346 | * |
||
347 | * @param string $view Path to file |
||
348 | * @param array $data Collection of variables to path to file |
||
349 | * |
||
350 | * @return string Rendered file contents |
||
351 | * @throws ViewPathNotFound |
||
352 | 7 | */ |
|
353 | public function render($view, $data = array()) |
||
417 | |||
418 | //[PHPCOMPRESSOR(remove,start)] |
||
419 | |||
420 | /** |
||
421 | * Load system from composer.json |
||
422 | * @param string $dependencyFilePath Path to dependencies file |
||
423 | * @return $this Chaining |
||
424 | */ |
||
425 | public function composer($dependencyFilePath = null) |
||
426 | { |
||
427 | $composerModules = array(); |
||
428 | |||
429 | Event::fire( |
||
430 | 'core.composer.create', |
||
431 | array( |
||
432 | &$composerModules, |
||
433 | isset($dependencyFilePath) ? $dependencyFilePath : $this->system_path, |
||
434 | array( |
||
435 | 'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/', 'samsonjavascript/'), |
||
436 | 'ignoreKey' => 'samson_module_ignore', |
||
437 | 'includeKey' => 'samson_module_include' |
||
438 | ) |
||
439 | ) |
||
440 | ); |
||
441 | |||
442 | $modulesToLoad = []; |
||
443 | |||
444 | // Iterate requirements |
||
445 | foreach ($composerModules as $requirement => $parameters) { |
||
446 | $moduleName = $this->load(__SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement, |
||
447 | array_merge( |
||
448 | is_array($parameters) ? $parameters : array($parameters), |
||
449 | array('module_id' => $requirement) |
||
450 | )); |
||
451 | |||
452 | $modulesToLoad[$moduleName] = $parameters; |
||
453 | } |
||
454 | |||
455 | $localModulesPath = '../src'; |
||
456 | ResourceMap::get('cache'); |
||
457 | // TODO: Nested modules relation |
||
458 | for ($i = 0; $i < 2; $i++) { |
||
459 | $resourceMap = ResourceMap::get($localModulesPath); |
||
460 | |||
461 | foreach ($resourceMap->modules as $moduleFile) { |
||
462 | $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]); |
||
463 | $modulePath = explode('/', $modulePath); |
||
464 | $modulePath = $localModulesPath . '/' . $modulePath[1]; |
||
465 | $moduleName = $this->load($modulePath, $parameters); |
||
466 | $modulesToLoad[$moduleName] = $parameters; |
||
467 | } |
||
468 | } |
||
469 | |||
470 | //$this->active = new VirtualModule($this->system_path, $this->map, $this, 'local'); |
||
471 | |||
472 | // Create local module and set it as active |
||
473 | $this->createMetadata(VirtualModule::class, 'local', $this->system_path); |
||
474 | |||
475 | // TODO: This should be changed to one single logic |
||
476 | // Require all local module model files |
||
477 | foreach ($this->map->models as $model) { |
||
478 | // TODO: Why have to require once? |
||
479 | require_once($model); |
||
480 | } |
||
481 | |||
482 | // Create all local modules |
||
483 | foreach ($this->map->controllers as $controller) { |
||
484 | // Require class into PHP |
||
485 | require($controller); |
||
486 | |||
487 | //new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php')); |
||
488 | |||
489 | $this->createMetadata(VirtualModule::class, basename($controller, '.php'), $this->system_path); |
||
490 | } |
||
491 | |||
492 | $this->createMetadata(get_class($this), get_class($this), $this->system_path); |
||
493 | |||
494 | $metadata = new ClassMetadata(); |
||
495 | $metadata->className = get_class($this); |
||
496 | $metadata->name = get_class($this); |
||
497 | $metadata->scopes[] = Builder::SCOPE_SERVICES; |
||
498 | $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata); |
||
499 | $metadata->methodsMetadata['__construct']->dependencies['map'] = ResourceMap::class; |
||
500 | |||
501 | $this->metadataCollection[$metadata->name] = $metadata; |
||
502 | |||
503 | $metadata = new ClassMetadata(); |
||
504 | $metadata->className = ResourceMap::class; |
||
505 | $metadata->name = ResourceMap::class; |
||
506 | $metadata->scopes[] = Builder::SCOPE_SERVICES; |
||
507 | |||
508 | $this->metadataCollection[$metadata->name] = $metadata; |
||
509 | |||
510 | $builder = new Builder(new Generator(), $this->metadataCollection); |
||
511 | $containerPath = $this->path().'www/cache/Container.php'; |
||
512 | |||
513 | //file_put_contents($containerPath, $builder->build()); |
||
514 | |||
515 | require_once($containerPath); |
||
516 | |||
517 | $this->container = new \Container(); |
||
518 | $containerReflection = new \ReflectionClass(get_class($this->container)); |
||
519 | $serviceProperty = $containerReflection->getProperty('servicesInstances'); |
||
520 | $serviceProperty->setAccessible(true); |
||
521 | $containerServices = $serviceProperty->getValue($this->container); |
||
522 | $containerServices[get_class($this)] = $this; |
||
523 | $serviceProperty->setValue(null, $containerServices); |
||
524 | $serviceProperty->setAccessible(false); |
||
525 | |||
526 | foreach ($modulesToLoad as $name => $parameters) { |
||
527 | $instance = $this->container->get($name); |
||
528 | $this->initModule($instance, $parameters); |
||
529 | } |
||
530 | |||
531 | $this->active = $this->container->getLocal(); |
||
532 | |||
533 | return $this; |
||
534 | } |
||
535 | |||
536 | /** |
||
537 | * Initialize module. |
||
538 | * |
||
539 | * @param ExternalModule $instance Module instance for initialization |
||
540 | * @param array $composerParameters Collection of extra parameters from composer.json file |
||
541 | */ |
||
542 | protected function initModule($instance, $composerParameters) |
||
576 | 7 | ||
577 | 7 | /** |
|
578 | * Load module from path to core. |
||
579 | * |
||
580 | * @param string $path Path for module loading |
||
581 | * @param array $parameters Collection of loading parameters |
||
582 | * |
||
583 | * @return string module name |
||
584 | * @throws \samsonphp\core\exception\CannotLoadModule |
||
585 | */ |
||
586 | public function load($path, $parameters = array()) |
||
587 | { |
||
588 | $name = ''; |
||
589 | // Check path |
||
590 | if (file_exists($path)) { |
||
591 | /** @var ResourceMap $resourceMap Gather all resources from path */ |
||
592 | $resourceMap = ResourceMap::get($path); |
||
593 | if (isset($resourceMap->module[0])) { |
||
594 | |||
595 | /** @var string $controllerPath Path to module controller file */ |
||
596 | $controllerPath = $resourceMap->module[1]; |
||
597 | |||
598 | /** @var string $moduleClass Name of module controller class to load */ |
||
599 | $moduleClass = $resourceMap->module[0]; |
||
600 | |||
601 | // Require module controller class into PHP |
||
602 | if (file_exists($controllerPath)) { |
||
603 | require_once($controllerPath); |
||
604 | } |
||
605 | |||
606 | // TODO: this should be done via composer autoload file field |
||
607 | // Iterate all function-style controllers and require them |
||
608 | foreach ($resourceMap->controllers as $controller) { |
||
609 | require_once($controller); |
||
610 | } |
||
611 | |||
612 | $reflection = new \ReflectionClass($moduleClass); |
||
613 | $name = $reflection->getDefaultProperties(); |
||
614 | $name = $name['id'] ?? str_replace('/', '', $moduleClass); |
||
615 | |||
616 | $this->createMetadata($moduleClass, $name, $path); |
||
617 | |||
618 | /*$this->initModule( |
||
619 | new $moduleClass($path, $resourceMap, $this), |
||
620 | $parameters |
||
621 | );*/ |
||
622 | } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) { |
||
623 | $name = str_replace('/', '', $parameters['module_id']); |
||
624 | $this->createMetadata(VirtualModule::class, str_replace('/', '', $parameters['module_id']), $path); |
||
625 | |||
626 | /*$this->initModule( |
||
627 | new VirtualModule($path, $resourceMap, $this, str_replace('/', '', $parameters['module_id'])), |
||
628 | $parameters |
||
629 | );*/ |
||
630 | } elseif (count($resourceMap->classes)) { |
||
631 | /** Update for future version: Search classes that implement LoadableInterface */ |
||
632 | foreach ($resourceMap->classes as $classPath => $class) { |
||
633 | // This class implements LoadableInterface LoadableInterface::class |
||
634 | if (in_array('\samsonframework\core\LoadableInterface', $resourceMap->classData[$classPath]['implements'])) { |
||
635 | |||
636 | $name = str_replace('/', '', $parameters['module_id']); |
||
637 | |||
638 | $this->createMetadata(VirtualModule::class, str_replace('/', '', $parameters['module_id']), $path); |
||
639 | |||
640 | /*$this->initModule( |
||
641 | new VirtualModule( |
||
642 | $path, |
||
643 | $resourceMap, |
||
644 | $this, |
||
645 | str_replace('/', '', $resourceMap->classData[$classPath]['className']) |
||
646 | ), |
||
647 | $parameters |
||
648 | );*/ |
||
649 | } |
||
650 | } |
||
651 | } |
||
652 | |||
653 | } else { |
||
654 | throw new CannotLoadModule($path); |
||
655 | } |
||
656 | |||
657 | return $name; |
||
658 | } |
||
659 | //[PHPCOMPRESSOR(remove,end)] |
||
660 | |||
661 | /** Магический метод для десериализации объекта */ |
||
662 | public function __wakeup() |
||
666 | |||
667 | /** Магический метод для сериализации объекта */ |
||
668 | public function __sleep() |
||
672 | |||
673 | protected function createMetadata($class, $name, $path) |
||
687 | } |
||
688 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: