QueueExplainer::getTree()   C
last analyzed

Complexity

Conditions 7
Paths 12

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 8
cts 8
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 17
nc 12
nop 1
crap 7
1
<?php
2
3
/*
4
 * This file is part of Rocketeer
5
 *
6
 * (c) Maxime Fabre <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 */
12
13
namespace Rocketeer\Services\Display;
14
15
use Rocketeer\Traits\ContainerAwareTrait;
16
17
/**
18
 * Gives some insight into what task is executing,
19
 * what it's doing, what its parent is, etc.
20
 */
21
class QueueExplainer
22
{
23
    use ContainerAwareTrait;
24
25
    /**
26
     * The level at which to display statuses.
27
     *
28
     * @var int
29
     */
30
    public $level = 0;
31
32
    /**
33
     * @var int
34
     */
35
    protected $padding = 2;
36
37
    /**
38
     * Length of the longest handle to display.
39
     *
40
     * @var int
41
     */
42
    protected $longest;
43
44
    //////////////////////////////////////////////////////////////////////
45
    /////////////////////////////// STATUS ///////////////////////////////
46
    //////////////////////////////////////////////////////////////////////
47
48
    /**
49
     * Execute a task in a level below.
50
     *
51
     * @param callable $callback
52
     * @param int      $offset
53
     *
54
     * @return mixed
55 91
     */
56
    public function displayBelow(callable $callback, $offset = 1)
57 91
    {
58
        if (!$this->hasCommand()) {
59
            return $callback();
60
        }
61 91
62 91
        $this->level += $offset;
63 91
        $results = $callback();
64
        $this->level -= $offset;
65 91
66
        return $results;
67
    }
68
69
    /**
70
     * Display a status.
71
     *
72
     * @param string|null $info
73
     * @param string|null $details
74
     * @param string|null $origin
75
     * @param float|null  $time
76 84
     */
77
    public function display($info = null, $details = null, $origin = null, $time = null)
78 84
    {
79
        if (!$this->hasCommand()) {
80
            return;
81
        }
82
83 84
        // Build handle
84
        $comment = $this->getTree().$this->getFork();
85
86 84
        // Add details
87 84
        if ($info) {
88 84
            $comment .= ' <info>'.$info.'</info>';
89 84
        }
90 64
        if ($details) {
91 64
            $comment .= ' <comment>('.$details.')</comment>';
92 84
        }
93 27
        if ($origin) {
94 27
            $comment .= ' fired by <info>'.$origin.'</info>';
95 84
        }
96 10
        if ($time) {
97 10
            $comment .= ' [~'.$time.'s]';
98
        }
99 84
100 84
        $this->command->writeln($comment);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method writeln does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? 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...
101
    }
102
103
    //////////////////////////////////////////////////////////////////////
104
    ////////////////////////////// PROGRESS //////////////////////////////
105
    //////////////////////////////////////////////////////////////////////
106
107
    /**
108
     * Format and send a message by the command.
109
     *
110
     * @param string|string[] $message
111
     * @param string|null     $color
112
     * @param bool            $withTree
113
     *
114
     * @return string|null
115 111
     */
116
    public function line($message, $color = null, $withTree = true)
117 111
    {
118 111
        $message = $this->toLines($message);
119 3
        if (!$this->hasCommand()) {
120
            return;
121
        }
122
123 111
        // Format the message
124 111
        $formatted = $message;
125 111
        foreach ($formatted as &$line) {
126 111
            $line = $this->colorize($line, $color);
127 111
            $line = $withTree ? $this->getTree().'|'.str_repeat(' ', $this->padding).$this->getFork().' '.$line : $line;
128
        }
129
130 111
        // Rejoin strings
131 111
        $formatted = implode(PHP_EOL, $formatted);
132
        $message = implode(PHP_EOL, $message);
133
134 111
        // Pass to command and log
135 111
        $this->command->writeln($formatted);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method writeln does not exist on object<Rocketeer\Console...mmands\AbstractCommand>? 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...
136
        $this->logs->log($message);
137 111
138
        return $formatted;
139
    }
140
141
    /**
142
     * Get the format for a progress bar
143
     * embedded within the tree.
144
     *
145
     * @param string $format
146
     *
147 81
     * @return string
148
     */
149 3
    public function getProgressBarFormat($format)
150 3
    {
151 3
        return $this->displayBelow(function () use (&$format) {
152 3
            $tree = $this->getTree().$this->getFork();
153
154 81
            $format = explode(PHP_EOL, $format);
155
            $format = $tree.implode(PHP_EOL.$tree.' ', $format);
156
157
            return $format;
158
        }, 2);
159
    }
160
161
    /**
162 26
     * Display a server-related message.
163
     *
164 26
     * @param string|string[] $message
165
     *
166
     * @return string|null
167
     */
168
    public function server($message)
169
    {
170
        $message = $this->toLines($message);
171
        foreach ($message as &$line) {
172 2
            $line = sprintf('<comment>[%s]</comment> %s', $this->connections->getCurrentConnectionKey()->toLongHandle(), $line);
173
        }
174 2
175
        return $this->line($message, null);
176
    }
177
178
    /**
179
     * @param string|string[] $message
180
     *
181
     * @return string|null
182 11
     */
183
    public function success($message)
184 11
    {
185
        return $this->line($message, 'green');
186
    }
187
188
    /**
189
     * @param string|string[] $message
190
     *
191
     * @return string|null
192 13
     */
193
    public function comment($message)
194 13
    {
195
        return $this->line($message, 'comment');
196
    }
197
198
    /**
199
     * @param string|string[] $message
200
     *
201
     * @return string|null
202
     */
203
    public function info($message)
204
    {
205
        return $this->line($message, 'info');
206 14
    }
207
208 14
    /**
209 8
     * @param string|string[] $message
210
     *
211
     * @return string|null
212
     */
213 14
    public function error($message)
214 14
    {
215 14
        return $this->line($message, 'error');
216 14
    }
217 14
218 14
    //////////////////////////////////////////////////////////////////////
219 3
    ////////////////////////////// HELPERS ///////////////////////////////
220 3
    //////////////////////////////////////////////////////////////////////
221
222 14
    /**
223 14
     * Get the longest size an handle can have.
224 14
     *
225 14
     * @return int
226
     */
227
    protected function getLongestSize()
228 14
    {
229 14
        if ($this->longest) {
230
            return $this->longest;
231
        }
232 14
233
        // Build possible handles
234 14
        $strings = [];
235
        $connections = $this->connections->getActiveConnections();
236
        $stages = (array) $this->connections->getAvailableStages() ?: [''];
237
        foreach ($connections as $handle => $connection) {
238
            $servers = $connection->getConnectionKey()->servers;
239
            if (count($servers) > 1) {
240
                $handle .= '/'.count($servers);
241
            }
242 145
243
            foreach ($stages as $stage) {
244
                $strings[] = trim($handle.'/'.$stage, '/');
245 145
            }
246 145
        }
247 145
248 145
        // Get longest string
249
        $strings = array_map('strlen', $strings);
250 145
        $strings = $strings ? max($strings) : 0;
251 145
252 14
        // Cache value
253 14
        $this->longest = $strings;
254 14
255 14
        return $this->longest;
256
    }
257
258 14
    /**
259 14
     * @param string $dashes
0 ignored issues
show
Documentation introduced by Maxime Fabre
Should the type for parameter $dashes not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
260 14
     *
261 14
     * @return string
262
     */
263
    protected function getTree($dashes = null)
264 145
    {
265 145
        // Build handle
266
        $dashes = $dashes ?: '│'.str_repeat(' ', $this->padding);
267 145
        $numberConnections = count($this->connections->getActiveConnections());
268
        $numberStages = count($this->connections->getAvailableStages());
269
        $numberServers = count($this->connections->getCurrentConnectionKey()->servers);
270
271
        $tree = null;
272
        if ($numberConnections > 1 || $numberStages > 1 || $numberServers > 1) {
273 145
            $handle = $this->connections->getCurrentConnectionKey()->toHandle();
274
            $spacing = $this->getLongestSize() - mb_strlen($handle) + 2;
275 145
            $spacing = max(1, $spacing);
276
            $spacing = str_repeat(' ', $spacing);
277
278
            // Build tree and command
279
            $handle = $handle === 'dummy' ? 'local' : $handle;
280
            $spacing = mb_substr($spacing, mb_strlen($spacing) / 2);
281
            $tree .= sprintf('<bg=magenta;options=bold>%s%s%s</bg=magenta;options=bold> ', $spacing, $handle, $spacing);
282
        }
283
284
        // Add tree
285
        $dashes = $this->level ? str_repeat($dashes, $this->level) : null;
286 111
        $tree .= $dashes;
287
288 111
        return $tree;
289 64
    }
290
291
    /**
292
     * @return string
293 92
     */
294
    protected function getFork()
295 92
    {
296
        return '├'.str_repeat('─', $this->padding - 1);
297
    }
298
299
    /**
300
     * Colorize text using Symfony Console tags.
301
     *
302
     * @param string      $message
303 111
     * @param string|null $color
304
     *
305 111
     * @return string
306
     */
307
    protected function colorize($message, $color = null)
308
    {
309
        if (!$color) {
310
            return $message;
311
        }
312
313
        // Create tag
314
        $tag = in_array($color, ['error', 'comment', 'info'], true) ? $color : 'fg='.$color;
315
316
        return sprintf('<%s>%s</%s>', $tag, $message, $tag);
317
    }
318
319
    /**
320
     * @param string|string[] $message
321
     *
322
     * @return array
323
     */
324
    protected function toLines($message)
325
    {
326
        return is_array($message) ? $message : explode(PHP_EOL, $message);
327
    }
328
}
329