Completed
Push — progress-each ( b08ce8 )
by Craig
13:48 queued 05:21
created

CLImate::each()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 0
cts 15
cp 0
rs 8.7624
c 0
b 0
f 0
cc 6
eloc 12
nc 7
nop 2
crap 42
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);
0 ignored issues
show
Bug introduced by
It seems like $class defined by parameter $class on line 206 can also be of type array or object; however, League\CLImate\TerminalO...\Router::addExtension() does only seem to accept string, maybe add an additional type check?

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.

Loading history...
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();
0 ignored issues
show
Documentation Bug introduced by
The method spinner does not exist on object<League\CLImate\CLImate>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
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