1: <?php
2:
3: namespace Charcoal\App\Script;
4:
5: use \InvalidArgumentException;
6: use \RuntimeException;
7:
8:
9: use \Psr\Log\LoggerAwareInterface;
10: use \Psr\Log\LoggerAwareTrait;
11:
12:
13: use \Psr\Http\Message\RequestInterface;
14: use \Psr\Http\Message\ResponseInterface;
15:
16:
17: use \Pimple\Container;
18:
19:
20: use \League\CLImate\CLImate;
21: use \League\CLImate\Util\Reader\ReaderInterface;
22:
23:
24: use \Charcoal\Config\AbstractEntity;
25:
26:
27: use \Charcoal\App\AppInterface;
28: use \Charcoal\App\Script\ScriptInterface;
29:
30: 31: 32:
33: abstract class AbstractScript extends AbstractEntity implements
34: LoggerAwareInterface,
35: ScriptInterface
36: {
37: use LoggerAwareTrait;
38:
39: 40: 41:
42: private $ident;
43:
44: 45: 46:
47: private $description;
48:
49: 50: 51:
52: private $arguments;
53:
54: 55: 56:
57: private $climate;
58:
59: 60: 61:
62: private $climateReader;
63:
64: 65: 66:
67: private $quiet = false;
68:
69: 70: 71:
72: private $verbose = false;
73:
74: 75: 76:
77: private $interactive = false;
78:
79: 80: 81:
82: private $dryRun = false;
83:
84: 85: 86: 87: 88:
89: public function __construct($data = null)
90: {
91: $this->setLogger($data['logger']);
92: $this->setClimate($data['climate']);
93: if (isset($data['climate_reader'])) {
94: $this->setClimateReader($data['climate_reader']);
95: }
96:
97: if (isset($data['container'])) {
98: $this->setDependencies($data['container']);
99: }
100: }
101:
102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112:
113: public function setDependencies(Container $container)
114: {
115:
116: }
117:
118: 119: 120:
121: protected function init()
122: {
123: $arguments = $this->defaultArguments();
124: $this->setArguments($arguments);
125: }
126:
127: 128: 129: 130: 131:
132: final public function __invoke(RequestInterface $request, ResponseInterface $response)
133: {
134: $this->init();
135:
136: $climate = $this->climate();
137: $arguments = $climate->arguments;
138:
139: if ($arguments->defined('help')) {
140: $climate->usage();
141: return $response;
142: }
143:
144: if ($arguments->defined('quiet') && $arguments->defined('verbose')) {
145: $climate->error('You must choose one of --quiet or --verbose');
146: return $response;
147: }
148:
149: if ($arguments->defined('quiet')) {
150: $this->setQuiet(true);
151: }
152:
153: if ($arguments->defined('verbose')) {
154: $this->setVerbose(true);
155: }
156:
157: if ($arguments->defined('interactive')) {
158: $this->setInteractive(true);
159: }
160:
161: if ($arguments->defined('dry_run')) {
162: $this->setDryRun(true);
163: }
164:
165: $arguments->parse();
166:
167: return $this->run($request, $response);
168: }
169:
170: 171: 172: 173:
174: private function setClimate(CLImate $climate)
175: {
176: $this->climate = $climate;
177: }
178:
179: 180: 181: 182: 183: 184: 185: 186: 187:
188: protected function climate()
189: {
190: return $this->climate;
191: }
192:
193: 194: 195: 196:
197: private function setClimateReader(ReaderInterface $climateReader)
198: {
199: $this->climateReader = $climateReader;
200: }
201:
202: 203: 204:
205: protected function climateReader()
206: {
207: return $this->climateReader;
208: }
209:
210: 211: 212: 213: 214:
215: public function defaultArguments()
216: {
217: return [
218: 'help' => [
219: 'prefix' => 'h',
220: 'longPrefix' => 'help',
221: 'noValue' => true,
222: 'description' => 'Display help information.'
223: ],
224: 'quiet' => [
225: 'prefix' => 'q',
226: 'longPrefix' => 'quiet',
227: 'noValue' => true,
228: 'description' => 'Only print error and warning messages.'
229: ],
230: 'verbose' => [
231: 'prefix' => 'v',
232: 'longPrefix' => 'verbose',
233: 'noValue' => true,
234: 'description' => 'Increase verbosity of messages.'
235: ],
236: 'interactive' => [
237: 'prefix' => 'i',
238: 'longPrefix' => 'interactive',
239: 'noValue' => true,
240: 'description' => 'Ask any interactive question.'
241: ],
242: 'dry_run' => [
243: 'longPrefix' => 'dry-run',
244: 'noValue' => true,
245: 'description' => 'This will simulate the script and show you what would happen.'
246: ]
247: ];
248: }
249:
250: 251: 252: 253: 254:
255: public function setIdent($ident)
256: {
257: if (!is_string($ident)) {
258: throw new InvalidArgumentException(
259: 'Ident must be a string'
260: );
261: }
262: $this->ident = $ident;
263: return $this;
264: }
265:
266: 267: 268:
269: public function ident()
270: {
271: return $this->ident;
272: }
273:
274: 275: 276: 277: 278:
279: public function setDescription($description)
280: {
281: if (!is_string($description)) {
282: throw new InvalidArgumentException(
283: 'Description must be a string'
284: );
285: }
286: $this->description = $description;
287: $this->climate()->description($description);
288: return $this;
289: }
290:
291: 292: 293:
294: public function description()
295: {
296: return $this->description;
297: }
298:
299: 300: 301: 302:
303: public function setQuiet($quiet)
304: {
305: $this->quiet = !!$quiet;
306: return $this;
307: }
308:
309: 310: 311:
312: public function quiet()
313: {
314: return $this->quiet;
315: }
316:
317: 318: 319: 320:
321: public function setVerbose($verbose)
322: {
323: $this->verbose = !!$verbose;
324: return $this;
325: }
326:
327: 328: 329:
330: public function verbose()
331: {
332: return $this->verbose;
333: }
334:
335: 336: 337: 338:
339: public function setInteractive($interactive)
340: {
341: $this->interactive = !!$interactive;
342: return $this;
343: }
344:
345: 346: 347:
348: public function interactive()
349: {
350: return $this->interactive;
351: }
352:
353: 354: 355: 356:
357: public function setDryRun($simulate)
358: {
359: $this->dryRun = !!$simulate;
360: return $this;
361: }
362:
363: 364: 365:
366: public function dryRun()
367: {
368: return $this->dryRun;
369: }
370:
371: 372: 373: 374:
375: public function setArguments(array $arguments)
376: {
377: $this->arguments = [];
378: foreach ($arguments as $argumentIdent => $argument) {
379: $this->addArgument($argumentIdent, $argument);
380: }
381:
382: return $this;
383: }
384:
385: 386: 387: 388: 389: 390:
391: public function addArgument($argumentIdent, array $argument)
392: {
393: if (!is_string($argumentIdent)) {
394: throw new InvalidArgumentException(
395: 'Argument ident must be a string.'
396: );
397: }
398: $this->arguments[$argumentIdent] = $argument;
399: $this->climate()->arguments->add([$argumentIdent=>$argument]);
400: return $this;
401: }
402:
403: 404: 405:
406: public function arguments()
407: {
408: return $this->arguments;
409: }
410:
411: 412: 413: 414:
415: public function argument($argumentIdent)
416: {
417: if (!isset($this->arguments[$argumentIdent])) {
418: return null;
419: }
420: return $this->arguments[$argumentIdent];
421: }
422:
423: 424: 425: 426: 427: 428:
429: protected function argOrInput($argName)
430: {
431: $climate = $this->climate();
432:
433: $value = $climate->arguments->get($argName);
434: if ($value) {
435: return $value;
436: }
437:
438: return $this->input($argName);
439: }
440:
441: 442: 443: 444: 445: 446: 447:
448: protected function input($name)
449: {
450: $cli = $this->climate();
451: $arg = $this->argument($name);
452:
453: if ($arg) {
454: $type = (isset($arg['inputType']) ? $arg['inputType'] : 'input');
455:
456: if (isset($arg['prompt'])) {
457: $label = $arg['prompt'];
458: } elseif (isset($arg['description'])) {
459: $label = $arg['description'];
460: } else {
461: $label = $name;
462: }
463:
464: if (isset($arg['choices'])) {
465: $arg['options'] = $arg['choices'];
466: $arg['acceptValue'] = $arg['choices'];
467: }
468:
469: $accept = true;
470: } else {
471: $type = 'input';
472: $label = $name;
473: $accept = false;
474: }
475:
476: $prompt = 'prompt';
477: switch ($type) {
478: case 'checkboxes':
479: case 'radio':
480: if (!isset($arg['options'])) {
481: throw new RuntimeException(
482: sprintf('The [%s] argument has no options.', $key)
483: );
484: }
485:
486: $accept = false;
487: $input = $cli->{$type}($label, $arg['options'], $this->climateReader);
488: break;
489:
490: case 'confirm':
491: $prompt = 'confirmed';
492: $input = $cli->confirm($label, $this->climateReader);
493: break;
494:
495: case 'password':
496: $input = $cli->password($label, $this->climateReader);
497: $input->multiLine();
498: break;
499:
500: case 'multiline':
501: $input = $cli->input($label, $this->climateReader);
502: $input->multiLine();
503: break;
504:
505: default:
506: $input = $cli->input($label, $this->climateReader);
507: break;
508: }
509:
510: if ($accept) {
511: if (isset($arg['acceptValue'])) {
512: if (is_array($arg['acceptValue']) || is_callable($arg['acceptValue'])) {
513: $input->accept($arg['acceptValue']);
514: }
515: }
516: }
517:
518: return $input->{$prompt}();
519: }
520: }
521: