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