1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org) |
4
|
|
|
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) |
5
|
|
|
* |
6
|
|
|
* Licensed under The MIT License |
7
|
|
|
* For full copyright and license information, please see the LICENSE.txt |
8
|
|
|
* Redistributions of files must retain the above copyright notice. |
9
|
|
|
* |
10
|
|
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) |
11
|
|
|
* @link https://cakephp.org CakePHP(tm) Project |
12
|
|
|
* @since 1.2.0 |
13
|
|
|
* @license https://opensource.org/licenses/mit-license.php MIT License |
14
|
|
|
*/ |
15
|
|
|
namespace Cake\Console; |
16
|
|
|
|
17
|
|
|
use Cake\Console\Exception\ConsoleException; |
18
|
|
|
use Cake\Console\Exception\StopException; |
19
|
|
|
use Cake\Core\App; |
20
|
|
|
use Cake\Datasource\ModelAwareTrait; |
21
|
|
|
use Cake\Filesystem\File; |
22
|
|
|
use Cake\Log\LogTrait; |
23
|
|
|
use Cake\ORM\Locator\LocatorAwareTrait; |
24
|
|
|
use Cake\ORM\Locator\LocatorInterface; |
25
|
|
|
use Cake\Utility\Inflector; |
26
|
|
|
use Cake\Utility\MergeVariablesTrait; |
27
|
|
|
use Cake\Utility\Text; |
28
|
|
|
use ReflectionException; |
29
|
|
|
use ReflectionMethod; |
30
|
|
|
use RuntimeException; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Base class for command-line utilities for automating programmer chores. |
34
|
|
|
* |
35
|
|
|
* Is the equivalent of Cake\Controller\Controller on the command line. |
36
|
|
|
* |
37
|
|
|
* @method int|bool|null main(...$args) |
38
|
|
|
*/ |
39
|
|
|
class Shell |
40
|
|
|
{ |
41
|
|
|
use LocatorAwareTrait; |
42
|
|
|
use LogTrait; |
43
|
|
|
use MergeVariablesTrait; |
44
|
|
|
use ModelAwareTrait; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Default error code |
48
|
|
|
* |
49
|
|
|
* @var int |
50
|
|
|
*/ |
51
|
|
|
const CODE_ERROR = 1; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Default success code |
55
|
|
|
* |
56
|
|
|
* @var int |
57
|
|
|
*/ |
58
|
|
|
const CODE_SUCCESS = 0; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Output constant making verbose shells. |
62
|
|
|
* |
63
|
|
|
* @var int |
64
|
|
|
*/ |
65
|
|
|
const VERBOSE = ConsoleIo::VERBOSE; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Output constant for making normal shells. |
69
|
|
|
* |
70
|
|
|
* @var int |
71
|
|
|
*/ |
72
|
|
|
const NORMAL = ConsoleIo::NORMAL; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Output constants for making quiet shells. |
76
|
|
|
* |
77
|
|
|
* @var int |
78
|
|
|
*/ |
79
|
|
|
const QUIET = ConsoleIo::QUIET; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* An instance of ConsoleOptionParser that has been configured for this class. |
83
|
|
|
* |
84
|
|
|
* @var \Cake\Console\ConsoleOptionParser |
85
|
|
|
*/ |
86
|
|
|
public $OptionParser; |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* If true, the script will ask for permission to perform actions. |
90
|
|
|
* |
91
|
|
|
* @var bool |
92
|
|
|
*/ |
93
|
|
|
public $interactive = true; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Contains command switches parsed from the command line. |
97
|
|
|
* |
98
|
|
|
* @var array |
99
|
|
|
*/ |
100
|
|
|
public $params = []; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* The command (method/task) that is being run. |
104
|
|
|
* |
105
|
|
|
* @var string |
106
|
|
|
*/ |
107
|
|
|
public $command; |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Contains arguments parsed from the command line. |
111
|
|
|
* |
112
|
|
|
* @var array |
113
|
|
|
*/ |
114
|
|
|
public $args = []; |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* The name of the shell in camelized. |
118
|
|
|
* |
119
|
|
|
* @var string |
120
|
|
|
*/ |
121
|
|
|
public $name; |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* The name of the plugin the shell belongs to. |
125
|
|
|
* Is automatically set by ShellDispatcher when a shell is constructed. |
126
|
|
|
* |
127
|
|
|
* @var string |
128
|
|
|
*/ |
129
|
|
|
public $plugin; |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Contains tasks to load and instantiate |
133
|
|
|
* |
134
|
|
|
* @var array|bool |
135
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Shell::$tasks |
136
|
|
|
*/ |
137
|
|
|
public $tasks = []; |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Contains the loaded tasks |
141
|
|
|
* |
142
|
|
|
* @var array |
143
|
|
|
*/ |
144
|
|
|
public $taskNames = []; |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Task Collection for the command, used to create Tasks. |
148
|
|
|
* |
149
|
|
|
* @var \Cake\Console\TaskRegistry |
150
|
|
|
*/ |
151
|
|
|
public $Tasks; |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Normalized map of tasks. |
155
|
|
|
* |
156
|
|
|
* @var array |
157
|
|
|
*/ |
158
|
|
|
protected $_taskMap = []; |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* ConsoleIo instance. |
162
|
|
|
* |
163
|
|
|
* @var \Cake\Console\ConsoleIo |
164
|
|
|
*/ |
165
|
|
|
protected $_io; |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* The root command name used when generating help output. |
169
|
|
|
* |
170
|
|
|
* @var string |
171
|
|
|
*/ |
172
|
|
|
protected $rootName = 'cake'; |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Constructs this Shell instance. |
176
|
|
|
* |
177
|
|
|
* @param \Cake\Console\ConsoleIo|null $io An io instance. |
178
|
|
|
* @param \Cake\ORM\Locator\LocatorInterface|null $locator Table locator instance. |
179
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Shell |
180
|
|
|
*/ |
181
|
|
|
public function __construct(ConsoleIo $io = null, LocatorInterface $locator = null) |
182
|
|
|
{ |
183
|
|
|
if (!$this->name) { |
184
|
|
|
list(, $class) = namespaceSplit(get_class($this)); |
185
|
|
|
$this->name = str_replace(['Shell', 'Task'], '', $class); |
186
|
|
|
} |
187
|
|
|
$this->_io = $io ?: new ConsoleIo(); |
188
|
|
|
$this->_tableLocator = $locator; |
189
|
|
|
|
190
|
|
|
$this->modelFactory('Table', [$this->getTableLocator(), 'get']); |
191
|
|
|
$this->Tasks = new TaskRegistry($this); |
192
|
|
|
|
193
|
|
|
$this->_mergeVars( |
194
|
|
|
['tasks'], |
195
|
|
|
['associative' => ['tasks']] |
196
|
|
|
); |
197
|
|
|
|
198
|
|
|
if (isset($this->modelClass)) { |
199
|
|
|
$this->loadModel(); |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Set the root command name for help output. |
205
|
|
|
* |
206
|
|
|
* @param string $name The name of the root command. |
207
|
|
|
* @return $this |
208
|
|
|
*/ |
209
|
|
|
public function setRootName($name) |
210
|
|
|
{ |
211
|
|
|
$this->rootName = (string)$name; |
212
|
|
|
|
213
|
|
|
return $this; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Get the io object for this shell. |
218
|
|
|
* |
219
|
|
|
* @return \Cake\Console\ConsoleIo The current ConsoleIo object. |
220
|
|
|
*/ |
221
|
|
|
public function getIo() |
222
|
|
|
{ |
223
|
|
|
return $this->_io; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Set the io object for this shell. |
228
|
|
|
* |
229
|
|
|
* @param \Cake\Console\ConsoleIo $io The ConsoleIo object to use. |
230
|
|
|
* @return void |
231
|
|
|
*/ |
232
|
|
|
public function setIo(ConsoleIo $io) |
233
|
|
|
{ |
234
|
|
|
$this->_io = $io; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* Get/Set the io object for this shell. |
239
|
|
|
* |
240
|
|
|
* @deprecated 3.5.0 Use getIo()/setIo() instead. |
241
|
|
|
* @param \Cake\Console\ConsoleIo|null $io The ConsoleIo object to use. |
242
|
|
|
* @return \Cake\Console\ConsoleIo The current ConsoleIo object. |
243
|
|
|
*/ |
244
|
|
|
public function io(ConsoleIo $io = null) |
245
|
|
|
{ |
246
|
|
|
deprecationWarning( |
247
|
|
|
'Shell::io() is deprecated. ' . |
248
|
|
|
'Use Shell::setIo()/getIo() instead.' |
249
|
|
|
); |
250
|
|
|
if ($io !== null) { |
251
|
|
|
$this->_io = $io; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
return $this->_io; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Initializes the Shell |
259
|
|
|
* acts as constructor for subclasses |
260
|
|
|
* allows configuration of tasks prior to shell execution |
261
|
|
|
* |
262
|
|
|
* @return void |
263
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Cake\Console\ConsoleOptionParser::initialize |
264
|
|
|
*/ |
265
|
|
|
public function initialize() |
266
|
|
|
{ |
267
|
|
|
$this->loadTasks(); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* Starts up the Shell and displays the welcome message. |
272
|
|
|
* Allows for checking and configuring prior to command or main execution |
273
|
|
|
* |
274
|
|
|
* Override this method if you want to remove the welcome information, |
275
|
|
|
* or otherwise modify the pre-command flow. |
276
|
|
|
* |
277
|
|
|
* @return void |
278
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Cake\Console\ConsoleOptionParser::startup |
279
|
|
|
*/ |
280
|
|
|
public function startup() |
281
|
|
|
{ |
282
|
|
|
if (!$this->param('requested')) { |
283
|
|
|
$this->_welcome(); |
284
|
|
|
} |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Displays a header for the shell |
289
|
|
|
* |
290
|
|
|
* @return void |
291
|
|
|
*/ |
292
|
|
|
protected function _welcome() |
293
|
|
|
{ |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* Loads tasks defined in public $tasks |
298
|
|
|
* |
299
|
|
|
* @return bool |
300
|
|
|
*/ |
301
|
|
|
public function loadTasks() |
302
|
|
|
{ |
303
|
|
|
if ($this->tasks === true || empty($this->tasks) || empty($this->Tasks)) { |
304
|
|
|
return true; |
305
|
|
|
} |
306
|
|
|
$this->_taskMap = $this->Tasks->normalizeArray((array)$this->tasks); |
307
|
|
|
$this->taskNames = array_merge($this->taskNames, array_keys($this->_taskMap)); |
308
|
|
|
|
309
|
|
|
$this->_validateTasks(); |
310
|
|
|
|
311
|
|
|
return true; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Checks that the tasks in the task map are actually available |
316
|
|
|
* |
317
|
|
|
* @throws \RuntimeException |
318
|
|
|
* @return void |
319
|
|
|
*/ |
320
|
|
|
protected function _validateTasks() |
321
|
|
|
{ |
322
|
|
|
foreach ($this->_taskMap as $taskName => $task) { |
323
|
|
|
$class = App::className($task['class'], 'Shell/Task', 'Task'); |
324
|
|
|
if (!class_exists($class)) { |
325
|
|
|
throw new RuntimeException(sprintf( |
326
|
|
|
'Task `%s` not found. Maybe you made a typo or a plugin is missing or not loaded?', |
327
|
|
|
$taskName |
328
|
|
|
)); |
329
|
|
|
} |
330
|
|
|
} |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/** |
334
|
|
|
* Check to see if this shell has a task with the provided name. |
335
|
|
|
* |
336
|
|
|
* @param string $task The task name to check. |
337
|
|
|
* @return bool Success |
338
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#shell-tasks |
339
|
|
|
*/ |
340
|
|
|
public function hasTask($task) |
341
|
|
|
{ |
342
|
|
|
return isset($this->_taskMap[Inflector::camelize($task)]); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* Check to see if this shell has a callable method by the given name. |
347
|
|
|
* |
348
|
|
|
* @param string $name The method name to check. |
349
|
|
|
* @return bool |
350
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#shell-tasks |
351
|
|
|
*/ |
352
|
|
|
public function hasMethod($name) |
353
|
|
|
{ |
354
|
|
|
try { |
355
|
|
|
$method = new ReflectionMethod($this, $name); |
356
|
|
|
if (!$method->isPublic()) { |
357
|
|
|
return false; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
return $method->getDeclaringClass()->name !== 'Cake\Console\Shell'; |
361
|
|
|
} catch (ReflectionException $e) { |
362
|
|
|
return false; |
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Dispatch a command to another Shell. Similar to Object::requestAction() |
368
|
|
|
* but intended for running shells from other shells. |
369
|
|
|
* |
370
|
|
|
* ### Usage: |
371
|
|
|
* |
372
|
|
|
* With a string command: |
373
|
|
|
* |
374
|
|
|
* ``` |
375
|
|
|
* return $this->dispatchShell('schema create DbAcl'); |
376
|
|
|
* ``` |
377
|
|
|
* |
378
|
|
|
* Avoid using this form if you have string arguments, with spaces in them. |
379
|
|
|
* The dispatched will be invoked incorrectly. Only use this form for simple |
380
|
|
|
* command dispatching. |
381
|
|
|
* |
382
|
|
|
* With an array command: |
383
|
|
|
* |
384
|
|
|
* ``` |
385
|
|
|
* return $this->dispatchShell('schema', 'create', 'i18n', '--dry'); |
386
|
|
|
* ``` |
387
|
|
|
* |
388
|
|
|
* With an array having two key / value pairs: |
389
|
|
|
* - `command` can accept either a string or an array. Represents the command to dispatch |
390
|
|
|
* - `extra` can accept an array of extra parameters to pass on to the dispatcher. This |
391
|
|
|
* parameters will be available in the `param` property of the called `Shell` |
392
|
|
|
* |
393
|
|
|
* `return $this->dispatchShell([ |
394
|
|
|
* 'command' => 'schema create DbAcl', |
395
|
|
|
* 'extra' => ['param' => 'value'] |
396
|
|
|
* ]);` |
397
|
|
|
* |
398
|
|
|
* or |
399
|
|
|
* |
400
|
|
|
* `return $this->dispatchShell([ |
401
|
|
|
* 'command' => ['schema', 'create', 'DbAcl'], |
402
|
|
|
* 'extra' => ['param' => 'value'] |
403
|
|
|
* ]);` |
404
|
|
|
* |
405
|
|
|
* @return int The cli command exit code. 0 is success. |
406
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#invoking-other-shells-from-your-shell |
407
|
|
|
*/ |
408
|
|
|
public function dispatchShell() |
409
|
|
|
{ |
410
|
|
|
list($args, $extra) = $this->parseDispatchArguments(func_get_args()); |
411
|
|
|
|
412
|
|
|
if (!isset($extra['requested'])) { |
413
|
|
|
$extra['requested'] = true; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
$dispatcher = new ShellDispatcher($args, false); |
417
|
|
|
|
418
|
|
|
return $dispatcher->dispatch($extra); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Parses the arguments for the dispatchShell() method. |
423
|
|
|
* |
424
|
|
|
* @param array $args Arguments fetch from the dispatchShell() method with |
425
|
|
|
* func_get_args() |
426
|
|
|
* @return array First value has to be an array of the command arguments. |
427
|
|
|
* Second value has to be an array of extra parameter to pass on to the dispatcher |
428
|
|
|
*/ |
429
|
|
|
public function parseDispatchArguments($args) |
430
|
|
|
{ |
431
|
|
|
$extra = []; |
432
|
|
|
|
433
|
|
|
if (is_string($args[0]) && count($args) === 1) { |
434
|
|
|
$args = explode(' ', $args[0]); |
435
|
|
|
|
436
|
|
|
return [$args, $extra]; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
if (is_array($args[0]) && !empty($args[0]['command'])) { |
440
|
|
|
$command = $args[0]['command']; |
441
|
|
|
if (is_string($command)) { |
442
|
|
|
$command = explode(' ', $command); |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
if (!empty($args[0]['extra'])) { |
446
|
|
|
$extra = $args[0]['extra']; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
return [$command, $extra]; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
return [$args, $extra]; |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
/** |
456
|
|
|
* Runs the Shell with the provided argv. |
457
|
|
|
* |
458
|
|
|
* Delegates calls to Tasks and resolves methods inside the class. Commands are looked |
459
|
|
|
* up with the following order: |
460
|
|
|
* |
461
|
|
|
* - Method on the shell. |
462
|
|
|
* - Matching task name. |
463
|
|
|
* - `main()` method. |
464
|
|
|
* |
465
|
|
|
* If a shell implements a `main()` method, all missing method calls will be sent to |
466
|
|
|
* `main()` with the original method name in the argv. |
467
|
|
|
* |
468
|
|
|
* For tasks to be invoked they *must* be exposed as subcommands. If you define any subcommands, |
469
|
|
|
* you must define all the subcommands your shell needs, whether they be methods on this class |
470
|
|
|
* or methods on tasks. |
471
|
|
|
* |
472
|
|
|
* @param array $argv Array of arguments to run the shell with. This array should be missing the shell name. |
473
|
|
|
* @param bool $autoMethod Set to true to allow any public method to be called even if it |
474
|
|
|
* was not defined as a subcommand. This is used by ShellDispatcher to make building simple shells easy. |
475
|
|
|
* @param array $extra Extra parameters that you can manually pass to the Shell |
476
|
|
|
* to be dispatched. |
477
|
|
|
* Built-in extra parameter is : |
478
|
|
|
* - `requested` : if used, will prevent the Shell welcome message to be displayed |
479
|
|
|
* @return int|bool|null |
480
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#the-cakephp-console |
481
|
|
|
*/ |
482
|
|
|
public function runCommand($argv, $autoMethod = false, $extra = []) |
483
|
|
|
{ |
484
|
|
|
$command = isset($argv[0]) ? Inflector::underscore($argv[0]) : null; |
485
|
|
|
$this->OptionParser = $this->getOptionParser(); |
486
|
|
|
try { |
487
|
|
|
list($this->params, $this->args) = $this->OptionParser->parse($argv); |
488
|
|
|
} catch (ConsoleException $e) { |
489
|
|
|
$this->err('Error: ' . $e->getMessage()); |
490
|
|
|
|
491
|
|
|
return false; |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
if (!empty($extra) && is_array($extra)) { |
495
|
|
|
$this->params = array_merge($this->params, $extra); |
496
|
|
|
} |
497
|
|
|
$this->_setOutputLevel(); |
498
|
|
|
$this->command = $command; |
499
|
|
|
if (!empty($this->params['help'])) { |
500
|
|
|
return $this->_displayHelp($command); |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
$subcommands = $this->OptionParser->subcommands(); |
504
|
|
|
$method = Inflector::camelize($command); |
505
|
|
|
$isMethod = $this->hasMethod($method); |
506
|
|
|
|
507
|
|
|
if ($isMethod && $autoMethod && count($subcommands) === 0) { |
508
|
|
|
array_shift($this->args); |
509
|
|
|
$this->startup(); |
510
|
|
|
|
511
|
|
|
return $this->$method(...$this->args); |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
if ($isMethod && isset($subcommands[$command])) { |
515
|
|
|
$this->startup(); |
516
|
|
|
|
517
|
|
|
return $this->$method(...$this->args); |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
if ($this->hasTask($command) && isset($subcommands[$command])) { |
521
|
|
|
$this->startup(); |
522
|
|
|
array_shift($argv); |
523
|
|
|
|
524
|
|
|
return $this->{$method}->runCommand($argv, false, ['requested' => true]); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
if ($this->hasMethod('main')) { |
528
|
|
|
$this->command = 'main'; |
529
|
|
|
$this->startup(); |
530
|
|
|
|
531
|
|
|
return $this->main(...$this->args); |
532
|
|
|
} |
533
|
|
|
|
534
|
|
|
$this->err('No subcommand provided. Choose one of the available subcommands.', 2); |
535
|
|
|
$this->_io->err($this->OptionParser->help($command)); |
536
|
|
|
|
537
|
|
|
return false; |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
/** |
541
|
|
|
* Set the output level based on the parameters. |
542
|
|
|
* |
543
|
|
|
* This reconfigures both the output level for out() |
544
|
|
|
* and the configured stdout/stderr logging |
545
|
|
|
* |
546
|
|
|
* @return void |
547
|
|
|
*/ |
548
|
|
|
protected function _setOutputLevel() |
549
|
|
|
{ |
550
|
|
|
$this->_io->setLoggers(ConsoleIo::NORMAL); |
551
|
|
View Code Duplication |
if (!empty($this->params['quiet'])) { |
552
|
|
|
$this->_io->level(ConsoleIo::QUIET); |
553
|
|
|
$this->_io->setLoggers(ConsoleIo::QUIET); |
554
|
|
|
} |
555
|
|
View Code Duplication |
if (!empty($this->params['verbose'])) { |
556
|
|
|
$this->_io->level(ConsoleIo::VERBOSE); |
557
|
|
|
$this->_io->setLoggers(ConsoleIo::VERBOSE); |
558
|
|
|
} |
559
|
|
|
} |
560
|
|
|
|
561
|
|
|
/** |
562
|
|
|
* Display the help in the correct format |
563
|
|
|
* |
564
|
|
|
* @param string $command The command to get help for. |
565
|
|
|
* @return int|bool The number of bytes returned from writing to stdout. |
566
|
|
|
*/ |
567
|
|
|
protected function _displayHelp($command) |
568
|
|
|
{ |
569
|
|
|
$format = 'text'; |
570
|
|
|
if (!empty($this->args[0]) && $this->args[0] === 'xml') { |
571
|
|
|
$format = 'xml'; |
572
|
|
|
$this->_io->setOutputAs(ConsoleOutput::RAW); |
573
|
|
|
} else { |
574
|
|
|
$this->_welcome(); |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
$subcommands = $this->OptionParser->subcommands(); |
578
|
|
|
$command = isset($subcommands[$command]) ? $command : null; |
579
|
|
|
|
580
|
|
|
return $this->out($this->OptionParser->help($command, $format)); |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
/** |
584
|
|
|
* Gets the option parser instance and configures it. |
585
|
|
|
* |
586
|
|
|
* By overriding this method you can configure the ConsoleOptionParser before returning it. |
587
|
|
|
* |
588
|
|
|
* @return \Cake\Console\ConsoleOptionParser |
589
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#configuring-options-and-generating-help |
590
|
|
|
*/ |
591
|
|
|
public function getOptionParser() |
592
|
|
|
{ |
593
|
|
|
$name = ($this->plugin ? $this->plugin . '.' : '') . $this->name; |
594
|
|
|
$parser = new ConsoleOptionParser($name); |
595
|
|
|
$parser->setRootName($this->rootName); |
596
|
|
|
|
597
|
|
|
return $parser; |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
/** |
601
|
|
|
* Overload get for lazy building of tasks |
602
|
|
|
* |
603
|
|
|
* @param string $name The task to get. |
604
|
|
|
* @return \Cake\Console\Shell Object of Task |
605
|
|
|
*/ |
606
|
|
|
public function __get($name) |
607
|
|
|
{ |
608
|
|
|
if (empty($this->{$name}) && in_array($name, $this->taskNames)) { |
609
|
|
|
$properties = $this->_taskMap[$name]; |
610
|
|
|
$this->{$name} = $this->Tasks->load($properties['class'], $properties['config']); |
611
|
|
|
$this->{$name}->args =& $this->args; |
612
|
|
|
$this->{$name}->params =& $this->params; |
613
|
|
|
$this->{$name}->initialize(); |
614
|
|
|
$this->{$name}->loadTasks(); |
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
return $this->{$name}; |
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
/** |
621
|
|
|
* Safely access the values in $this->params. |
622
|
|
|
* |
623
|
|
|
* @param string $name The name of the parameter to get. |
624
|
|
|
* @return string|bool|null Value. Will return null if it doesn't exist. |
625
|
|
|
*/ |
626
|
|
|
public function param($name) |
627
|
|
|
{ |
628
|
|
|
if (!isset($this->params[$name])) { |
629
|
|
|
return null; |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
return $this->params[$name]; |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
/** |
636
|
|
|
* Prompts the user for input, and returns it. |
637
|
|
|
* |
638
|
|
|
* @param string $prompt Prompt text. |
639
|
|
|
* @param string|array|null $options Array or string of options. |
640
|
|
|
* @param string|null $default Default input value. |
641
|
|
|
* @return string|null Either the default value, or the user-provided input. |
642
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Shell::in |
643
|
|
|
*/ |
644
|
|
|
public function in($prompt, $options = null, $default = null) |
645
|
|
|
{ |
646
|
|
|
if (!$this->interactive) { |
647
|
|
|
return $default; |
648
|
|
|
} |
649
|
|
|
if ($options) { |
650
|
|
|
return $this->_io->askChoice($prompt, $options, $default); |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
return $this->_io->ask($prompt, $default); |
654
|
|
|
} |
655
|
|
|
|
656
|
|
|
/** |
657
|
|
|
* Wrap a block of text. |
658
|
|
|
* Allows you to set the width, and indenting on a block of text. |
659
|
|
|
* |
660
|
|
|
* ### Options |
661
|
|
|
* |
662
|
|
|
* - `width` The width to wrap to. Defaults to 72 |
663
|
|
|
* - `wordWrap` Only wrap on words breaks (spaces) Defaults to true. |
664
|
|
|
* - `indent` Indent the text with the string provided. Defaults to null. |
665
|
|
|
* |
666
|
|
|
* @param string $text Text the text to format. |
667
|
|
|
* @param int|array $options Array of options to use, or an integer to wrap the text to. |
668
|
|
|
* @return string Wrapped / indented text |
669
|
|
|
* @see \Cake\Utility\Text::wrap() |
670
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Shell::wrapText |
671
|
|
|
*/ |
672
|
|
|
public function wrapText($text, $options = []) |
673
|
|
|
{ |
674
|
|
|
return Text::wrap($text, $options); |
675
|
|
|
} |
676
|
|
|
|
677
|
|
|
/** |
678
|
|
|
* Output at the verbose level. |
679
|
|
|
* |
680
|
|
|
* @param string|string[] $message A string or an array of strings to output |
681
|
|
|
* @param int $newlines Number of newlines to append |
682
|
|
|
* @return int|bool The number of bytes returned from writing to stdout. |
683
|
|
|
*/ |
684
|
|
|
public function verbose($message, $newlines = 1) |
685
|
|
|
{ |
686
|
|
|
return $this->_io->verbose($message, $newlines); |
687
|
|
|
} |
688
|
|
|
|
689
|
|
|
/** |
690
|
|
|
* Output at all levels. |
691
|
|
|
* |
692
|
|
|
* @param string|string[] $message A string or an array of strings to output |
693
|
|
|
* @param int $newlines Number of newlines to append |
694
|
|
|
* @return int|bool The number of bytes returned from writing to stdout. |
695
|
|
|
*/ |
696
|
|
|
public function quiet($message, $newlines = 1) |
697
|
|
|
{ |
698
|
|
|
return $this->_io->quiet($message, $newlines); |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
/** |
702
|
|
|
* Outputs a single or multiple messages to stdout. If no parameters |
703
|
|
|
* are passed outputs just a newline. |
704
|
|
|
* |
705
|
|
|
* ### Output levels |
706
|
|
|
* |
707
|
|
|
* There are 3 built-in output level. Shell::QUIET, Shell::NORMAL, Shell::VERBOSE. |
708
|
|
|
* The verbose and quiet output levels, map to the `verbose` and `quiet` output switches |
709
|
|
|
* present in most shells. Using Shell::QUIET for a message means it will always display. |
710
|
|
|
* While using Shell::VERBOSE means it will only display when verbose output is toggled. |
711
|
|
|
* |
712
|
|
|
* @param string|string[]|null $message A string or an array of strings to output |
713
|
|
|
* @param int $newlines Number of newlines to append |
714
|
|
|
* @param int $level The message's output level, see above. |
715
|
|
|
* @return int|bool The number of bytes returned from writing to stdout. |
716
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Shell::out |
717
|
|
|
*/ |
718
|
|
|
public function out($message = null, $newlines = 1, $level = Shell::NORMAL) |
719
|
|
|
{ |
720
|
|
|
return $this->_io->out($message, $newlines, $level); |
|
|
|
|
721
|
|
|
} |
722
|
|
|
|
723
|
|
|
/** |
724
|
|
|
* Outputs a single or multiple error messages to stderr. If no parameters |
725
|
|
|
* are passed outputs just a newline. |
726
|
|
|
* |
727
|
|
|
* @param string|string[]|null $message A string or an array of strings to output |
728
|
|
|
* @param int $newlines Number of newlines to append |
729
|
|
|
* @return int|bool The number of bytes returned from writing to stderr. |
730
|
|
|
*/ |
731
|
|
|
public function err($message = null, $newlines = 1) |
732
|
|
|
{ |
733
|
|
|
return $this->_io->error($message, $newlines); |
734
|
|
|
} |
735
|
|
|
|
736
|
|
|
/** |
737
|
|
|
* Convenience method for out() that wraps message between <info /> tag |
738
|
|
|
* |
739
|
|
|
* @param string|string[]|null $message A string or an array of strings to output |
740
|
|
|
* @param int $newlines Number of newlines to append |
741
|
|
|
* @param int $level The message's output level, see above. |
742
|
|
|
* @return int|bool The number of bytes returned from writing to stdout. |
743
|
|
|
* @see https://book.cakephp.org/3/en/console-and-shells.html#Shell::out |
744
|
|
|
*/ |
745
|
|
|
public function info($message = null, $newlines = 1, $level = Shell::NORMAL) |
746
|
|
|
{ |
747
|
|
|
return $this->_io->info($message, $newlines, $level); |
748
|
|
|
} |
749
|
|
|
|
750
|
|
|
/** |
751
|
|
|
* Convenience method for err() that wraps message between <warning /> tag |
752
|
|
|
* |
753
|
|
|
* @param string|string[]|null $message A string or an array of strings to output |
754
|
|
|
* @param int $newlines Number of newlines to append |
755
|
|
|
* @return int|bool The number of bytes returned from writing to stderr. |
756
|
|
|
* @see https://book.cakephp.org/3/en/console-and-shells.html#Shell::err |
757
|
|
|
*/ |
758
|
|
|
public function warn($message = null, $newlines = 1) |
759
|
|
|
{ |
760
|
|
|
return $this->_io->warning($message, $newlines); |
761
|
|
|
} |
762
|
|
|
|
763
|
|
|
/** |
764
|
|
|
* Convenience method for out() that wraps message between <success /> tag |
765
|
|
|
* |
766
|
|
|
* @param string|string[]|null $message A string or an array of strings to output |
767
|
|
|
* @param int $newlines Number of newlines to append |
768
|
|
|
* @param int $level The message's output level, see above. |
769
|
|
|
* @return int|bool The number of bytes returned from writing to stdout. |
770
|
|
|
* @see https://book.cakephp.org/3/en/console-and-shells.html#Shell::out |
771
|
|
|
*/ |
772
|
|
|
public function success($message = null, $newlines = 1, $level = Shell::NORMAL) |
773
|
|
|
{ |
774
|
|
|
return $this->_io->success($message, $newlines, $level); |
775
|
|
|
} |
776
|
|
|
|
777
|
|
|
/** |
778
|
|
|
* Wraps a message with a given message type, e.g. <warning> |
779
|
|
|
* |
780
|
|
|
* @param string $messageType The message type, e.g. "warning". |
781
|
|
|
* @param string|array $message The message to wrap. |
782
|
|
|
* @return array|string The message wrapped with the given message type. |
783
|
|
|
* @deprecated 3.6.0 Will be removed in 4.0.0 as it is no longer in use. |
784
|
|
|
*/ |
785
|
|
|
protected function wrapMessageWithType($messageType, $message) |
786
|
|
|
{ |
787
|
|
|
deprecationWarning( |
788
|
|
|
'Shell::wrapMessageWithType() is deprecated. ' . |
789
|
|
|
'Use output methods on ConsoleIo instead.' |
790
|
|
|
); |
791
|
|
|
if (is_array($message)) { |
792
|
|
|
foreach ($message as $k => $v) { |
793
|
|
|
$message[$k] = "<$messageType>" . $v . "</$messageType>"; |
794
|
|
|
} |
795
|
|
|
} else { |
796
|
|
|
$message = "<$messageType>" . $message . "</$messageType>"; |
797
|
|
|
} |
798
|
|
|
|
799
|
|
|
return $message; |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
/** |
803
|
|
|
* Returns a single or multiple linefeeds sequences. |
804
|
|
|
* |
805
|
|
|
* @param int $multiplier Number of times the linefeed sequence should be repeated |
806
|
|
|
* @return string |
807
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Shell::nl |
808
|
|
|
*/ |
809
|
|
|
public function nl($multiplier = 1) |
810
|
|
|
{ |
811
|
|
|
return $this->_io->nl($multiplier); |
812
|
|
|
} |
813
|
|
|
|
814
|
|
|
/** |
815
|
|
|
* Outputs a series of minus characters to the standard output, acts as a visual separator. |
816
|
|
|
* |
817
|
|
|
* @param int $newlines Number of newlines to pre- and append |
818
|
|
|
* @param int $width Width of the line, defaults to 63 |
819
|
|
|
* @return void |
820
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Shell::hr |
821
|
|
|
*/ |
822
|
|
|
public function hr($newlines = 0, $width = 63) |
823
|
|
|
{ |
824
|
|
|
$this->_io->hr($newlines, $width); |
825
|
|
|
} |
826
|
|
|
|
827
|
|
|
/** |
828
|
|
|
* Displays a formatted error message |
829
|
|
|
* and exits the application with status code 1 |
830
|
|
|
* |
831
|
|
|
* @param string $message The error message |
832
|
|
|
* @param int $exitCode The exit code for the shell task. |
833
|
|
|
* @throws \Cake\Console\Exception\StopException |
834
|
|
|
* @return void |
835
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#styling-output |
836
|
|
|
*/ |
837
|
|
|
public function abort($message, $exitCode = self::CODE_ERROR) |
838
|
|
|
{ |
839
|
|
|
$this->_io->err('<error>' . $message . '</error>'); |
840
|
|
|
throw new StopException($message, $exitCode); |
841
|
|
|
} |
842
|
|
|
|
843
|
|
|
/** |
844
|
|
|
* Displays a formatted error message |
845
|
|
|
* and exits the application with status code 1 |
846
|
|
|
* |
847
|
|
|
* @param string $title Title of the error |
848
|
|
|
* @param string|null $message An optional error message |
849
|
|
|
* @param int $exitCode The exit code for the shell task. |
850
|
|
|
* @throws \Cake\Console\Exception\StopException |
851
|
|
|
* @return int Error code |
852
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#styling-output |
853
|
|
|
* @deprecated 3.2.0 Use Shell::abort() instead. |
854
|
|
|
*/ |
855
|
|
|
public function error($title, $message = null, $exitCode = self::CODE_ERROR) |
856
|
|
|
{ |
857
|
|
|
deprecationWarning('Shell::error() is deprecated. `Use Shell::abort() instead.'); |
858
|
|
|
$this->_io->err(sprintf('<error>Error:</error> %s', $title)); |
859
|
|
|
|
860
|
|
|
if (!empty($message)) { |
861
|
|
|
$this->_io->err($message); |
862
|
|
|
} |
863
|
|
|
|
864
|
|
|
$this->_stop($exitCode); |
865
|
|
|
|
866
|
|
|
return $exitCode; |
867
|
|
|
} |
868
|
|
|
|
869
|
|
|
/** |
870
|
|
|
* Clear the console |
871
|
|
|
* |
872
|
|
|
* @return void |
873
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#console-output |
874
|
|
|
*/ |
875
|
|
|
public function clear() |
876
|
|
|
{ |
877
|
|
|
if (!empty($this->params['noclear'])) { |
878
|
|
|
return; |
879
|
|
|
} |
880
|
|
|
|
881
|
|
|
if (DIRECTORY_SEPARATOR === '/') { |
882
|
|
|
passthru('clear'); |
883
|
|
|
} else { |
884
|
|
|
passthru('cls'); |
885
|
|
|
} |
886
|
|
|
} |
887
|
|
|
|
888
|
|
|
/** |
889
|
|
|
* Creates a file at given path |
890
|
|
|
* |
891
|
|
|
* @param string $path Where to put the file. |
892
|
|
|
* @param string $contents Content to put in the file. |
893
|
|
|
* @return bool Success |
894
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#creating-files |
895
|
|
|
*/ |
896
|
|
|
public function createFile($path, $contents) |
897
|
|
|
{ |
898
|
|
|
$path = str_replace(DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, $path); |
899
|
|
|
|
900
|
|
|
$this->_io->out(); |
901
|
|
|
|
902
|
|
|
$fileExists = is_file($path); |
903
|
|
|
if ($fileExists && empty($this->params['force']) && !$this->interactive) { |
904
|
|
|
$this->_io->out('<warning>File exists, skipping</warning>.'); |
905
|
|
|
|
906
|
|
|
return false; |
907
|
|
|
} |
908
|
|
|
|
909
|
|
|
if ($fileExists && $this->interactive && empty($this->params['force'])) { |
910
|
|
|
$this->_io->out(sprintf('<warning>File `%s` exists</warning>', $path)); |
911
|
|
|
$key = $this->_io->askChoice('Do you want to overwrite?', ['y', 'n', 'a', 'q'], 'n'); |
912
|
|
|
|
913
|
|
|
if (strtolower($key) === 'q') { |
914
|
|
|
$this->_io->out('<error>Quitting</error>.', 2); |
915
|
|
|
$this->_stop(); |
916
|
|
|
|
917
|
|
|
return false; |
918
|
|
|
} |
919
|
|
|
if (strtolower($key) === 'a') { |
920
|
|
|
$this->params['force'] = true; |
921
|
|
|
$key = 'y'; |
922
|
|
|
} |
923
|
|
|
if (strtolower($key) !== 'y') { |
924
|
|
|
$this->_io->out(sprintf('Skip `%s`', $path), 2); |
925
|
|
|
|
926
|
|
|
return false; |
927
|
|
|
} |
928
|
|
|
} else { |
929
|
|
|
$this->out(sprintf('Creating file %s', $path)); |
930
|
|
|
} |
931
|
|
|
|
932
|
|
|
$File = new File($path, true); |
933
|
|
|
|
934
|
|
|
try { |
935
|
|
|
if ($File->exists() && $File->writable()) { |
936
|
|
|
$File->write($contents); |
937
|
|
|
$this->_io->out(sprintf('<success>Wrote</success> `%s`', $path)); |
938
|
|
|
|
939
|
|
|
return true; |
940
|
|
|
} |
941
|
|
|
|
942
|
|
|
$this->_io->err(sprintf('<error>Could not write to `%s`</error>.', $path), 2); |
943
|
|
|
|
944
|
|
|
return false; |
945
|
|
|
} finally { |
946
|
|
|
$File->close(); |
947
|
|
|
} |
948
|
|
|
} |
949
|
|
|
|
950
|
|
|
/** |
951
|
|
|
* Makes absolute file path easier to read |
952
|
|
|
* |
953
|
|
|
* @param string $file Absolute file path |
954
|
|
|
* @return string short path |
955
|
|
|
* @link https://book.cakephp.org/3/en/console-and-shells.html#Shell::shortPath |
956
|
|
|
*/ |
957
|
|
|
public function shortPath($file) |
958
|
|
|
{ |
959
|
|
|
$shortPath = str_replace(ROOT, null, $file); |
960
|
|
|
$shortPath = str_replace('..' . DIRECTORY_SEPARATOR, '', $shortPath); |
961
|
|
|
$shortPath = str_replace(DIRECTORY_SEPARATOR, '/', $shortPath); |
962
|
|
|
|
963
|
|
|
return str_replace('//', DIRECTORY_SEPARATOR, $shortPath); |
964
|
|
|
} |
965
|
|
|
|
966
|
|
|
/** |
967
|
|
|
* Render a Console Helper |
968
|
|
|
* |
969
|
|
|
* Create and render the output for a helper object. If the helper |
970
|
|
|
* object has not already been loaded, it will be loaded and constructed. |
971
|
|
|
* |
972
|
|
|
* @param string $name The name of the helper to render |
973
|
|
|
* @param array $settings Configuration data for the helper. |
974
|
|
|
* @return \Cake\Console\Helper The created helper instance. |
975
|
|
|
*/ |
976
|
|
|
public function helper($name, array $settings = []) |
977
|
|
|
{ |
978
|
|
|
return $this->_io->helper($name, $settings); |
979
|
|
|
} |
980
|
|
|
|
981
|
|
|
/** |
982
|
|
|
* Stop execution of the current script. |
983
|
|
|
* Raises a StopException to try and halt the execution. |
984
|
|
|
* |
985
|
|
|
* @param int|string $status see https://secure.php.net/exit for values |
986
|
|
|
* @throws \Cake\Console\Exception\StopException |
987
|
|
|
* @return void |
988
|
|
|
*/ |
989
|
|
|
protected function _stop($status = self::CODE_SUCCESS) |
990
|
|
|
{ |
991
|
|
|
throw new StopException('Halting error reached', $status); |
992
|
|
|
} |
993
|
|
|
|
994
|
|
|
/** |
995
|
|
|
* Returns an array that can be used to describe the internal state of this |
996
|
|
|
* object. |
997
|
|
|
* |
998
|
|
|
* @return array |
999
|
|
|
*/ |
1000
|
|
|
public function __debugInfo() |
1001
|
|
|
{ |
1002
|
|
|
return [ |
1003
|
|
|
'name' => $this->name, |
1004
|
|
|
'plugin' => $this->plugin, |
1005
|
|
|
'command' => $this->command, |
1006
|
|
|
'tasks' => $this->tasks, |
1007
|
|
|
'params' => $this->params, |
1008
|
|
|
'args' => $this->args, |
1009
|
|
|
'interactive' => $this->interactive, |
1010
|
|
|
]; |
1011
|
|
|
} |
1012
|
|
|
} |
1013
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.