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: