Colors::write()   C
last analyzed

Complexity

Conditions 11
Paths 48

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 20
c 0
b 0
f 0
nc 48
nop 4
dl 0
loc 37
rs 5.2653

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Koch Framework
5
 * Jens-André Koch © 2005 - onwards.
6
 *
7
 * This file is part of "Koch Framework".
8
 *
9
 * License: GNU/GPL v2 or any later version, see LICENSE file.
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace Koch\Console;
26
27
/**
28
 * Command Line Colors.
29
 *
30
 * The PHP Command Line Interface (CLI) has not built-in coloring for script output.
31
 * This class adds some color to PHP CLI output by using ANSI escape codes/sequences.
32
 *
33
 * These escape codes work on Linux BASH shells.
34
 * For ANSI coloring on the Windows console, you might consider using ANSICON.
35
 *
36
 * @link https://github.com/adoxa/ansicon
37
 *
38
 * Ansi Escape Sequences takes from
39
 * @link http://ascii-table.com/ansi-escape-sequences.php
40
 * @link https://wiki.archlinux.org/index.php/Color_Bash_Prompt
41
 */
42
class Colors
0 ignored issues
show
Coding Style introduced by
Colors does not seem to conform to the naming convention (Utils?$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
43
{
44
    // Ansi foreground colors
45
    private static $foreground = [
46
        'black'       => '0;30',
47
        'dark_gray'   => '1;30',
48
        'red'         => '0;31',
49
        'bold_red'    => '1;31',
50
        'green'       => '0;32',
51
        'bold_green'  => '1;32',
52
        'brown'       => '0;33',
53
        'yellow'      => '1;33',
54
        'blue'        => '0;34',
55
        'bold_blue'   => '1;34',
56
        'purple'      => '0;35',
57
        'bold_purple' => '1;35',
58
        'cyan'        => '0;36',
59
        'bold_cyan'   => '1;36',
60
        'white'       => '1;37',
61
        'bold_gray'   => '0;37',
62
    ];
63
64
    // Ansi background colors
65
    private static $background = [
66
        'black'   => '40',
67
        'red'     => '41',
68
        'magenta' => '45',
69
        'yellow'  => '43',
70
        'green'   => '42',
71
        'blue'    => '44',
72
        'cyan'    => '46',
73
        'grey'    => '47',
74
    ];
75
76
    // Ansi Modifiers
77
    private static $modifier = [
78
        'reset'         => '0',
79
        'bold'          => '1',
80
        'dark'          => '2',
81
        'italic'        => '3',
82
        'underline'     => '4',
83
        'blink'         => '5',
84
        'blinkfast'     => '6',
85
        'inverse'       => '7',
86
        'strikethrough' => '9',
87
    ];
88
89
    // Unicode Symbol Name to Octal Escape Sequence
90
    private static $unicode = [
91
        'ok'       => '✓', // "check mark" - \u221A
92
        'fail'     => '✖', // "ballot x" - \u00D7
93
        'big fail' => '✖',
94
        'big ok'   => '✔',
95
    ];
96
97
    /**
98
     * @param string   $symbol
99
     * @param string[] $options
0 ignored issues
show
Documentation introduced by
Should the type for parameter $options 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...
100
     */
101
    public static function unicodeSymbol($symbol, $options = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
102
    {
103 View Code Duplication
        if (false === isset(self::$unicode[$symbol])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
104
            throw new \InvalidArgumentException(
105
                sprintf(
106
                    'Invalid unicode symbol specified: "%s". Expected one of (%s).',
107
                    $symbol,
108
                    implode(', ', array_keys(self::$unicode))
109
                )
110
            );
111
        }
112
113
        $symbol = self::$unicode[$symbol];
114
115
        return is_array($options) ? self::write($symbol, $options) : $symbol;
116
    }
117
118
    /**
119
     * @param string $background
0 ignored issues
show
Documentation introduced by
Should the type for parameter $background 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...
120
     * @param string $modifiers
0 ignored issues
show
Documentation introduced by
Should the type for parameter $modifiers 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...
121
     */
122
    public static function write($text, $foreground = null, $background = null, $modifiers = null)
123
    {
124
        if (is_array($foreground)) {
125
            $options    = self::setOptions($foreground);
126
            $foreground = array_shift($options); // 0
127
            $background = array_shift($options); // 1
128
            $modifiers  = $options;
129
        }
130
131
        $codes = [];
132
133
        if (null !== $foreground and isset(self::$foreground[$foreground])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
134
            $codes[] = self::$foreground[$foreground];
135
        }
136
137
        if (null !== $background and isset(self::$background[$background])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
138
            $codes[] = self::$background[$background];
139
        }
140
141
        if (null !== $modifiers) {
142
            // handle comma separated list of modifiers
143
            if (is_string($modifiers)) {
144
                $modifiers = explode(',', $modifiers);
145
            }
146
            foreach ($modifiers as $modifier) {
147
                if (isset(self::$modifier[$modifier])) {
148
                    $codes[] = self::$modifier[$modifier];
149
                }
150
            }
151
        }
152
153
        if (is_array($codes)) {
154
            $escapeCodes = implode(';', $codes);
155
        }
156
157
        return sprintf('\033[%sm%s\033[0m', $escapeCodes, $text);
0 ignored issues
show
Bug introduced by
The variable $escapeCodes does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
158
    }
159
160
    public static function setOptions($options)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
161
    {
162
        // string to array
163
        if (is_string($options)) {
164
            $options = explode(',', $options);
165
        }
166
167
        return $options;
168
    }
169
170
    /**
171
     * Colorizes a specific parts of a text, which are matched by search_regexp.
172
     *
173
     * @param string
174
     * @param string regexp
175
     * @param mixed|string|array
176
     * @param string $text
177
     * @param string $search_regexp
178
     * @param string $color
179
     */
180
    public static function colorizePart($text, $search_regexp, $color)
0 ignored issues
show
Coding Style introduced by
$search_regexp does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
181
    {
182
        $callback = function ($matches) use ($color) {
183
            return Colors::write($matches[1], $color);
184
        };
185
186
        $ansi_text = preg_replace_callback("/($search_regexp)/", $callback, $text);
0 ignored issues
show
Coding Style introduced by
$ansi_text does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
187
188
        return is_null($ansi_text) ? $text : $ansi_text;
0 ignored issues
show
Coding Style introduced by
$ansi_text does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
189
    }
190
191
    /**
192
     * @param int $value
193
     */
194
    public static function colorizeReturnValue($value)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
195
    {
196
        return ($value === 0) ? self::unicodeSymbol('fail', ['red']) : self::unicodeSymbol('ok', ['green']);
197
    }
198
}
199