Style::fromString()   B
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
c 0
b 0
f 0
rs 8.439
cc 5
eloc 14
nc 5
nop 2
1
<?php namespace nyx\console\output\formatting;
2
3
/**
4
 * Output Formatting Style
5
 *
6
 * Used internally by the Output Formatter to apply the respective styles onto text within style tags,
7
 * but it can also be used to manually stylize strings.
8
 *
9
 * Note: The class does not check if the given strings already contain any sort of color codes or other escape
10
 * sequences. Applying a style onto an already decorated string can therefore yield unexpected results.
11
 *
12
 * @version     0.1.0
13
 * @author      Michal Chojnacki <[email protected]>
14
 * @copyright   2012-2017 Nyx Dev Team
15
 * @link        https://github.com/unyx/nyx
16
 */
17
class Style implements interfaces\Style
18
{
19
    /**
20
     * @var array   The available foreground colors. The actual color representations may vary from the names as it
21
     *              depends on the terminal used to display them.
22
     */
23
    protected static $foregrounds = [
24
        'black'   => 30,
25
        'red'     => 31,
26
        'green'   => 32,
27
        'yellow'  => 33,
28
        'blue'    => 34,
29
        'magenta' => 35,
30
        'cyan'    => 36,
31
        'white'   => 37,
32
        'default' => 39
33
    ];
34
35
    /**
36
     * @var array   The available background colors.
37
     */
38
    protected static $backgrounds = [
39
        'black'   => 40,
40
        'red'     => 41,
41
        'green'   => 42,
42
        'yellow'  => 43,
43
        'blue'    => 44,
44
        'magenta' => 45,
45
        'cyan'    => 46,
46
        'white'   => 47,
47
        'default' => 49
48
    ];
49
50
    /**
51
     * @var array   The available additional emphasis options (Note: support for those depends on the type
52
     *              of the terminal used to display the application).
53
     */
54
    protected static $options = [
55
        'bold'       => [1, 22],
56
        'italic'     => [3, 23],
57
        'underscore' => [4, 24],
58
        'blink'      => [5, 25],
59
        'reverse'    => [7, 27],
60
        'conceal'    => [8, 28]
61
    ];
62
63
    /**
64
     * @var int     The currently set foreground color of this Style.
65
     */
66
    private $foreground;
67
68
    /**
69
     * @var int     The currently set background color of this Style.
70
     */
71
    private $background;
72
73
    /**
74
     * @var array   The currently set additional emphasis options of this Style.
75
     */
76
    private $emphasis = [];
77
78
    /**
79
     * Factory which attempts to create a new Style based on an inline styling syntax.
80
     *
81
     * @param   string  $string         The string to parse.
82
     * @param   array   $properties     An array containing at most 2 keys: 'foreground' and 'background',
83
     *                                  denoting the names those properties will be sought for to in the string.
84
     *                                  Defaults to: ['foreground' => 'color', 'background' => 'bg'].
85
     * @return  Style                   The Style that was created based on it.
86
     */
87
    public static function fromString(string $string, array $properties = null) : ?Style
88
    {
89
        if (!preg_match_all('/([^:]+):([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
90
            return null;
91
        }
92
93
        // H-okay, seems we got a match.
94
        $style = new Style(null);
95
96
        foreach ($matches as $match) {
97
98
            // Be forgiving regarding whitespaces (or lack thereof) in the string.
99
            $property = trim($match[1]);
100
            $value    = trim($match[2]);
101
102
            if ($property === ($properties['foreground'] ?? 'color')) {
103
                $style->setForeground($value);
104
            } elseif ($property === ($properties['background'] ?? 'bg')) {
105
                $style->setBackground($value);
106
            } else {
107
                $style->setEmphasis([$value]);
108
            }
109
        }
110
111
        return $style;
112
    }
113
114
    /**
115
     * Constructs a new Output Formatting Style.
116
     *
117
     * @param   string  $foreground     The foreground color to be set.
118
     * @param   string  $background     The background color to be set.
119
     * @param   array   $options        An array of additional options emphasis to be set.
120
     */
121
    public function __construct(?string $foreground, string $background = null, array $options = null)
122
    {
123
        if (isset($foreground)) {
124
            $this->setForeground($foreground);
125
        }
126
127
        if (isset($background)) {
128
            $this->setBackground($background);
129
        }
130
131
        if (isset($options)) {
132
            $this->setEmphasis($options);
133
        }
134
    }
135
136
    /**
137
     * {@inheritDoc}
138
     */
139
    public function setForeground(?string $color) : interfaces\Style
140
    {
141
        return $this->setColor('foreground', $color);
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147
    public function setBackground(?string $color) : interfaces\Style
148
    {
149
        return $this->setColor('background', $color);
150
    }
151
152
    /**
153
     * {@inheritdoc}
154
     */
155
    public function setEmphasis(array $options) : interfaces\Style
156
    {
157
        foreach ($options as $option) {
158
            if (!isset(static::$options[$option])) {
159
                throw new \InvalidArgumentException("The emphasis option [$option] is not recognized.");
160
            }
161
162
            // Only add the option when it is not already set.
163
            if (false === array_search($option, $this->emphasis)) {
164
                $this->emphasis[] = $option;
165
            }
166
        }
167
168
        return $this;
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function apply(string $text) : string
175
    {
176
        $set   = [];
177
        $unset = [];
178
179
        if (isset($this->foreground)) {
180
            $set[]   = static::$foregrounds[$this->foreground];
181
            $unset[] = 39;
182
        }
183
184
        if (isset($this->background)) {
185
            $set[]   = static::$backgrounds[$this->background];
186
            $unset[] = 49;
187
        }
188
189
        if (!empty($this->emphasis)) {
190
            foreach ($this->emphasis as $option) {
191
                $set[]   = static::$options[$option][0];
192
                $unset[] = static::$options[$option][1];
193
            }
194
        }
195
196
        // Return the text wrapped by the appropriate escape sequences.
197
        return !empty($set)
198
            ? sprintf("\e[%sm%s\e[%sm", implode(';', $set), $text, implode(';', $unset))
199
            : $text;
200
    }
201
202
    /**
203
     * Sets a specific type of color on this Style.
204
     *
205
     * @param   string  $type   The type of the color to set.
206
     * @param   string  $color  The color to set.
207
     * @return  $this
208
     */
209
    protected function setColor(string $type, ?string $color) : interfaces\Style
210
    {
211
        if (isset($color) && !isset(static::${$type.'s'}[$color])) {
212
            throw new \InvalidArgumentException("The $type color [$color] is not recognized.");
213
        }
214
215
        $this->$type = $color;
216
217
        return $this;
218
    }
219
}
220