1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* For the full copyright and license information, please view the LICENSE.md |
4
|
|
|
* file that was distributed with this source code. |
5
|
|
|
*/ |
6
|
|
|
|
7
|
|
|
namespace Notamedia\ConsoleJedi\Application; |
8
|
|
|
|
9
|
|
|
use Bitrix\Main\DB\ConnectionException; |
10
|
|
|
use Bitrix\Main\Loader; |
11
|
|
|
use Bitrix\Main\ModuleManager; |
12
|
|
|
use Notamedia\ConsoleJedi\Agent\Command\OnCronCommand; |
13
|
|
|
use Notamedia\ConsoleJedi\Agent\Command\ExecuteCommand; |
14
|
|
|
use Notamedia\ConsoleJedi\Application\Exception\ConfigurationException; |
15
|
|
|
use Notamedia\ConsoleJedi\Cache\Command\ClearCommand; |
16
|
|
|
use Notamedia\ConsoleJedi\Environment\Command\InitCommand; |
17
|
|
|
use Notamedia\ConsoleJedi\Module\Command as Module; |
18
|
|
|
use Notamedia\ConsoleJedi\Search\Command\ReIndexCommand; |
19
|
|
|
use Notamedia\ConsoleJedi\Iblock\Command\ExportCommand; |
20
|
|
|
use Notamedia\ConsoleJedi\Iblock\Command\ImportCommand; |
21
|
|
|
use Symfony\Component\Console\Command\Command; |
22
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
23
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
24
|
|
|
use Symfony\Component\Filesystem\Filesystem; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Console Jedi application. |
28
|
|
|
* |
29
|
|
|
* @author Nik Samokhvalov <[email protected]> |
30
|
|
|
*/ |
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) |
70
|
|
|
{ |
71
|
|
|
parent::__construct($name, static::VERSION); |
72
|
|
|
} |
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() |
135
|
|
|
{ |
136
|
|
|
$commands = parent::getDefaultCommands(); |
137
|
|
|
$commands[] = new \Notamedia\ConsoleJedi\Application\Command\InitCommand(); |
138
|
|
|
|
139
|
|
|
return $commands; |
|
|
|
|
140
|
|
|
} |
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() |
236
|
|
|
{ |
237
|
|
|
return $this->configuration; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Initialize kernel of Bitrix. |
242
|
|
|
* |
243
|
|
|
* @return int The status of readiness kernel. |
244
|
|
|
*/ |
245
|
|
|
public function initializeBitrix() |
|
|
|
|
246
|
|
|
{ |
247
|
|
|
if ($this->bitrixStatus === static::BITRIX_STATUS_COMPLETE) { |
248
|
|
|
return static::BITRIX_STATUS_COMPLETE; |
249
|
|
|
} elseif (!$this->checkBitrix()) { |
250
|
|
|
return static::BITRIX_STATUS_UNAVAILABLE; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
define('NO_KEEP_STATISTIC', true); |
254
|
|
|
define('NOT_CHECK_PERMISSIONS', true); |
255
|
|
|
|
256
|
|
|
try { |
257
|
|
|
/** |
258
|
|
|
* Declare global legacy variables |
259
|
|
|
* |
260
|
|
|
* Including kernel here makes them local by default but some modules depend on them in installation class |
261
|
|
|
*/ |
262
|
|
|
global |
|
|
|
|
263
|
|
|
/** @noinspection PhpUnusedLocalVariableInspection */ |
264
|
|
|
$DB, $DBType, $DBHost, $DBLogin, $DBPassword, $DBName, $DBDebug, $DBDebugToFile, $APPLICATION, $USER, $DBSQLServerType; |
265
|
|
|
|
266
|
|
|
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php'; |
267
|
|
|
|
268
|
|
|
if (defined('B_PROLOG_INCLUDED') && B_PROLOG_INCLUDED === true) { |
269
|
|
|
$this->bitrixStatus = static::BITRIX_STATUS_COMPLETE; |
270
|
|
|
} |
271
|
|
|
} catch (ConnectionException $e) { |
|
|
|
|
272
|
|
|
$this->bitrixStatus = static::BITRIX_STATUS_NO_DB_CONNECTION; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
return $this->bitrixStatus; |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Checks readiness of Bitrix for kernel initialize. |
280
|
|
|
* |
281
|
|
|
* @return bool |
282
|
|
|
*/ |
283
|
|
|
public function checkBitrix() |
|
|
|
|
284
|
|
|
{ |
285
|
|
|
if ( |
286
|
|
|
!is_file($_SERVER['DOCUMENT_ROOT'] . '/bitrix/.settings.php') |
287
|
|
|
&& !is_file($_SERVER['DOCUMENT_ROOT'] . '/bitrix/.settings_extra.php') |
288
|
|
|
) { |
289
|
|
|
return false; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
return true; |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Gets Bitrix status. |
297
|
|
|
* |
298
|
|
|
* @return int Value of constant `Application::BITRIX_STATUS_*`. |
299
|
|
|
*/ |
300
|
|
|
public function getBitrixStatus() |
301
|
|
|
{ |
302
|
|
|
return $this->bitrixStatus; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
/** |
306
|
|
|
* Checks that the Bitrix kernel is loaded. |
307
|
|
|
* |
308
|
|
|
* @return bool |
309
|
|
|
*/ |
310
|
|
|
public function isBitrixLoaded() |
311
|
|
|
{ |
312
|
|
|
return $this->bitrixStatus === static::BITRIX_STATUS_COMPLETE; |
313
|
|
|
} |
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() |
324
|
|
|
{ |
325
|
|
|
if ($this->getConfiguration() === null) { |
326
|
|
|
$this->loadConfiguration(); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
$this->initializeBitrix(); |
330
|
|
|
|
331
|
|
|
spl_autoload_register(function ($className) { |
332
|
|
|
$file = ltrim($className, "\\"); |
333
|
|
|
$file = strtr($file, Loader::ALPHA_UPPER, Loader::ALPHA_LOWER); |
334
|
|
|
$file = str_replace('\\', '/', $file); |
335
|
|
|
|
336
|
|
|
if (substr($file, -5) === 'table') { |
337
|
|
|
$file = substr($file, 0, -5); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
$arFile = explode('/', $file); |
341
|
|
|
|
342
|
|
|
if (preg_match("#[^\\\\/a-zA-Z0-9_]#", $file)) { |
343
|
|
|
return false; |
344
|
|
|
} elseif ($arFile[0] === 'bitrix') { |
345
|
|
|
return false; |
346
|
|
|
} elseif ($arFile[2] !== 'tests') { |
347
|
|
|
return false; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
$module = array_shift($arFile) . '.' . array_shift($arFile); |
351
|
|
|
|
352
|
|
|
if (!Loader::includeModule($module)) { |
353
|
|
|
return false; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
$path = getLocalPath('/modules/' . $module . '/' . implode('/', $arFile) . '.php'); |
357
|
|
|
|
358
|
|
|
if ($path !== false) { |
359
|
|
|
include_once $this->getDocumentRoot() . $path; |
360
|
|
|
} |
361
|
|
|
}); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Gets root directory from which are running Console Jedi. |
366
|
|
|
* |
367
|
|
|
* @return string |
368
|
|
|
*/ |
369
|
|
|
public function getRoot() |
370
|
|
|
{ |
371
|
|
|
return getcwd(); |
372
|
|
|
} |
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) |
|
|
|
|
380
|
|
|
{ |
381
|
|
|
$_SERVER['DOCUMENT_ROOT'] = $this->documentRoot = $dir; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
/** |
385
|
|
|
* Gets document root of site. |
386
|
|
|
* |
387
|
|
|
* @return null|string |
388
|
|
|
*/ |
389
|
|
|
public function getDocumentRoot() |
390
|
|
|
{ |
391
|
|
|
return $this->documentRoot; |
392
|
|
|
} |
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.