Test Failed
Pull Request — master (#12)
by wujunze
03:09
created

ConsoleColor   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 88
c 1
b 0
f 1
dl 0
loc 177
rs 10
wmc 27

10 Methods

Rating   Name   Duplication   Size   Complexity  
B isSupported() 0 11 7
A setForceStyle() 0 3 1
A styleSequence() 0 12 4
A isValidStyle() 0 3 2
A apply() 0 17 6
A escSequence() 0 3 1
A __construct() 0 4 1
A getPossibleStyles() 0 3 1
A are256ColorsSupported() 0 6 3
A isStyleForced() 0 3 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Seasx\SeasLogger;
5
6
use InvalidArgumentException;
7
8
/**
9
 * Class ConsoleColor
10
 * @package Seasx\SeasLogger
11
 */
12
class ConsoleColor
13
{
14
    const FOREGROUND = 38,
15
        BACKGROUND = 48;
16
    const COLOR256_REGEXP = '~^(bg_)?color_([0-9]{1,3})$~';
17
    const RESET_STYLE = 0;
18
    /** @var bool */
19
    private $isSupported;
20
    /** @var bool */
21
    private $forceStyle = false;
22
    /** @var array */
23
    private $styles = array(
24
        'none' => null,
25
        'bold' => '1',
26
        'dark' => '2',
27
        'italic' => '3',
28
        'underline' => '4',
29
        'blink' => '5',
30
        'reverse' => '7',
31
        'concealed' => '8',
32
        'default' => '39',
33
        'black' => '30',
34
        'red' => '31',
35
        'green' => '32',
36
        'yellow' => '33',
37
        'blue' => '34',
38
        'magenta' => '35',
39
        'cyan' => '36',
40
        'light_gray' => '37',
41
        'dark_gray' => '90',
42
        'light_red' => '91',
43
        'light_green' => '92',
44
        'light_yellow' => '93',
45
        'light_blue' => '94',
46
        'light_magenta' => '95',
47
        'light_cyan' => '96',
48
        'white' => '97',
49
        'bg_default' => '49',
50
        'bg_black' => '40',
51
        'bg_red' => '41',
52
        'bg_green' => '42',
53
        'bg_yellow' => '43',
54
        'bg_blue' => '44',
55
        'bg_magenta' => '45',
56
        'bg_cyan' => '46',
57
        'bg_light_gray' => '47',
58
        'bg_dark_gray' => '100',
59
        'bg_light_red' => '101',
60
        'bg_light_green' => '102',
61
        'bg_light_yellow' => '103',
62
        'bg_light_blue' => '104',
63
        'bg_light_magenta' => '105',
64
        'bg_light_cyan' => '106',
65
        'bg_white' => '107',
66
    );
67
68
    /**
69
     * ConsoleColor constructor.
70
     * @param bool $forceStyle
71
     */
72
    public function __construct(bool $forceStyle = true)
73
    {
74
        $this->forceStyle = $forceStyle;
75
        $this->isSupported = $this->isSupported();
76
    }
77
78
    /**
79
     * @return bool
80
     */
81
    public function isSupported(): bool
82
    {
83
        if (DIRECTORY_SEPARATOR === '\\') {
84
            if (function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT)) {
85
                return true;
86
            } elseif (getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON') {
87
                return true;
88
            }
89
            return false;
90
        } else {
91
            return function_exists('posix_isatty') && @posix_isatty(STDOUT);
92
        }
93
    }
94
95
    /**
96
     * @param string $style
97
     * @param string $text
98
     * @return string
99
     */
100
    public function apply(string $style, string $text): string
101
    {
102
        if (empty($style)) {
103
            return $text;
104
        }
105
        if (!$this->isStyleForced() && !$this->isSupported()) {
106
            return $text;
107
        }
108
        if ($this->isValidStyle($style)) {
109
            $sequences = $this->styleSequence($style);
110
        } else {
111
            throw new InvalidArgumentException($style);
112
        }
113
        if (empty($sequences)) {
114
            return $text;
115
        }
116
        return $this->escSequence($sequences) . $text . $this->escSequence((string)self::RESET_STYLE);
117
    }
118
119
    /**
120
     * @return bool
121
     */
122
    public function isStyleForced(): bool
123
    {
124
        return $this->forceStyle;
125
    }
126
127
    /**
128
     * @param string $style
129
     * @return bool
130
     */
131
    private function isValidStyle(string $style): bool
132
    {
133
        return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style);
134
    }
135
136
    /**
137
     * @param string $style
138
     * @return string
139
     */
140
    private function styleSequence(string $style): ?string
141
    {
142
        if (array_key_exists($style, $this->styles)) {
143
            return $this->styles[$style];
144
        }
145
        if (!$this->are256ColorsSupported()) {
146
            return null;
147
        }
148
        preg_match(self::COLOR256_REGEXP, $style, $matches);
149
        $type = $matches[1] === 'bg_' ? self::BACKGROUND : self::FOREGROUND;
150
        $value = $matches[2];
151
        return "$type;5;$value";
152
    }
153
154
    /**
155
     * @return bool
156
     */
157
    public function are256ColorsSupported(): bool
158
    {
159
        if (DIRECTORY_SEPARATOR === '\\') {
160
            return function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT);
161
        } else {
162
            return strpos(getenv('TERM'), '256color') !== false;
163
        }
164
    }
165
166
    /**
167
     * @param string|int $value
168
     * @return string
169
     */
170
    private function escSequence(string $value): string
171
    {
172
        return "\033[{$value}m";
173
    }
174
175
    /**
176
     * @param bool $forceStyle
177
     */
178
    public function setForceStyle(bool $forceStyle)
179
    {
180
        $this->forceStyle = (bool)$forceStyle;
181
    }
182
183
    /**
184
     * @return array
185
     */
186
    public function getPossibleStyles(): array
187
    {
188
        return array_keys($this->styles);
189
    }
190
}