Completed
Push — master ( 14b1fe...69643f )
by Mark
04:01
created

Command::executeCommand()   B

Complexity

Conditions 6
Paths 11

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 11
nop 3
dl 0
loc 22
rs 8.9457
c 0
b 0
f 0
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