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:
68: /**
69: * Set the name this command uses in the collection.
70: *
71: * Generally invoked by the CommandCollection when the command is added.
72: * Required to have at least one space in the name so that the root
73: * command can be calculated.
74: *
75: * @param string $name The name the command uses in the collection.
76: * @return $this
77: * @throws \InvalidArgumentException
78: */
79: public function setName($name)
80: {
81: if (strpos($name, ' ') < 1) {
82: throw new InvalidArgumentException(
83: "The name '{$name}' is missing a space. Names should look like `cake routes`"
84: );
85: }
86: $this->name = $name;
87:
88: return $this;
89: }
90:
91: /**
92: * Get the command name.
93: *
94: * @return string
95: */
96: public function getName()
97: {
98: return $this->name;
99: }
100:
101: /**
102: * Get the option parser.
103: *
104: * You can override buildOptionParser() to define your options & arguments.
105: *
106: * @return \Cake\Console\ConsoleOptionParser
107: * @throws \RuntimeException When the parser is invalid
108: */
109: public function getOptionParser()
110: {
111: list($root, $name) = explode(' ', $this->name, 2);
112: $parser = new ConsoleOptionParser($name);
113: $parser->setRootName($root);
114:
115: $parser = $this->buildOptionParser($parser);
116: if (!($parser instanceof ConsoleOptionParser)) {
117: throw new RuntimeException(sprintf(
118: "Invalid option parser returned from buildOptionParser(). Expected %s, got %s",
119: ConsoleOptionParser::class,
120: getTypeName($parser)
121: ));
122: }
123:
124: return $parser;
125: }
126:
127: /**
128: * Hook method for defining this command's option parser.
129: *
130: * @param \Cake\Console\ConsoleOptionParser $parser The parser to be defined
131: * @return \Cake\Console\ConsoleOptionParser The built parser.
132: */
133: protected function buildOptionParser(ConsoleOptionParser $parser)
134: {
135: return $parser;
136: }
137:
138: /**
139: * Hook method invoked by CakePHP when a command is about to be executed.
140: *
141: * Override this method and implement expensive/important setup steps that
142: * should not run on every command run. This method will be called *before*
143: * the options and arguments are validated and processed.
144: *
145: * @return void
146: */
147: public function initialize()
148: {
149: }
150:
151: /**
152: * Run the command.
153: *
154: * @param array $argv Arguments from the CLI environment.
155: * @param \Cake\Console\ConsoleIo $io The console io
156: * @return int|null Exit code or null for success.
157: */
158: public function run(array $argv, ConsoleIo $io)
159: {
160: $this->initialize();
161:
162: $parser = $this->getOptionParser();
163: try {
164: list($options, $arguments) = $parser->parse($argv);
165: $args = new Arguments(
166: $arguments,
167: $options,
168: $parser->argumentNames()
169: );
170: } catch (ConsoleException $e) {
171: $io->err('Error: ' . $e->getMessage());
172:
173: return static::CODE_ERROR;
174: }
175: $this->setOutputLevel($args, $io);
176:
177: if ($args->getOption('help')) {
178: $this->displayHelp($parser, $args, $io);
179:
180: return static::CODE_SUCCESS;
181: }
182:
183: return $this->execute($args, $io);
184: }
185:
186: /**
187: * Output help content
188: *
189: * @param \Cake\Console\ConsoleOptionParser $parser The option parser.
190: * @param \Cake\Console\Arguments $args The command arguments.
191: * @param \Cake\Console\ConsoleIo $io The console io
192: * @return void
193: */
194: protected function displayHelp(ConsoleOptionParser $parser, Arguments $args, ConsoleIo $io)
195: {
196: $format = 'text';
197: if ($args->getArgumentAt(0) === 'xml') {
198: $format = 'xml';
199: $io->setOutputAs(ConsoleOutput::RAW);
200: }
201:
202: $io->out($parser->help(null, $format));
203: }
204:
205: /**
206: * Set the output level based on the Arguments.
207: *
208: * @param \Cake\Console\Arguments $args The command arguments.
209: * @param \Cake\Console\ConsoleIo $io The console io
210: * @return void
211: */
212: protected function setOutputLevel(Arguments $args, ConsoleIo $io)
213: {
214: $io->setLoggers(ConsoleIo::NORMAL);
215: if ($args->getOption('quiet')) {
216: $io->level(ConsoleIo::QUIET);
217: $io->setLoggers(ConsoleIo::QUIET);
218: }
219: if ($args->getOption('verbose')) {
220: $io->level(ConsoleIo::VERBOSE);
221: $io->setLoggers(ConsoleIo::VERBOSE);
222: }
223: }
224:
225: /**
226: * Implement this method with your command's logic.
227: *
228: * @param \Cake\Console\Arguments $args The command arguments.
229: * @param \Cake\Console\ConsoleIo $io The console io
230: * @return null|int The exit code or null for success
231: */
232: public function execute(Arguments $args, ConsoleIo $io)
233: {
234: return null;
235: }
236:
237: /**
238: * Halt the the current process with a StopException.
239: *
240: * @param int $code The exit code to use.
241: * @throws \Cake\Console\Exception\StopException
242: * @return void
243: */
244: public function abort($code = self::CODE_ERROR)
245: {
246: throw new StopException('Command aborted', $code);
247: }
248: }
249: