1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Symfony\Component\Console\Command;
13:
14: use Symfony\Component\Console\Input\InputDefinition;
15: use Symfony\Component\Console\Input\InputOption;
16: use Symfony\Component\Console\Input\InputArgument;
17: use Symfony\Component\Console\Input\InputInterface;
18: use Symfony\Component\Console\Output\OutputInterface;
19: use Symfony\Component\Console\Application;
20: use Symfony\Component\Console\Helper\HelperSet;
21:
22: 23: 24: 25: 26: 27: 28:
29: class Command
30: {
31: private $application;
32: private $name;
33: private $aliases;
34: private $definition;
35: private $help;
36: private $description;
37: private $ignoreValidationErrors;
38: private $applicationDefinitionMerged;
39: private $code;
40: private $synopsis;
41: private $helperSet;
42:
43: 44: 45: 46: 47: 48: 49: 50: 51:
52: public function __construct($name = null)
53: {
54: $this->definition = new InputDefinition();
55: $this->ignoreValidationErrors = false;
56: $this->applicationDefinitionMerged = false;
57: $this->aliases = array();
58:
59: if (null !== $name) {
60: $this->setName($name);
61: }
62:
63: $this->configure();
64:
65: if (!$this->name) {
66: throw new \LogicException('The command name cannot be empty.');
67: }
68: }
69:
70: 71: 72: 73: 74:
75: public function ignoreValidationErrors()
76: {
77: $this->ignoreValidationErrors = true;
78: }
79:
80: 81: 82: 83: 84: 85: 86:
87: public function setApplication(Application $application = null)
88: {
89: $this->application = $application;
90: if ($application) {
91: $this->setHelperSet($application->getHelperSet());
92: } else {
93: $this->helperSet = null;
94: }
95: }
96:
97: 98: 99: 100: 101:
102: public function setHelperSet(HelperSet $helperSet)
103: {
104: $this->helperSet = $helperSet;
105: }
106:
107: 108: 109: 110: 111:
112: public function getHelperSet()
113: {
114: return $this->helperSet;
115: }
116:
117: 118: 119: 120: 121: 122: 123:
124: public function getApplication()
125: {
126: return $this->application;
127: }
128:
129: 130: 131: 132: 133: 134: 135: 136:
137: public function isEnabled()
138: {
139: return true;
140: }
141:
142: 143: 144:
145: protected function configure()
146: {
147: }
148:
149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164:
165: protected function execute(InputInterface $input, OutputInterface $output)
166: {
167: throw new \LogicException('You must override the execute() method in the concrete command class.');
168: }
169:
170: 171: 172: 173: 174: 175:
176: protected function interact(InputInterface $input, OutputInterface $output)
177: {
178: }
179:
180: 181: 182: 183: 184: 185: 186: 187: 188:
189: protected function initialize(InputInterface $input, OutputInterface $output)
190: {
191: }
192:
193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207:
208: public function run(InputInterface $input, OutputInterface $output)
209: {
210:
211: $this->getSynopsis();
212:
213:
214: $this->mergeApplicationDefinition();
215:
216:
217: try {
218: $input->bind($this->definition);
219: } catch (\Exception $e) {
220: if (!$this->ignoreValidationErrors) {
221: throw $e;
222: }
223: }
224:
225: $this->initialize($input, $output);
226:
227: if ($input->isInteractive()) {
228: $this->interact($input, $output);
229: }
230:
231: $input->validate();
232:
233: if ($this->code) {
234: return call_user_func($this->code, $input, $output);
235: }
236:
237: return $this->execute($input, $output);
238: }
239:
240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253:
254: public function setCode(\Closure $code)
255: {
256: $this->code = $code;
257:
258: return $this;
259: }
260:
261: 262: 263:
264: private function mergeApplicationDefinition()
265: {
266: if (null === $this->application || true === $this->applicationDefinitionMerged) {
267: return;
268: }
269:
270: $currentArguments = $this->definition->getArguments();
271: $this->definition->setArguments($this->application->getDefinition()->getArguments());
272: $this->definition->addArguments($currentArguments);
273:
274: $this->definition->addOptions($this->application->getDefinition()->getOptions());
275:
276: $this->applicationDefinitionMerged = true;
277: }
278:
279: 280: 281: 282: 283: 284: 285: 286: 287:
288: public function setDefinition($definition)
289: {
290: if ($definition instanceof InputDefinition) {
291: $this->definition = $definition;
292: } else {
293: $this->definition->setDefinition($definition);
294: }
295:
296: $this->applicationDefinitionMerged = false;
297:
298: return $this;
299: }
300:
301: 302: 303: 304: 305: 306: 307:
308: public function getDefinition()
309: {
310: return $this->definition;
311: }
312:
313: 314: 315: 316: 317: 318: 319: 320:
321: protected function getNativeDefinition()
322: {
323: return $this->getDefinition();
324: }
325:
326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337:
338: public function addArgument($name, $mode = null, $description = '', $default = null)
339: {
340: $this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
341:
342: return $this;
343: }
344:
345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357:
358: public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
359: {
360: $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
361:
362: return $this;
363: }
364:
365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380:
381: public function setName($name)
382: {
383: $this->validateName($name);
384:
385: $this->name = $name;
386:
387: return $this;
388: }
389:
390: 391: 392: 393: 394: 395: 396:
397: public function getName()
398: {
399: return $this->name;
400: }
401:
402: 403: 404: 405: 406: 407: 408: 409: 410:
411: public function setDescription($description)
412: {
413: $this->description = $description;
414:
415: return $this;
416: }
417:
418: 419: 420: 421: 422: 423: 424:
425: public function getDescription()
426: {
427: return $this->description;
428: }
429:
430: 431: 432: 433: 434: 435: 436: 437: 438:
439: public function setHelp($help)
440: {
441: $this->help = $help;
442:
443: return $this;
444: }
445:
446: 447: 448: 449: 450: 451: 452:
453: public function getHelp()
454: {
455: return $this->help;
456: }
457:
458: 459: 460: 461: 462: 463:
464: public function getProcessedHelp()
465: {
466: $name = $this->name;
467:
468: $placeholders = array(
469: '%command.name%',
470: '%command.full_name%'
471: );
472: $replacements = array(
473: $name,
474: $_SERVER['PHP_SELF'].' '.$name
475: );
476:
477: return str_replace($placeholders, $replacements, $this->getHelp());
478: }
479:
480: 481: 482: 483: 484: 485: 486: 487: 488:
489: public function setAliases($aliases)
490: {
491: foreach ($aliases as $alias) {
492: $this->validateName($alias);
493: }
494:
495: $this->aliases = $aliases;
496:
497: return $this;
498: }
499:
500: 501: 502: 503: 504: 505: 506:
507: public function getAliases()
508: {
509: return $this->aliases;
510: }
511:
512: 513: 514: 515: 516:
517: public function getSynopsis()
518: {
519: if (null === $this->synopsis) {
520: $this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis()));
521: }
522:
523: return $this->synopsis;
524: }
525:
526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536:
537: public function getHelper($name)
538: {
539: return $this->helperSet->get($name);
540: }
541:
542: 543: 544: 545: 546:
547: public function asText()
548: {
549: $messages = array(
550: '<comment>Usage:</comment>',
551: ' '.$this->getSynopsis(),
552: '',
553: );
554:
555: if ($this->getAliases()) {
556: $messages[] = '<comment>Aliases:</comment> <info>'.implode(', ', $this->getAliases()).'</info>';
557: }
558:
559: $messages[] = $this->getNativeDefinition()->asText();
560:
561: if ($help = $this->getProcessedHelp()) {
562: $messages[] = '<comment>Help:</comment>';
563: $messages[] = ' '.str_replace("\n", "\n ", $help)."\n";
564: }
565:
566: return implode("\n", $messages);
567: }
568:
569: 570: 571: 572: 573: 574: 575:
576: public function asXml($asDom = false)
577: {
578: $dom = new \DOMDocument('1.0', 'UTF-8');
579: $dom->formatOutput = true;
580: $dom->appendChild($commandXML = $dom->createElement('command'));
581: $commandXML->setAttribute('id', $this->name);
582: $commandXML->setAttribute('name', $this->name);
583:
584: $commandXML->appendChild($usageXML = $dom->createElement('usage'));
585: $usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), '')));
586:
587: $commandXML->appendChild($descriptionXML = $dom->createElement('description'));
588: $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $this->getDescription())));
589:
590: $commandXML->appendChild($helpXML = $dom->createElement('help'));
591: $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $this->getProcessedHelp())));
592:
593: $commandXML->appendChild($aliasesXML = $dom->createElement('aliases'));
594: foreach ($this->getAliases() as $alias) {
595: $aliasesXML->appendChild($aliasXML = $dom->createElement('alias'));
596: $aliasXML->appendChild($dom->createTextNode($alias));
597: }
598:
599: $definition = $this->getNativeDefinition()->asXml(true);
600: $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true));
601: $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true));
602:
603: return $asDom ? $dom : $dom->saveXml();
604: }
605:
606: private function validateName($name)
607: {
608: if (!preg_match('/^[^\:]+(\:[^\:]+)*$/', $name)) {
609: throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
610: }
611: }
612: }
613: