Complex classes like Application 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 Application, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | class Application extends \Symfony\Component\Console\Application |
||
32 | { |
||
33 | /** |
||
34 | * Version of the Console Jedi application. |
||
35 | */ |
||
36 | const VERSION = '1.0.0'; |
||
37 | /** |
||
38 | * Default name of configuration file. |
||
39 | */ |
||
40 | const CONFIG_DEFAULT_FILE = './.jedi.php'; |
||
41 | /** |
||
42 | * Bitrix is unavailable. |
||
43 | */ |
||
44 | const BITRIX_STATUS_UNAVAILABLE = 500; |
||
45 | /** |
||
46 | * Bitrix is available, but not have connection to DB. |
||
47 | */ |
||
48 | const BITRIX_STATUS_NO_DB_CONNECTION = 100; |
||
49 | /** |
||
50 | * Bitrix is available. |
||
51 | */ |
||
52 | const BITRIX_STATUS_COMPLETE = 0; |
||
53 | /** |
||
54 | * @var int Status of Bitrix kernel. Value of constant `Application::BITRIX_STATUS_*`. |
||
55 | */ |
||
56 | protected $bitrixStatus = Application::BITRIX_STATUS_UNAVAILABLE; |
||
57 | /** |
||
58 | * @var null|string |
||
59 | */ |
||
60 | private $documentRoot = null; |
||
61 | /** |
||
62 | * @var null|array |
||
63 | */ |
||
64 | private $configuration = null; |
||
65 | |||
66 | /** |
||
67 | * {@inheritdoc} |
||
68 | */ |
||
69 | public function __construct($name = 'Console Jedi', $version = self::VERSION) |
||
73 | |||
74 | /** |
||
75 | * {@inheritdoc} |
||
76 | */ |
||
77 | public function doRun(InputInterface $input, OutputInterface $output) |
||
78 | { |
||
79 | if ($this->getConfiguration() === null) { |
||
80 | $this->loadConfiguration(); |
||
81 | } |
||
82 | |||
83 | if (!in_array($this->getCommandName($input), ['environment:init', 'env:init'])) { |
||
84 | $this->initializeBitrix(); |
||
85 | } |
||
86 | |||
87 | if ($this->getConfiguration()) { |
||
88 | foreach ($this->getBitrixCommands() as $bitrixCommand) { |
||
89 | $this->add($bitrixCommand); |
||
90 | } |
||
91 | |||
92 | foreach ($this->getConfiguration()['commands'] as $command) { |
||
93 | $this->add($command); |
||
94 | } |
||
95 | } |
||
96 | |||
97 | if ($this->isBitrixLoaded() && $this->getConfiguration()['useModules'] === true) { |
||
98 | foreach ($this->getModulesCommands() as $moduleCommand) { |
||
99 | $this->add($moduleCommand); |
||
100 | } |
||
101 | } |
||
102 | |||
103 | $exitCode = parent::doRun($input, $output); |
||
104 | |||
105 | if ($this->getConfiguration() === null) { |
||
106 | $output->writeln(PHP_EOL . '<error>No configuration loaded.</error> ' |
||
107 | . 'Please run <info>init</info> command first'); |
||
108 | } else { |
||
109 | switch ($this->getBitrixStatus()) { |
||
110 | case static::BITRIX_STATUS_UNAVAILABLE: |
||
111 | $output->writeln(PHP_EOL . sprintf('<error>No Bitrix kernel found in %s.</error> ' |
||
112 | . 'Please run <info>env:init</info> command to configure', $this->getDocumentRoot())); |
||
113 | break; |
||
114 | |||
115 | case static::BITRIX_STATUS_NO_DB_CONNECTION: |
||
116 | $output->writeln(PHP_EOL . '<error>Bitrix database connection is unavailable.</error>'); |
||
117 | break; |
||
118 | |||
119 | case static::BITRIX_STATUS_COMPLETE: |
||
120 | if ($this->getCommandName($input) === null) { |
||
121 | $output->writeln(PHP_EOL . sprintf('Using Bitrix <info>kernel v%s</info>.</info>', SM_VERSION), |
||
122 | OutputInterface::VERBOSITY_VERY_VERBOSE); |
||
123 | } |
||
124 | break; |
||
125 | } |
||
126 | } |
||
127 | |||
128 | return $exitCode; |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * {@inheritdoc} |
||
133 | */ |
||
134 | protected function getDefaultCommands() |
||
141 | |||
142 | /** |
||
143 | * Gets Bitrix console commands from this package. |
||
144 | * |
||
145 | * @return Command[] |
||
146 | */ |
||
147 | protected function getBitrixCommands() |
||
148 | { |
||
149 | return array_merge( |
||
150 | [ |
||
151 | new OnCronCommand(), |
||
152 | new ExecuteCommand(), |
||
153 | new ClearCommand(), |
||
154 | new InitCommand(), |
||
155 | new ReIndexCommand(), |
||
156 | new ExportCommand(), |
||
157 | new ImportCommand(), |
||
158 | new ReIndexCommand(), |
||
159 | ], |
||
160 | Module\ModuleCommand::getCommands() |
||
161 | ); |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Gets console commands from modules. |
||
166 | * |
||
167 | * @return Command[] |
||
168 | * |
||
169 | * @throws \Bitrix\Main\LoaderException |
||
170 | */ |
||
171 | protected function getModulesCommands() |
||
172 | { |
||
173 | $commands = []; |
||
174 | |||
175 | foreach (ModuleManager::getInstalledModules() as $module) { |
||
176 | $cliFile = getLocalPath('modules/' . $module['ID'] . '/cli.php'); |
||
177 | |||
178 | if ($cliFile === false) { |
||
179 | continue; |
||
180 | } elseif (!Loader::includeModule($module['ID'])) { |
||
181 | continue; |
||
182 | } |
||
183 | |||
184 | $config = include_once $this->getDocumentRoot() . $cliFile; |
||
185 | |||
186 | if (isset($config['commands']) && is_array($config['commands'])) { |
||
187 | $commands = array_merge($commands, $config['commands']); |
||
188 | } |
||
189 | } |
||
190 | |||
191 | return $commands; |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * Loading application configuration. |
||
196 | * |
||
197 | * @param string $path Path to configuration file. |
||
198 | * |
||
199 | * @return bool |
||
200 | * |
||
201 | * @throws ConfigurationException |
||
202 | */ |
||
203 | public function loadConfiguration($path = self::CONFIG_DEFAULT_FILE) |
||
204 | { |
||
205 | if (!is_file($path)) { |
||
206 | return false; |
||
207 | } |
||
208 | |||
209 | $this->configuration = include $path; |
||
210 | |||
211 | if (!is_array($this->configuration)) { |
||
212 | throw new ConfigurationException('Configuration file ' . $path . ' must return an array'); |
||
213 | } |
||
214 | |||
215 | $filesystem = new Filesystem(); |
||
216 | |||
217 | if ($filesystem->isAbsolutePath($this->configuration['web-dir'])) { |
||
218 | $this->setDocumentRoot($this->configuration['web-dir']); |
||
219 | } else { |
||
220 | $this->setDocumentRoot($this->getRoot() . '/' . $this->configuration['web-dir']); |
||
221 | } |
||
222 | |||
223 | if (!is_dir($_SERVER['DOCUMENT_ROOT'])) { |
||
224 | return false; |
||
225 | } |
||
226 | |||
227 | return true; |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Gets application configuration. |
||
232 | * |
||
233 | * @return null|array |
||
234 | */ |
||
235 | public function getConfiguration() |
||
239 | |||
240 | /** |
||
241 | * Initialize kernel of Bitrix. |
||
242 | * |
||
243 | * @return int The status of readiness kernel. |
||
244 | */ |
||
245 | public function initializeBitrix() |
||
277 | |||
278 | /** |
||
279 | * Checks readiness of Bitrix for kernel initialize. |
||
280 | * |
||
281 | * @return bool |
||
282 | */ |
||
283 | public function checkBitrix() |
||
294 | |||
295 | /** |
||
296 | * Gets Bitrix status. |
||
297 | * |
||
298 | * @return int Value of constant `Application::BITRIX_STATUS_*`. |
||
299 | */ |
||
300 | public function getBitrixStatus() |
||
304 | |||
305 | /** |
||
306 | * Checks that the Bitrix kernel is loaded. |
||
307 | * |
||
308 | * @return bool |
||
309 | */ |
||
310 | public function isBitrixLoaded() |
||
314 | |||
315 | /** |
||
316 | * Autoloader classes of the tests. |
||
317 | * |
||
318 | * Initializes Bitrix kernel, finds and connects files in directory `vendor.module/tests/` |
||
319 | * by pattern `<class>test.php` and loading modules of tests. |
||
320 | * |
||
321 | * @throws ConfigurationException |
||
322 | */ |
||
323 | public function autoloadTests() |
||
363 | |||
364 | /** |
||
365 | * Gets root directory from which are running Console Jedi. |
||
366 | * |
||
367 | * @return string |
||
368 | */ |
||
369 | public function getRoot() |
||
373 | |||
374 | /** |
||
375 | * Sets path to the document root of site. |
||
376 | * |
||
377 | * @param string $dir Path to document root. |
||
378 | */ |
||
379 | public function setDocumentRoot($dir) |
||
383 | |||
384 | /** |
||
385 | * Gets document root of site. |
||
386 | * |
||
387 | * @return null|string |
||
388 | */ |
||
389 | public function getDocumentRoot() |
||
393 | } |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.