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: | |