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 3.6.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\Datasource\ModelAwareTrait; |
20
|
|
|
use Cake\Log\LogTrait; |
21
|
|
|
use Cake\ORM\Locator\LocatorAwareTrait; |
22
|
|
|
use InvalidArgumentException; |
23
|
|
|
use RuntimeException; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Base class for console commands. |
27
|
|
|
*/ |
28
|
|
|
class Command |
29
|
|
|
{ |
30
|
|
|
use LocatorAwareTrait; |
31
|
|
|
use LogTrait; |
32
|
|
|
use ModelAwareTrait; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Default error code |
36
|
|
|
* |
37
|
|
|
* @var int |
38
|
|
|
*/ |
39
|
|
|
const CODE_ERROR = 1; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Default success code |
43
|
|
|
* |
44
|
|
|
* @var int |
45
|
|
|
*/ |
46
|
|
|
const CODE_SUCCESS = 0; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* The name of this command. |
50
|
|
|
* |
51
|
|
|
* @var string |
52
|
|
|
*/ |
53
|
|
|
protected $name = 'cake unknown'; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Constructor |
57
|
|
|
* |
58
|
|
|
* By default CakePHP will construct command objects when |
59
|
|
|
* building the CommandCollection for your application. |
60
|
|
|
*/ |
61
|
|
|
public function __construct() |
62
|
|
|
{ |
63
|
|
|
$this->modelFactory('Table', function ($alias) { |
64
|
|
|
return $this->getTableLocator()->get($alias); |
65
|
|
|
}); |
66
|
|
|
|
67
|
|
|
if (isset($this->modelClass)) { |
68
|
|
|
$this->loadModel(); |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Set the name this command uses in the collection. |
74
|
|
|
* |
75
|
|
|
* Generally invoked by the CommandCollection when the command is added. |
76
|
|
|
* Required to have at least one space in the name so that the root |
77
|
|
|
* command can be calculated. |
78
|
|
|
* |
79
|
|
|
* @param string $name The name the command uses in the collection. |
80
|
|
|
* @return $this |
81
|
|
|
* @throws \InvalidArgumentException |
82
|
|
|
*/ |
83
|
|
|
public function setName($name) |
84
|
|
|
{ |
85
|
|
|
if (strpos($name, ' ') < 1) { |
86
|
|
|
throw new InvalidArgumentException( |
87
|
|
|
"The name '{$name}' is missing a space. Names should look like `cake routes`" |
88
|
|
|
); |
89
|
|
|
} |
90
|
|
|
$this->name = $name; |
91
|
|
|
|
92
|
|
|
return $this; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Get the command name. |
97
|
|
|
* |
98
|
|
|
* @return string |
99
|
|
|
*/ |
100
|
|
|
public function getName() |
101
|
|
|
{ |
102
|
|
|
return $this->name; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Get the option parser. |
107
|
|
|
* |
108
|
|
|
* You can override buildOptionParser() to define your options & arguments. |
109
|
|
|
* |
110
|
|
|
* @return \Cake\Console\ConsoleOptionParser |
111
|
|
|
* @throws \RuntimeException When the parser is invalid |
112
|
|
|
*/ |
113
|
|
|
public function getOptionParser() |
114
|
|
|
{ |
115
|
|
|
list($root, $name) = explode(' ', $this->name, 2); |
116
|
|
|
$parser = new ConsoleOptionParser($name); |
117
|
|
|
$parser->setRootName($root); |
118
|
|
|
|
119
|
|
|
$parser = $this->buildOptionParser($parser); |
120
|
|
|
if (!($parser instanceof ConsoleOptionParser)) { |
121
|
|
|
throw new RuntimeException(sprintf( |
122
|
|
|
"Invalid option parser returned from buildOptionParser(). Expected %s, got %s", |
123
|
|
|
ConsoleOptionParser::class, |
124
|
|
|
getTypeName($parser) |
125
|
|
|
)); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
return $parser; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Hook method for defining this command's option parser. |
133
|
|
|
* |
134
|
|
|
* @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined |
135
|
|
|
* @return \Cake\Console\ConsoleOptionParser The built parser. |
136
|
|
|
*/ |
137
|
|
|
protected function buildOptionParser(ConsoleOptionParser $parser) |
138
|
|
|
{ |
139
|
|
|
return $parser; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Hook method invoked by CakePHP when a command is about to be executed. |
144
|
|
|
* |
145
|
|
|
* Override this method and implement expensive/important setup steps that |
146
|
|
|
* should not run on every command run. This method will be called *before* |
147
|
|
|
* the options and arguments are validated and processed. |
148
|
|
|
* |
149
|
|
|
* @return void |
150
|
|
|
*/ |
151
|
|
|
public function initialize() |
152
|
|
|
{ |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Run the command. |
157
|
|
|
* |
158
|
|
|
* @param array $argv Arguments from the CLI environment. |
159
|
|
|
* @param \Cake\Console\ConsoleIo $io The console io |
160
|
|
|
* @return int|null Exit code or null for success. |
161
|
|
|
*/ |
162
|
|
|
public function run(array $argv, ConsoleIo $io) |
163
|
|
|
{ |
164
|
|
|
$this->initialize(); |
165
|
|
|
|
166
|
|
|
$parser = $this->getOptionParser(); |
167
|
|
|
try { |
168
|
|
|
list($options, $arguments) = $parser->parse($argv); |
169
|
|
|
$args = new Arguments( |
170
|
|
|
$arguments, |
171
|
|
|
$options, |
172
|
|
|
$parser->argumentNames() |
173
|
|
|
); |
174
|
|
|
} catch (ConsoleException $e) { |
175
|
|
|
$io->err('Error: ' . $e->getMessage()); |
176
|
|
|
|
177
|
|
|
return static::CODE_ERROR; |
178
|
|
|
} |
179
|
|
|
$this->setOutputLevel($args, $io); |
180
|
|
|
|
181
|
|
|
if ($args->getOption('help')) { |
182
|
|
|
$this->displayHelp($parser, $args, $io); |
183
|
|
|
|
184
|
|
|
return static::CODE_SUCCESS; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
return $this->execute($args, $io); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Output help content |
192
|
|
|
* |
193
|
|
|
* @param \Cake\Console\ConsoleOptionParser $parser The option parser. |
194
|
|
|
* @param \Cake\Console\Arguments $args The command arguments. |
195
|
|
|
* @param \Cake\Console\ConsoleIo $io The console io |
196
|
|
|
* @return void |
197
|
|
|
*/ |
198
|
|
|
protected function displayHelp(ConsoleOptionParser $parser, Arguments $args, ConsoleIo $io) |
199
|
|
|
{ |
200
|
|
|
$format = 'text'; |
201
|
|
|
if ($args->getArgumentAt(0) === 'xml') { |
202
|
|
|
$format = 'xml'; |
203
|
|
|
$io->setOutputAs(ConsoleOutput::RAW); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
$io->out($parser->help(null, $format)); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* Set the output level based on the Arguments. |
211
|
|
|
* |
212
|
|
|
* @param \Cake\Console\Arguments $args The command arguments. |
213
|
|
|
* @param \Cake\Console\ConsoleIo $io The console io |
214
|
|
|
* @return void |
215
|
|
|
*/ |
216
|
|
|
protected function setOutputLevel(Arguments $args, ConsoleIo $io) |
217
|
|
|
{ |
218
|
|
|
$io->setLoggers(ConsoleIo::NORMAL); |
219
|
|
|
if ($args->getOption('quiet')) { |
220
|
|
|
$io->level(ConsoleIo::QUIET); |
221
|
|
|
$io->setLoggers(ConsoleIo::QUIET); |
222
|
|
|
} |
223
|
|
|
if ($args->getOption('verbose')) { |
224
|
|
|
$io->level(ConsoleIo::VERBOSE); |
225
|
|
|
$io->setLoggers(ConsoleIo::VERBOSE); |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Implement this method with your command's logic. |
231
|
|
|
* |
232
|
|
|
* @param \Cake\Console\Arguments $args The command arguments. |
233
|
|
|
* @param \Cake\Console\ConsoleIo $io The console io |
234
|
|
|
* @return null|int The exit code or null for success |
235
|
|
|
*/ |
236
|
|
|
public function execute(Arguments $args, ConsoleIo $io) |
237
|
|
|
{ |
238
|
|
|
return null; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Halt the the current process with a StopException. |
243
|
|
|
* |
244
|
|
|
* @param int $code The exit code to use. |
245
|
|
|
* @throws \Cake\Console\Exception\StopException |
246
|
|
|
* @return void |
247
|
|
|
*/ |
248
|
|
|
public function abort($code = self::CODE_ERROR) |
249
|
|
|
{ |
250
|
|
|
throw new StopException('Command aborted', $code); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Execute another command with the provided set of arguments. |
255
|
|
|
* |
256
|
|
|
* @param string|\Cake\Console\Command $command The command class name or command instance. |
257
|
|
|
* @param array $args The arguments to invoke the command with. |
258
|
|
|
* @param \Cake\Console\ConsoleIo $io The ConsoleIo instance to use for the executed command. |
259
|
|
|
* @return int|null The exit code or null for success of the command. |
260
|
|
|
*/ |
261
|
|
|
public function executeCommand($command, array $args = [], ConsoleIo $io = null) |
262
|
|
|
{ |
263
|
|
|
if (is_string($command)) { |
264
|
|
|
if (!class_exists($command)) { |
265
|
|
|
throw new InvalidArgumentException("Command class '{$command}' does not exist."); |
266
|
|
|
} |
267
|
|
|
$command = new $command(); |
268
|
|
|
} |
269
|
|
|
if (!$command instanceof Command) { |
270
|
|
|
$commandType = getTypeName($command); |
271
|
|
|
throw new InvalidArgumentException( |
272
|
|
|
"Command '{$commandType}' is not a subclass of Cake\Console\Command." |
273
|
|
|
); |
274
|
|
|
} |
275
|
|
|
$io = $io ?: new ConsoleIo(); |
276
|
|
|
|
277
|
|
|
try { |
278
|
|
|
return $command->run($args, $io); |
279
|
|
|
} catch (StopException $e) { |
280
|
|
|
return $e->getCode(); |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
|