Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Command.php
001 <?php
002
003 /*
004 * This file is part of the Symfony package.
005 *
006 * (c) Fabien Potencier <fabien@symfony.com>
007 *
008 * For the full copyright and license information, please view the LICENSE
009 * file that was distributed with this source code.
010 */
011
012 namespace Symfony\Component\Console\Command;
013
014 use Symfony\Component\Console\Application;
015 use Symfony\Component\Console\Exception\ExceptionInterface;
016 use Symfony\Component\Console\Exception\InvalidArgumentException;
017 use Symfony\Component\Console\Exception\LogicException;
018 use Symfony\Component\Console\Helper\HelperSet;
019 use Symfony\Component\Console\Input\InputArgument;
020 use Symfony\Component\Console\Input\InputDefinition;
021 use Symfony\Component\Console\Input\InputInterface;
022 use Symfony\Component\Console\Input\InputOption;
023 use Symfony\Component\Console\Output\OutputInterface;
024
025 /**
026 * Base class for all commands.
027 *
028 * @author Fabien Potencier <fabien@symfony.com>
029 */
030 class Command
031 {
032 /**
033 * @var string|null The default command name
034 */
035 protected static $defaultName;
036
037 private $application;
038 private $name;
039 private $processTitle;
040 private $aliases = [];
041 private $definition;
042 private $hidden = false;
043 private $help = '';
044 private $description = '';
045 private $ignoreValidationErrors = false;
046 private $applicationDefinitionMerged = false;
047 private $applicationDefinitionMergedWithArgs = false;
048 private $code;
049 private $synopsis = [];
050 private $usages = [];
051 private $helperSet;
052
053 /**
054 * @return string|null The default command name or null when no default name is set
055 */
056 public static function getDefaultName()
057 {
058 $class = static::class;
059 $r = new \ReflectionProperty($class, 'defaultName');
060
061 return $class === $r->class ? static::$defaultName : null;
062 }
063
064 /**
065 * @param string|null $name The name of the command; passing null means it must be set in configure()
066 *
067 * @throws LogicException When the command name is empty
068 */
069 public function __construct($name = null)
070 {
071 $this->definition = new InputDefinition();
072
073 if (null !== $name || null !== $name = static::getDefaultName()) {
074 $this->setName($name);
075 }
076
077 $this->configure();
078 }
079
080 /**
081 * Ignores validation errors.
082 *
083 * This is mainly useful for the help command.
084 */
085 public function ignoreValidationErrors()
086 {
087 $this->ignoreValidationErrors = true;
088 }
089
090 public function setApplication(Application $application = null)
091 {
092 $this->application = $application;
093 if ($application) {
094 $this->setHelperSet($application->getHelperSet());
095 } else {
096 $this->helperSet = null;
097 }
098 }
099
100 public function setHelperSet(HelperSet $helperSet)
101 {
102 $this->helperSet = $helperSet;
103 }
104
105 /**
106 * Gets the helper set.
107 *
108 * @return HelperSet|null A HelperSet instance
109 */
110 public function getHelperSet()
111 {
112 return $this->helperSet;
113 }
114
115 /**
116 * Gets the application instance for this command.
117 *
118 * @return Application|null An Application instance
119 */
120 public function getApplication()
121 {
122 return $this->application;
123 }
124
125 /**
126 * Checks whether the command is enabled or not in the current environment.
127 *
128 * Override this to check for x or y and return false if the command can not
129 * run properly under the current conditions.
130 *
131 * @return bool
132 */
133 public function isEnabled()
134 {
135 return true;
136 }
137
138 /**
139 * Configures the current command.
140 */
141 protected function configure()
142 {
143 }
144
145 /**
146 * Executes the current command.
147 *
148 * This method is not abstract because you can use this class
149 * as a concrete class. In this case, instead of defining the
150 * execute() method, you set the code to execute by passing
151 * a Closure to the setCode() method.
152 *
153 * @return int|null null or 0 if everything went fine, or an error code
154 *
155 * @throws LogicException When this abstract method is not implemented
156 *
157 * @see setCode()
158 */
159 protected function execute(InputInterface $input, OutputInterface $output)
160 {
161 throw new LogicException('You must override the execute() method in the concrete command class.');
162 }
163
164 /**
165 * Interacts with the user.
166 *
167 * This method is executed before the InputDefinition is validated.
168 * This means that this is the only place where the command can
169 * interactively ask for values of missing required arguments.
170 */
171 protected function interact(InputInterface $input, OutputInterface $output)
172 {
173 }
174
175 /**
176 * Initializes the command after the input has been bound and before the input
177 * is validated.
178 *
179 * This is mainly useful when a lot of commands extends one main command
180 * where some things need to be initialized based on the input arguments and options.
181 *
182 * @see InputInterface::bind()
183 * @see InputInterface::validate()
184 */
185 protected function initialize(InputInterface $input, OutputInterface $output)
186 {
187 }
188
189 /**
190 * Runs the command.
191 *
192 * The code to execute is either defined directly with the
193 * setCode() method or by overriding the execute() method
194 * in a sub-class.
195 *
196 * @return int The command exit code
197 *
198 * @throws \Exception When binding input fails. Bypass this by calling {@link ignoreValidationErrors()}.
199 *
200 * @see setCode()
201 * @see execute()
202 */
203 public function run(InputInterface $input, OutputInterface $output)
204 {
205 // force the creation of the synopsis before the merge with the app definition
206 $this->getSynopsis(true);
207 $this->getSynopsis(false);
208
209 // add the application arguments and options
210 $this->mergeApplicationDefinition();
211
212 // bind the input against the command specific arguments/options
213 try {
214 $input->bind($this->definition);
215 } catch (ExceptionInterface $e) {
216 if (!$this->ignoreValidationErrors) {
217 throw $e;
218 }
219 }
220
221 $this->initialize($input, $output);
222
223 if (null !== $this->processTitle) {
224 if (\function_exists('cli_set_process_title')) {
225 if (!@cli_set_process_title($this->processTitle)) {
226 if ('Darwin' === \PHP_OS) {
227 $output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
228 } else {
229 cli_set_process_title($this->processTitle);
230 }
231 }
232 } elseif (\function_exists('setproctitle')) {
233 setproctitle($this->processTitle);
234 } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
235 $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
236 }
237 }
238
239 if ($input->isInteractive()) {
240 $this->interact($input, $output);
241 }
242
243 // The command name argument is often omitted when a command is executed directly with its run() method.
244 // It would fail the validation if we didn't make sure the command argument is present,
245 // since it's required by the application.
246 if ($input->hasArgument('command') && null === $input->getArgument('command')) {
247 $input->setArgument('command', $this->getName());
248 }
249
250 $input->validate();
251
252 if ($this->code) {
253 $statusCode = \call_user_func($this->code, $input, $output);
254 } else {
255 $statusCode = $this->execute($input, $output);
256 }
257
258 return is_numeric($statusCode) ? (int) $statusCode : 0;
259 }
260
261 /**
262 * Sets the code to execute when running this command.
263 *
264 * If this method is used, it overrides the code defined
265 * in the execute() method.
266 *
267 * @param callable $code A callable(InputInterface $input, OutputInterface $output)
268 *
269 * @return $this
270 *
271 * @throws InvalidArgumentException
272 *
273 * @see execute()
274 */
275 public function setCode(callable $code)
276 {
277 if ($code instanceof \Closure) {
278 $r = new \ReflectionFunction($code);
279 if (null === $r->getClosureThis()) {
280 if (\PHP_VERSION_ID < 70000) {
281 // Bug in PHP5: https://bugs.php.net/64761
282 // This means that we cannot bind static closures and therefore we must
283 // ignore any errors here. There is no way to test if the closure is
284 // bindable.
285 $code = @\Closure::bind($code, $this);
286 } else {
287 $code = \Closure::bind($code, $this);
288 }
289 }
290 }
291
292 $this->code = $code;
293
294 return $this;
295 }
296
297 /**
298 * Merges the application definition with the command definition.
299 *
300 * This method is not part of public API and should not be used directly.
301 *
302 * @param bool $mergeArgs Whether to merge or not the Application definition arguments to Command definition arguments
303 */
304 public function mergeApplicationDefinition($mergeArgs = true)
305 {
306 if (null === $this->application || (true === $this->applicationDefinitionMerged && ($this->applicationDefinitionMergedWithArgs || !$mergeArgs))) {
307 return;
308 }
309
310 $this->definition->addOptions($this->application->getDefinition()->getOptions());
311
312 $this->applicationDefinitionMerged = true;
313
314 if ($mergeArgs) {
315 $currentArguments = $this->definition->getArguments();
316 $this->definition->setArguments($this->application->getDefinition()->getArguments());
317 $this->definition->addArguments($currentArguments);
318
319 $this->applicationDefinitionMergedWithArgs = true;
320 }
321 }
322
323 /**
324 * Sets an array of argument and option instances.
325 *
326 * @param array|InputDefinition $definition An array of argument and option instances or a definition instance
327 *
328 * @return $this
329 */
330 public function setDefinition($definition)
331 {
332 if ($definition instanceof InputDefinition) {
333 $this->definition = $definition;
334 } else {
335 $this->definition->setDefinition($definition);
336 }
337
338 $this->applicationDefinitionMerged = false;
339
340 return $this;
341 }
342
343 /**
344 * Gets the InputDefinition attached to this Command.
345 *
346 * @return InputDefinition An InputDefinition instance
347 */
348 public function getDefinition()
349 {
350 if (null === $this->definition) {
351 throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class));
352 }
353
354 return $this->definition;
355 }
356
357 /**
358 * Gets the InputDefinition to be used to create representations of this Command.
359 *
360 * Can be overridden to provide the original command representation when it would otherwise
361 * be changed by merging with the application InputDefinition.
362 *
363 * This method is not part of public API and should not be used directly.
364 *
365 * @return InputDefinition An InputDefinition instance
366 */
367 public function getNativeDefinition()
368 {
369 return $this->getDefinition();
370 }
371
372 /**
373 * Adds an argument.
374 *
375 * @param string $name The argument name
376 * @param int|null $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
377 * @param string $description A description text
378 * @param string|string[]|null $default The default value (for InputArgument::OPTIONAL mode only)
379 *
380 * @throws InvalidArgumentException When argument mode is not valid
381 *
382 * @return $this
383 */
384 public function addArgument($name, $mode = null, $description = '', $default = null)
385 {
386 $this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
387
388 return $this;
389 }
390
391 /**
392 * Adds an option.
393 *
394 * @param string $name The option name
395 * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
396 * @param int|null $mode The option mode: One of the InputOption::VALUE_* constants
397 * @param string $description A description text
398 * @param string|string[]|int|bool|null $default The default value (must be null for InputOption::VALUE_NONE)
399 *
400 * @throws InvalidArgumentException If option mode is invalid or incompatible
401 *
402 * @return $this
403 */
404 public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
405 {
406 $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
407
408 return $this;
409 }
410
411 /**
412 * Sets the name of the command.
413 *
414 * This method can set both the namespace and the name if
415 * you separate them by a colon (:)
416 *
417 * $command->setName('foo:bar');
418 *
419 * @param string $name The command name
420 *
421 * @return $this
422 *
423 * @throws InvalidArgumentException When the name is invalid
424 */
425 public function setName($name)
426 {
427 $this->validateName($name);
428
429 $this->name = $name;
430
431 return $this;
432 }
433
434 /**
435 * Sets the process title of the command.
436 *
437 * This feature should be used only when creating a long process command,
438 * like a daemon.
439 *
440 * @param string $title The process title
441 *
442 * @return $this
443 */
444 public function setProcessTitle($title)
445 {
446 $this->processTitle = $title;
447
448 return $this;
449 }
450
451 /**
452 * Returns the command name.
453 *
454 * @return string|null
455 */
456 public function getName()
457 {
458 return $this->name;
459 }
460
461 /**
462 * @param bool $hidden Whether or not the command should be hidden from the list of commands
463 *
464 * @return Command The current instance
465 */
466 public function setHidden($hidden)
467 {
468 $this->hidden = (bool) $hidden;
469
470 return $this;
471 }
472
473 /**
474 * @return bool whether the command should be publicly shown or not
475 */
476 public function isHidden()
477 {
478 return $this->hidden;
479 }
480
481 /**
482 * Sets the description for the command.
483 *
484 * @param string $description The description for the command
485 *
486 * @return $this
487 */
488 public function setDescription($description)
489 {
490 $this->description = $description;
491
492 return $this;
493 }
494
495 /**
496 * Returns the description for the command.
497 *
498 * @return string The description for the command
499 */
500 public function getDescription()
501 {
502 return $this->description;
503 }
504
505 /**
506 * Sets the help for the command.
507 *
508 * @param string $help The help for the command
509 *
510 * @return $this
511 */
512 public function setHelp($help)
513 {
514 $this->help = $help;
515
516 return $this;
517 }
518
519 /**
520 * Returns the help for the command.
521 *
522 * @return string The help for the command
523 */
524 public function getHelp()
525 {
526 return $this->help;
527 }
528
529 /**
530 * Returns the processed help for the command replacing the %command.name% and
531 * %command.full_name% patterns with the real values dynamically.
532 *
533 * @return string The processed help for the command
534 */
535 public function getProcessedHelp()
536 {
537 $name = $this->name;
538 $isSingleCommand = $this->application && $this->application->isSingleCommand();
539
540 $placeholders = [
541 '%command.name%',
542 '%command.full_name%',
543 ];
544 $replacements = [
545 $name,
546 $isSingleCommand ? $_SERVER['PHP_SELF'] : $_SERVER['PHP_SELF'].' '.$name,
547 ];
548
549 return str_replace($placeholders, $replacements, $this->getHelp() ?: $this->getDescription());
550 }
551
552 /**
553 * Sets the aliases for the command.
554 *
555 * @param string[] $aliases An array of aliases for the command
556 *
557 * @return $this
558 *
559 * @throws InvalidArgumentException When an alias is invalid
560 */
561 public function setAliases($aliases)
562 {
563 if (!\is_array($aliases) && !$aliases instanceof \Traversable) {
564 throw new InvalidArgumentException('$aliases must be an array or an instance of \Traversable.');
565 }
566
567 foreach ($aliases as $alias) {
568 $this->validateName($alias);
569 }
570
571 $this->aliases = $aliases;
572
573 return $this;
574 }
575
576 /**
577 * Returns the aliases for the command.
578 *
579 * @return array An array of aliases for the command
580 */
581 public function getAliases()
582 {
583 return $this->aliases;
584 }
585
586 /**
587 * Returns the synopsis for the command.
588 *
589 * @param bool $short Whether to show the short version of the synopsis (with options folded) or not
590 *
591 * @return string The synopsis
592 */
593 public function getSynopsis($short = false)
594 {
595 $key = $short ? 'short' : 'long';
596
597 if (!isset($this->synopsis[$key])) {
598 $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
599 }
600
601 return $this->synopsis[$key];
602 }
603
604 /**
605 * Add a command usage example.
606 *
607 * @param string $usage The usage, it'll be prefixed with the command name
608 *
609 * @return $this
610 */
611 public function addUsage($usage)
612 {
613 if (0 !== strpos($usage, $this->name)) {
614 $usage = sprintf('%s %s', $this->name, $usage);
615 }
616
617 $this->usages[] = $usage;
618
619 return $this;
620 }
621
622 /**
623 * Returns alternative usages of the command.
624 *
625 * @return array
626 */
627 public function getUsages()
628 {
629 return $this->usages;
630 }
631
632 /**
633 * Gets a helper instance by name.
634 *
635 * @param string $name The helper name
636 *
637 * @return mixed The helper value
638 *
639 * @throws LogicException if no HelperSet is defined
640 * @throws InvalidArgumentException if the helper is not defined
641 */
642 public function getHelper($name)
643 {
644 if (null === $this->helperSet) {
645 throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name));
646 }
647
648 return $this->helperSet->get($name);
649 }
650
651 /**
652 * Validates a command name.
653 *
654 * It must be non-empty and parts can optionally be separated by ":".
655 *
656 * @param string $name
657 *
658 * @throws InvalidArgumentException When the name is invalid
659 */
660 private function validateName($name)
661 {
662 if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
663 throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
664 }
665 }
666 }
667