| 1: | <?php
|
| 2: | |
| 3: | |
| 4: | |
| 5: | |
| 6: | |
| 7: | |
| 8: | |
| 9: | |
| 10: | |
| 11: | |
| 12: | |
| 13: | |
| 14: |
|
| 15: |
|
| 16: | namespace LucidFrame\Console;
|
| 17: |
|
| 18: | use Closure;
|
| 19: |
|
| 20: | |
| 21: | |
| 22: |
|
| 23: | class Command
|
| 24: | {
|
| 25: |
|
| 26: | protected $name;
|
| 27: |
|
| 28: | protected $description;
|
| 29: |
|
| 30: | protected $help;
|
| 31: |
|
| 32: | protected $options = array();
|
| 33: |
|
| 34: | protected $shortcuts = array();
|
| 35: |
|
| 36: | protected $arguments = array();
|
| 37: |
|
| 38: | protected $argumentNames = array();
|
| 39: |
|
| 40: | protected $definition;
|
| 41: |
|
| 42: | private $argv;
|
| 43: |
|
| 44: | private $parsedOptions = array();
|
| 45: |
|
| 46: | private $parsedArguments = array();
|
| 47: |
|
| 48: | private $longestArgument = '';
|
| 49: |
|
| 50: | private $longestOption = '';
|
| 51: |
|
| 52: | |
| 53: | |
| 54: | |
| 55: |
|
| 56: | public function __construct($name)
|
| 57: | {
|
| 58: | $this->setName($name);
|
| 59: | $this->addOption('help', 'h', 'Display the help message', null, LC_CONSOLE_OPTION_NOVALUE);
|
| 60: | }
|
| 61: |
|
| 62: | |
| 63: | |
| 64: | |
| 65: | |
| 66: |
|
| 67: | public function setName($name)
|
| 68: | {
|
| 69: | $this->name = $name;
|
| 70: |
|
| 71: | return $this;
|
| 72: | }
|
| 73: |
|
| 74: | |
| 75: | |
| 76: | |
| 77: |
|
| 78: | public function getName()
|
| 79: | {
|
| 80: | return $this->name;
|
| 81: | }
|
| 82: |
|
| 83: | |
| 84: | |
| 85: | |
| 86: | |
| 87: |
|
| 88: | public function setDescription($description = null)
|
| 89: | {
|
| 90: | $this->description = $description;
|
| 91: |
|
| 92: | return $this;
|
| 93: | }
|
| 94: |
|
| 95: | |
| 96: | |
| 97: | |
| 98: |
|
| 99: | public function getDescription()
|
| 100: | {
|
| 101: | return $this->description;
|
| 102: | }
|
| 103: |
|
| 104: | |
| 105: | |
| 106: | |
| 107: | |
| 108: |
|
| 109: | public function setHelp($help = null)
|
| 110: | {
|
| 111: | $this->help = $help;
|
| 112: |
|
| 113: | return $this;
|
| 114: | }
|
| 115: |
|
| 116: | |
| 117: | |
| 118: | |
| 119: |
|
| 120: | public function getHelp()
|
| 121: | {
|
| 122: | return $this->help;
|
| 123: | }
|
| 124: |
|
| 125: | |
| 126: | |
| 127: | |
| 128: | |
| 129: | |
| 130: | |
| 131: | |
| 132: | |
| 133: | |
| 134: | |
| 135: |
|
| 136: | public function addOption($name, $shortcut = null, $description = '', $default = null, $type = LC_CONSOLE_OPTION_OPTIONAL)
|
| 137: | {
|
| 138: | $name = ltrim($name, '--');
|
| 139: | if ($shortcut) {
|
| 140: | $shortcut = ltrim($shortcut, '-');
|
| 141: | }
|
| 142: |
|
| 143: | $this->options[$name] = array(
|
| 144: | 'name' => $name,
|
| 145: | 'shortcut' => $shortcut,
|
| 146: | 'description' => $description,
|
| 147: | 'default' => $default,
|
| 148: | 'type' => $type
|
| 149: | );
|
| 150: |
|
| 151: | $this->shortcuts[$shortcut] = $name;
|
| 152: | $this->parsedOptions[$name] = $default;
|
| 153: |
|
| 154: | $key = ($shortcut ? "-{$shortcut}, " : _indent(4)) . "--{$name}";
|
| 155: | $this->options[$name]['key'] = $key;
|
| 156: | if (strlen($key) > strlen($this->longestOption)) {
|
| 157: | $this->longestOption = $key;
|
| 158: | }
|
| 159: |
|
| 160: | return $this;
|
| 161: | }
|
| 162: |
|
| 163: | |
| 164: | |
| 165: | |
| 166: | |
| 167: | |
| 168: | |
| 169: | |
| 170: | |
| 171: |
|
| 172: | public function addArgument($name, $description = '', $default = null)
|
| 173: | {
|
| 174: | $this->arguments[] = array(
|
| 175: | 'name' => $name,
|
| 176: | 'description' => $description,
|
| 177: | 'default' => $default,
|
| 178: | );
|
| 179: | $this->argumentNames[] = $name;
|
| 180: |
|
| 181: | if (strlen($name) > strlen($this->longestArgument)) {
|
| 182: | $this->longestArgument = $name;
|
| 183: | }
|
| 184: |
|
| 185: | return $this;
|
| 186: | }
|
| 187: |
|
| 188: | |
| 189: | |
| 190: |
|
| 191: | public function getArguments()
|
| 192: | {
|
| 193: | return $this->parsedArguments;
|
| 194: | }
|
| 195: |
|
| 196: | |
| 197: | |
| 198: |
|
| 199: | public function getOptions()
|
| 200: | {
|
| 201: | return $this->parsedOptions;
|
| 202: | }
|
| 203: |
|
| 204: | |
| 205: | |
| 206: | |
| 207: | |
| 208: |
|
| 209: | public function setDefinition($function)
|
| 210: | {
|
| 211: | $this->definition = $function;
|
| 212: |
|
| 213: | return $this;
|
| 214: | }
|
| 215: |
|
| 216: | |
| 217: | |
| 218: | |
| 219: |
|
| 220: | public function register()
|
| 221: | {
|
| 222: | Console::registerCommand($this);
|
| 223: |
|
| 224: | return $this;
|
| 225: | }
|
| 226: |
|
| 227: | |
| 228: | |
| 229: | |
| 230: | |
| 231: | |
| 232: |
|
| 233: | public function getOption($name)
|
| 234: | {
|
| 235: | if (!empty($this->parsedOptions[$name])) {
|
| 236: | return $this->parsedOptions[$name];
|
| 237: | } else {
|
| 238: | if ($this->options[$name]['type'] == LC_CONSOLE_OPTION_REQUIRED) {
|
| 239: | _writeln('The option "' . $name . '" is required.');
|
| 240: | }
|
| 241: | }
|
| 242: |
|
| 243: | return null;
|
| 244: | }
|
| 245: |
|
| 246: | |
| 247: | |
| 248: | |
| 249: | |
| 250: | |
| 251: |
|
| 252: | public function getArgument($name)
|
| 253: | {
|
| 254: | return isset($this->parsedArguments[$name]) ? $this->parsedArguments[$name] : null;
|
| 255: | }
|
| 256: |
|
| 257: | |
| 258: | |
| 259: |
|
| 260: | public function getParsedOptions()
|
| 261: | {
|
| 262: | return $this->parsedOptions;
|
| 263: | }
|
| 264: |
|
| 265: | |
| 266: | |
| 267: |
|
| 268: | public function getParsedArguments()
|
| 269: | {
|
| 270: | return $this->parsedArguments;
|
| 271: | }
|
| 272: |
|
| 273: | |
| 274: | |
| 275: |
|
| 276: | public function resetToDefaults()
|
| 277: | {
|
| 278: | foreach ($this->options as $name => $opt) {
|
| 279: | $this->parsedOptions[$name] = $opt['default'];
|
| 280: | }
|
| 281: |
|
| 282: | foreach ($this->arguments as $arg) {
|
| 283: | $this->parsedArguments[$arg['name']] = $arg['default'];
|
| 284: | }
|
| 285: | }
|
| 286: |
|
| 287: | |
| 288: | |
| 289: | |
| 290: | |
| 291: |
|
| 292: | public function run($argv = array())
|
| 293: | {
|
| 294: | $this->parseArguments($argv);
|
| 295: |
|
| 296: | if ($this->getOption('help')) {
|
| 297: | $this->showHelp();
|
| 298: | return true;
|
| 299: | }
|
| 300: |
|
| 301: | if (is_string($this->definition)) {
|
| 302: | $cmd = new $this->definition;
|
| 303: | $cmd->execute($this);
|
| 304: | return true;
|
| 305: | } else {
|
| 306: | return call_user_func_array($this->definition, array($this));
|
| 307: | }
|
| 308: | }
|
| 309: |
|
| 310: | |
| 311: | |
| 312: | |
| 313: |
|
| 314: | public function showHelp()
|
| 315: | {
|
| 316: | $options = $this->getOptions();
|
| 317: |
|
| 318: | if (count($options)) {
|
| 319: | _writeln('Usage:');
|
| 320: | $usage = _indent() . $this->name . ' [options]';
|
| 321: |
|
| 322: | if (count($this->arguments)) {
|
| 323: | $usage .= ' [<' . implode('>] [<', $this->argumentNames) . '>]';
|
| 324: | }
|
| 325: |
|
| 326: | _writeln($usage);
|
| 327: |
|
| 328: |
|
| 329: | if (count($this->arguments)) {
|
| 330: | _writeln();
|
| 331: | _writeln('Arguments:');
|
| 332: |
|
| 333: | $table = new ConsoleTable();
|
| 334: | $table->hideBorder()->setPadding(2);
|
| 335: | foreach ($this->arguments as $arg) {
|
| 336: | $table->addRow();
|
| 337: | $table->addColumn($arg['name']);
|
| 338: | $desc = $arg['description'];
|
| 339: | if ($arg['default']) {
|
| 340: | $desc .= ' [default: "' . $arg['default'] . '"]';
|
| 341: | }
|
| 342: | $table->addColumn($desc);
|
| 343: | }
|
| 344: | $table->display();
|
| 345: | }
|
| 346: |
|
| 347: |
|
| 348: | if (count($options)) {
|
| 349: | _writeln();
|
| 350: | _writeln('Options:');
|
| 351: |
|
| 352: | $table = new ConsoleTable();
|
| 353: | $table->hideBorder()->setPadding(2);
|
| 354: | foreach ($this->options as $name => $opt) {
|
| 355: | $table->addRow();
|
| 356: | $table->addColumn($opt['key']);
|
| 357: | $desc = $opt['description'];
|
| 358: | if ($opt['default']) {
|
| 359: | $desc .= ' [default: "' . $opt['default'] . '"]';
|
| 360: | }
|
| 361: | $table->addColumn($desc);
|
| 362: | }
|
| 363: | $table->display();
|
| 364: | }
|
| 365: |
|
| 366: | if ($this->description) {
|
| 367: | _writeln();
|
| 368: | _writeln('Help:');
|
| 369: | _writeln(_indent() . $this->description);
|
| 370: | }
|
| 371: | }
|
| 372: | }
|
| 373: |
|
| 374: | |
| 375: | |
| 376: | |
| 377: | |
| 378: | |
| 379: |
|
| 380: | private function validateOption($name, $type)
|
| 381: | {
|
| 382: | if (!in_array($type, array('shortopt', 'longopt'))) {
|
| 383: | return $name;
|
| 384: | }
|
| 385: |
|
| 386: | if ($type === 'longopt') {
|
| 387: | return isset($this->options[$name]) ? $name : false;
|
| 388: | }
|
| 389: |
|
| 390: | if ($type === 'shortopt') {
|
| 391: | return isset($this->shortcuts[$name]) ? $this->shortcuts[$name] : false;
|
| 392: | }
|
| 393: |
|
| 394: | return false;
|
| 395: | }
|
| 396: |
|
| 397: | |
| 398: | |
| 399: | |
| 400: | |
| 401: | |
| 402: | |
| 403: | |
| 404: | |
| 405: | |
| 406: |
|
| 407: | private function getArgTypeAndName($pos)
|
| 408: | {
|
| 409: | if (isset($this->argv[$pos])) {
|
| 410: | $arg = $this->argv[$pos];
|
| 411: | } else {
|
| 412: | return array(null, null);
|
| 413: | }
|
| 414: |
|
| 415: | $a = explode('=', $arg);
|
| 416: |
|
| 417: | if (substr($a[0], 0, 2) === '--') {
|
| 418: | $type = 'longopt';
|
| 419: | $name = ltrim($a[0], '--');
|
| 420: | } elseif (substr($a[0], 0, 1) === '-') {
|
| 421: | $type = 'shortopt';
|
| 422: | $name = ltrim($a[0], '-');
|
| 423: | } else {
|
| 424: | $type = 'value';
|
| 425: | $name = $a[0];
|
| 426: | }
|
| 427: |
|
| 428: | return array($type, $name);
|
| 429: | }
|
| 430: |
|
| 431: | |
| 432: | |
| 433: | |
| 434: | |
| 435: |
|
| 436: | public function parseArguments($argv = array())
|
| 437: | {
|
| 438: | $this->argv = $argv;
|
| 439: | $this->resetToDefaults();
|
| 440: | $parsedArguments = array();
|
| 441: |
|
| 442: | foreach ($argv as $pos => $arg) {
|
| 443: | list($type, $name) = $this->getArgTypeAndName($pos);
|
| 444: | list($lastType, $lastName) = $this->getArgTypeAndName($pos - 1);
|
| 445: |
|
| 446: | $name = $this->validateOption($name, $type);
|
| 447: | if (!$name) {
|
| 448: | continue;
|
| 449: | }
|
| 450: |
|
| 451: | $a = explode('=', $arg);
|
| 452: | if (count($a) === 2) {
|
| 453: |
|
| 454: | $value = $a[1];
|
| 455: | if ($type === 'value') {
|
| 456: | $parsedArguments[] = $value;
|
| 457: | } else {
|
| 458: | $this->parsedOptions[$name] = $value;
|
| 459: | }
|
| 460: | } else {
|
| 461: | $value = $a[0];
|
| 462: | if ($type === 'value') {
|
| 463: | if (in_array($lastType, array('shortopt', 'longopt')) && $lastName = $this->validateOption($lastName, $lastType)) {
|
| 464: | if ($this->options[$lastName]['type'] === LC_CONSOLE_OPTION_NOVALUE) {
|
| 465: | $parsedArguments[] = $value;
|
| 466: | } elseif ($this->parsedOptions[$lastName] === true) {
|
| 467: | $this->parsedOptions[$lastName] = $value;
|
| 468: | } else {
|
| 469: | $parsedArguments[] = $value;
|
| 470: | }
|
| 471: | } else {
|
| 472: | $parsedArguments[] = $value;
|
| 473: | }
|
| 474: | } else {
|
| 475: | $this->parsedOptions[$name] = true;
|
| 476: | }
|
| 477: | }
|
| 478: | }
|
| 479: |
|
| 480: | foreach ($parsedArguments as $key => $value) {
|
| 481: | if (isset($this->arguments[$key])) {
|
| 482: | $name = $this->arguments[$key]['name'];
|
| 483: | $this->parsedArguments[$name] = $value;
|
| 484: | }
|
| 485: | }
|
| 486: |
|
| 487: | return array($this->parsedArguments, $this->parsedOptions);
|
| 488: | }
|
| 489: |
|
| 490: | |
| 491: | |
| 492: | |
| 493: | |
| 494: | |
| 495: |
|
| 496: | public function confirm($message = 'Are you sure? Type "yes" or "y" to continue:', $input = array('yes', 'y'))
|
| 497: | {
|
| 498: | _write(trim($message) . ' ');
|
| 499: |
|
| 500: | $handle = fopen("php://stdin", "r");
|
| 501: | $line = fgets($handle);
|
| 502: | $line = strtolower(trim($line));
|
| 503: |
|
| 504: | if (is_string($input) && $line == $input) {
|
| 505: | fclose($handle);
|
| 506: | return true;
|
| 507: | }
|
| 508: |
|
| 509: | if (is_array($input) && in_array($line, $input)) {
|
| 510: | fclose($handle);
|
| 511: | return true;
|
| 512: | }
|
| 513: |
|
| 514: | fclose($handle);
|
| 515: | return false;
|
| 516: | }
|
| 517: | }
|
| 518: | |