1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace League\CLImate; |
4
|
|
|
|
5
|
|
|
use League\CLImate\Argument\Manager as ArgumentManager; |
6
|
|
|
use League\CLImate\Decorator\Style; |
7
|
|
|
use League\CLImate\Settings\Manager as SettingsManager; |
8
|
|
|
use League\CLImate\TerminalObject\Router\Router; |
9
|
|
|
use League\CLImate\Util\Helper; |
10
|
|
|
use League\CLImate\Util\Output; |
11
|
|
|
use League\CLImate\Util\UtilFactory; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* @method mixed black(string $str = null) |
15
|
|
|
* @method mixed red(string $str = null) |
16
|
|
|
* @method mixed green(string $str = null) |
17
|
|
|
* @method mixed yellow(string $str = null) |
18
|
|
|
* @method mixed blue(string $str = null) |
19
|
|
|
* @method mixed magenta(string $str = null) |
20
|
|
|
* @method mixed cyan(string $str = null) |
21
|
|
|
* @method mixed lightGray(string $str = null) |
22
|
|
|
* @method mixed darkGray(string $str = null) |
23
|
|
|
* @method mixed lightRed(string $str = null) |
24
|
|
|
* @method mixed lightGreen(string $str = null) |
25
|
|
|
* @method mixed lightYellow(string $str = null) |
26
|
|
|
* @method mixed lightBlue(string $str = null) |
27
|
|
|
* @method mixed lightMagenta(string $str = null) |
28
|
|
|
* @method mixed lightCyan(string $str = null) |
29
|
|
|
* @method mixed white(string $str = null) |
30
|
|
|
* |
31
|
|
|
* @method mixed backgroundBlack(string $str = null) |
32
|
|
|
* @method mixed backgroundRed(string $str = null) |
33
|
|
|
* @method mixed backgroundGreen(string $str = null) |
34
|
|
|
* @method mixed backgroundYellow(string $str = null) |
35
|
|
|
* @method mixed backgroundBlue(string $str = null) |
36
|
|
|
* @method mixed backgroundMagenta(string $str = null) |
37
|
|
|
* @method mixed backgroundCyan(string $str = null) |
38
|
|
|
* @method mixed backgroundLightGray(string $str = null) |
39
|
|
|
* @method mixed backgroundDarkGray(string $str = null) |
40
|
|
|
* @method mixed backgroundLightRed(string $str = null) |
41
|
|
|
* @method mixed backgroundLightGreen(string $str = null) |
42
|
|
|
* @method mixed backgroundLightYellow(string $str = null) |
43
|
|
|
* @method mixed backgroundLightBlue(string $str = null) |
44
|
|
|
* @method mixed backgroundLightMagenta(string $str = null) |
45
|
|
|
* @method mixed backgroundLightCyan(string $str = null) |
46
|
|
|
* @method mixed backgroundWhite(string $str = null) |
47
|
|
|
* |
48
|
|
|
* @method mixed bold(string $str = null) |
49
|
|
|
* @method mixed dim(string $str = null) |
50
|
|
|
* @method mixed underline(string $str = null) |
51
|
|
|
* @method mixed blink(string $str = null) |
52
|
|
|
* @method mixed invert(string $str = null) |
53
|
|
|
* @method mixed hidden(string $str = null) |
54
|
|
|
* |
55
|
|
|
* @method mixed info(string $str = null) |
56
|
|
|
* @method mixed comment(string $str = null) |
57
|
|
|
* @method mixed whisper(string $str = null) |
58
|
|
|
* @method mixed shout(string $str = null) |
59
|
|
|
* @method mixed error(string $str = null) |
60
|
|
|
* |
61
|
|
|
* @method mixed out(string $str) |
62
|
|
|
* @method mixed inline(string $str) |
63
|
|
|
* @method mixed table(array $data) |
64
|
|
|
* @method mixed json(mixed $var) |
65
|
|
|
* @method mixed br($count = 1) |
66
|
|
|
* @method mixed tab($count = 1) |
67
|
|
|
* @method mixed draw(string $art) |
68
|
|
|
* @method mixed border(string $char = null, integer $length = null) |
69
|
|
|
* @method mixed dump(mixed $var) |
70
|
|
|
* @method mixed flank(string $output, string $char = null, integer $length = null) |
71
|
|
|
* @method mixed progress(integer $total = null) |
72
|
|
|
* @method mixed padding(integer $length = 0, string $char = '.') |
73
|
|
|
* @method mixed input(string $prompt, Util\Reader\ReaderInterface $reader = null) |
74
|
|
|
* @method mixed confirm(string $prompt, Util\Reader\ReaderInterface $reader = null) |
75
|
|
|
* @method mixed password(string $prompt, Util\Reader\ReaderInterface $reader = null) |
76
|
|
|
* @method mixed checkboxes(string $prompt, array $options, Util\Reader\ReaderInterface $reader = null) |
77
|
|
|
* @method mixed radio(string $prompt, array $options, Util\Reader\ReaderInterface $reader = null) |
78
|
|
|
* @method mixed animation(string $art, TerminalObject\Helper\Sleeper $sleeper = null) |
79
|
|
|
* @method mixed columns(array $data, $column_count = null) |
80
|
|
|
* @method mixed clear() |
81
|
|
|
* |
82
|
|
|
* @method CLImate addArt(string $dir) |
83
|
|
|
*/ |
84
|
|
|
class CLImate |
85
|
|
|
{ |
86
|
|
|
/** |
87
|
|
|
* An instance of the Style class |
88
|
|
|
* |
89
|
|
|
* @var \League\CLImate\Decorator\Style $style |
90
|
|
|
*/ |
91
|
|
|
public $style; |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* An instance of the Terminal Object Router class |
95
|
|
|
* |
96
|
|
|
* @var \League\CLImate\TerminalObject\Router\Router $router |
97
|
|
|
*/ |
98
|
|
|
protected $router; |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* An instance of the Settings Manager class |
102
|
|
|
* |
103
|
|
|
* @var \League\CLImate\Settings\Manager $settings |
104
|
|
|
*/ |
105
|
|
|
protected $settings; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* An instance of the Argument Manager class |
109
|
|
|
* |
110
|
|
|
* @var \League\CLImate\Argument\Manager $arguments |
111
|
|
|
*/ |
112
|
|
|
public $arguments; |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* An instance of the Output class |
116
|
|
|
* |
117
|
|
|
* @var \League\CLImate\Util\Output $output |
118
|
|
|
*/ |
119
|
|
|
public $output; |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* An instance of the Util Factory |
123
|
|
|
* |
124
|
|
|
* @var \League\CLImate\Util\UtilFactory $util |
125
|
|
|
*/ |
126
|
|
|
protected $util; |
127
|
|
|
|
128
|
936 |
|
public function __construct() |
129
|
|
|
{ |
130
|
936 |
|
$this->setStyle(new Style()); |
131
|
936 |
|
$this->setRouter(new Router()); |
132
|
936 |
|
$this->setSettingsManager(new SettingsManager()); |
133
|
936 |
|
$this->setOutput(new Output()); |
134
|
936 |
|
$this->setUtil(new UtilFactory()); |
135
|
936 |
|
$this->setArgumentManager(new ArgumentManager()); |
136
|
936 |
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Set the style property |
140
|
|
|
* |
141
|
|
|
* @param \League\CLImate\Decorator\Style $style |
142
|
|
|
*/ |
143
|
936 |
|
public function setStyle(Style $style) |
144
|
|
|
{ |
145
|
936 |
|
$this->style = $style; |
146
|
936 |
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Set the router property |
150
|
|
|
* |
151
|
|
|
* @param \League\CLImate\TerminalObject\Router\Router $router |
152
|
|
|
*/ |
153
|
936 |
|
public function setRouter(Router $router) |
154
|
|
|
{ |
155
|
936 |
|
$this->router = $router; |
156
|
936 |
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Set the settings property |
160
|
|
|
* |
161
|
|
|
* @param \League\CLImate\Settings\Manager $manager |
162
|
|
|
*/ |
163
|
936 |
|
public function setSettingsManager(SettingsManager $manager) |
164
|
|
|
{ |
165
|
936 |
|
$this->settings = $manager; |
166
|
936 |
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Set the arguments property |
170
|
|
|
* |
171
|
|
|
* @param \League\CLImate\Argument\Manager $manager |
172
|
|
|
*/ |
173
|
936 |
|
public function setArgumentManager(ArgumentManager $manager) |
174
|
|
|
{ |
175
|
936 |
|
$this->arguments = $manager; |
176
|
936 |
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Set the output property |
180
|
|
|
* |
181
|
|
|
* @param \League\CLImate\Util\Output $output |
182
|
|
|
*/ |
183
|
936 |
|
public function setOutput(Output $output) |
184
|
|
|
{ |
185
|
936 |
|
$this->output = $output; |
186
|
936 |
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Set the util property |
190
|
|
|
* |
191
|
|
|
* @param \League\CLImate\Util\UtilFactory $util |
192
|
|
|
*/ |
193
|
936 |
|
public function setUtil(UtilFactory $util) |
194
|
|
|
{ |
195
|
936 |
|
$this->util = $util; |
196
|
936 |
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Extend CLImate with custom methods |
200
|
|
|
* |
201
|
|
|
* @param string|object|array $class |
202
|
|
|
* @param string $key Optional custom key instead of class name |
203
|
|
|
* |
204
|
|
|
* @return \League\CLImate\CLImate |
205
|
|
|
*/ |
206
|
36 |
|
public function extend($class, $key = null) |
207
|
|
|
{ |
208
|
36 |
|
$this->router->addExtension($key, $class); |
|
|
|
|
209
|
|
|
|
210
|
28 |
|
return $this; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Force ansi support on |
215
|
|
|
* |
216
|
|
|
* @return \League\CLImate\CLImate |
217
|
|
|
*/ |
218
|
4 |
|
public function forceAnsiOn() |
219
|
|
|
{ |
220
|
4 |
|
$this->util->system->forceAnsi(); |
221
|
|
|
|
222
|
4 |
|
return $this; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Force ansi support off |
227
|
|
|
* |
228
|
|
|
* @return \League\CLImate\CLImate |
229
|
|
|
*/ |
230
|
4 |
|
public function forceAnsiOff() |
231
|
|
|
{ |
232
|
4 |
|
$this->util->system->forceAnsi(false); |
233
|
|
|
|
234
|
4 |
|
return $this; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* Write line to writer once |
239
|
|
|
* |
240
|
|
|
* @param string|array $writer |
241
|
|
|
* |
242
|
|
|
* @return \League\CLImate\CLImate |
243
|
|
|
*/ |
244
|
4 |
|
public function to($writer) |
245
|
|
|
{ |
246
|
4 |
|
$this->output->once($writer); |
247
|
|
|
|
248
|
4 |
|
return $this; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Output the program's usage statement |
253
|
|
|
* |
254
|
|
|
* @param array $argv |
255
|
|
|
*/ |
256
|
4 |
|
public function usage(array $argv = null) |
257
|
|
|
{ |
258
|
4 |
|
return $this->arguments->usage($this, $argv); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Set the program's description |
263
|
|
|
* |
264
|
|
|
* @param string $description |
265
|
|
|
* |
266
|
|
|
* @return \League\CLImate\CLImate |
267
|
|
|
*/ |
268
|
4 |
|
public function description($description) |
269
|
|
|
{ |
270
|
4 |
|
$this->arguments->description($description); |
271
|
|
|
|
272
|
4 |
|
return $this; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* they pass in a total, set the total |
277
|
|
|
* |
278
|
|
|
* @param iterable $data Array or any other iterable object |
279
|
|
|
* @param callable $callback |
280
|
|
|
*/ |
281
|
|
|
public function each($data, callable $callback = null) |
282
|
|
|
{ |
283
|
|
|
if ($data instanceof \Countable || is_array($data)) { |
284
|
|
|
$total = count($data); |
285
|
|
|
if (!$total) { |
286
|
|
|
return; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
$progress = $this->progress($total); |
290
|
|
|
} else { |
291
|
|
|
$progress = $this->spinner(); |
|
|
|
|
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
foreach ($data as $key => $item) { |
295
|
|
|
if ($callback) { |
296
|
|
|
$callback($item, $key); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
$progress->advance(); |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Check if we have valid output |
305
|
|
|
* |
306
|
|
|
* @param mixed $output |
307
|
|
|
* |
308
|
|
|
* @return boolean |
309
|
|
|
*/ |
310
|
76 |
|
protected function hasOutput($output) |
311
|
|
|
{ |
312
|
76 |
|
if (!empty($output)) { |
313
|
48 |
|
return true; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
// Check for type first to avoid errors with objects/arrays/etc |
317
|
40 |
|
return ((is_string($output) || is_numeric($output)) && strlen($output) > 0); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Search for the method within the string |
322
|
|
|
* and route it if we find one. |
323
|
|
|
* |
324
|
|
|
* @param string $method |
325
|
|
|
* @param string $name |
326
|
|
|
* |
327
|
|
|
* @return string The new string without the executed method. |
328
|
|
|
*/ |
329
|
556 |
|
protected function parseStyleMethod($method, $name) |
330
|
|
|
{ |
331
|
|
|
// If the name starts with this method string... |
332
|
556 |
|
if (substr($name, 0, strlen($method)) == $method) { |
333
|
|
|
// ...remove the method name from the beginning of the string... |
334
|
112 |
|
$name = substr($name, strlen($method)); |
335
|
|
|
|
336
|
|
|
// ...and trim off any of those underscores hanging around |
337
|
112 |
|
$name = ltrim($name, '_'); |
338
|
|
|
|
339
|
112 |
|
$this->style->set($method); |
340
|
112 |
|
} |
341
|
|
|
|
342
|
556 |
|
return $name; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* Search for any style methods within the name and apply them |
347
|
|
|
* |
348
|
|
|
* @param string $name |
349
|
|
|
* @param array $method_search |
350
|
|
|
* |
351
|
|
|
* @return string Anything left over after applying styles |
352
|
|
|
*/ |
353
|
556 |
|
protected function applyStyleMethods($name, $method_search = null) |
354
|
|
|
{ |
355
|
|
|
// Get all of the possible style attributes |
356
|
556 |
|
$method_search = $method_search ?: array_keys($this->style->all()); |
357
|
|
|
|
358
|
556 |
|
$new_name = $this->searchForStyleMethods($name, $method_search); |
359
|
|
|
|
360
|
|
|
// While we still have a name left and we keep finding methods, |
361
|
|
|
// loop through the possibilities |
362
|
556 |
|
if (strlen($new_name) > 0 && $new_name != $name) { |
363
|
36 |
|
return $this->applyStyleMethods($new_name, $method_search); |
364
|
|
|
} |
365
|
|
|
|
366
|
556 |
|
return $new_name; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Search for style methods in the current name |
371
|
|
|
* |
372
|
|
|
* @param string $name |
373
|
|
|
* @param array $search |
374
|
|
|
* @return string |
375
|
|
|
*/ |
376
|
556 |
|
protected function searchForStyleMethods($name, $search) |
377
|
|
|
{ |
378
|
|
|
// Loop through the possible methods |
379
|
556 |
|
foreach ($search as $method) { |
380
|
|
|
// See if we found a valid method |
381
|
556 |
|
$name = $this->parseStyleMethod($method, $name); |
382
|
556 |
|
} |
383
|
|
|
|
384
|
556 |
|
return $name; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Build up the terminal object and return it |
389
|
|
|
* |
390
|
|
|
* @param string $name |
391
|
|
|
* @param array $arguments |
392
|
|
|
* |
393
|
|
|
* @return object|null |
394
|
|
|
*/ |
395
|
556 |
|
protected function buildTerminalObject($name, $arguments) |
396
|
|
|
{ |
397
|
|
|
// Retrieve the parser for the current set of styles |
398
|
556 |
|
$parser = $this->style->parser($this->util->system); |
399
|
|
|
|
400
|
|
|
// Reset the styles |
401
|
556 |
|
$this->style->reset(); |
402
|
|
|
|
403
|
|
|
// Execute the terminal object |
404
|
556 |
|
$this->router->settings($this->settings); |
405
|
556 |
|
$this->router->parser($parser); |
406
|
556 |
|
$this->router->output($this->output); |
407
|
556 |
|
$this->router->util($this->util); |
408
|
|
|
|
409
|
556 |
|
return $this->router->execute($name, $arguments); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* Route anything leftover after styles were applied |
414
|
|
|
* |
415
|
|
|
* @param string $name |
416
|
|
|
* @param array $arguments |
417
|
|
|
* |
418
|
|
|
* @return object|null |
419
|
|
|
*/ |
420
|
556 |
|
protected function routeRemainingMethod($name, array $arguments) |
421
|
|
|
{ |
422
|
|
|
// If we still have something left, let's figure out what it is |
423
|
556 |
|
if ($this->router->exists($name)) { |
424
|
556 |
|
$obj = $this->buildTerminalObject($name, $arguments); |
425
|
|
|
|
426
|
|
|
// If something was returned, return it |
427
|
556 |
|
if (is_object($obj)) { |
428
|
212 |
|
return $obj; |
429
|
|
|
} |
430
|
388 |
|
} elseif ($this->settings->exists($name)) { |
431
|
44 |
|
$this->settings->add($name, reset($arguments)); |
432
|
|
|
// Handle passthroughs to the arguments manager. |
433
|
44 |
|
} else { |
434
|
|
|
// If we can't find it at this point, let's fail gracefully |
435
|
4 |
|
$this->out(reset($arguments)); |
436
|
|
|
} |
437
|
388 |
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* Magic method for anything called that doesn't exist |
441
|
|
|
* |
442
|
|
|
* @param string $requested_method |
443
|
|
|
* @param array $arguments |
444
|
|
|
* |
445
|
|
|
* @return \League\CLImate\CLImate|\League\CLImate\TerminalObject\Dynamic\DynamicTerminalObject |
446
|
|
|
* |
447
|
|
|
* List of many of the possible method being called here |
448
|
|
|
* documented at the top of this class. |
449
|
|
|
*/ |
450
|
556 |
|
public function __call($requested_method, $arguments) |
451
|
|
|
{ |
452
|
|
|
// Apply any style methods that we can find first |
453
|
556 |
|
$name = $this->applyStyleMethods(Helper::snakeCase($requested_method)); |
454
|
|
|
|
455
|
|
|
// The first argument is the string|array|object we want to echo out |
456
|
556 |
|
$output = reset($arguments); |
457
|
|
|
|
458
|
556 |
|
if (strlen($name)) { |
459
|
|
|
// If we have something left, let's try and route it to the appropriate place |
460
|
556 |
|
if ($result = $this->routeRemainingMethod($name, $arguments)) { |
461
|
212 |
|
return $result; |
462
|
|
|
} |
463
|
388 |
|
} elseif ($this->hasOutput($output)) { |
464
|
|
|
// If we have fulfilled all of the requested methods and we have output, output it |
465
|
48 |
|
$this->out($output); |
466
|
48 |
|
} |
467
|
|
|
|
468
|
388 |
|
return $this; |
469
|
|
|
} |
470
|
|
|
} |
471
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.