Completed
Push — master ( 2b1385...7c6a84 )
by Thomas
07:21
created

OutputFormatter::format()   D

Complexity

Conditions 10
Paths 14

Size

Total Lines 42
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 42
rs 4.8197
cc 10
eloc 27
nc 14
nop 1

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
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[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
namespace Symfony\Component\Console\Formatter;
13
14
/**
15
 * Formatter class for console output.
16
 *
17
 * @author Konstantin Kudryashov <[email protected]>
18
 */
19
class OutputFormatter implements OutputFormatterInterface
20
{
21
    private $decorated;
22
    private $styles = array();
23
    private $styleStack;
24
25
    /**
26
     * Escapes "<" special char in given text.
27
     *
28
     * @param string $text Text to escape
29
     *
30
     * @return string Escaped text
31
     */
32
    public static function escape($text)
33
    {
34
        return preg_replace('/([^\\\\]?)</', '$1\\<', $text);
35
    }
36
37
    /**
38
     * Initializes console output formatter.
39
     *
40
     * @param bool                            $decorated Whether this formatter should actually decorate strings
41
     * @param OutputFormatterStyleInterface[] $styles    Array of "name => FormatterStyle" instances
42
     */
43
    public function __construct($decorated = false, array $styles = array())
44
    {
45
        $this->decorated = (bool) $decorated;
46
47
        $this->setStyle('error', new OutputFormatterStyle('white', 'red'));
48
        $this->setStyle('info', new OutputFormatterStyle('green'));
49
        $this->setStyle('comment', new OutputFormatterStyle('yellow'));
50
        $this->setStyle('question', new OutputFormatterStyle('black', 'cyan'));
51
52
        foreach ($styles as $name => $style) {
53
            $this->setStyle($name, $style);
54
        }
55
56
        $this->styleStack = new OutputFormatterStyleStack();
57
    }
58
59
    /**
60
     * Sets the decorated flag.
61
     *
62
     * @param bool $decorated Whether to decorate the messages or not
63
     */
64
    public function setDecorated($decorated)
65
    {
66
        $this->decorated = (bool) $decorated;
67
    }
68
69
    /**
70
     * Gets the decorated flag.
71
     *
72
     * @return bool true if the output will decorate messages, false otherwise
73
     */
74
    public function isDecorated()
75
    {
76
        return $this->decorated;
77
    }
78
79
    /**
80
     * Sets a new style.
81
     *
82
     * @param string                        $name  The style name
83
     * @param OutputFormatterStyleInterface $style The style instance
84
     */
85
    public function setStyle($name, OutputFormatterStyleInterface $style)
86
    {
87
        $this->styles[strtolower($name)] = $style;
88
    }
89
90
    /**
91
     * Checks if output formatter has style with specified name.
92
     *
93
     * @param string $name
94
     *
95
     * @return bool
96
     */
97
    public function hasStyle($name)
98
    {
99
        return isset($this->styles[strtolower($name)]);
100
    }
101
102
    /**
103
     * Gets style options from style with specified name.
104
     *
105
     * @param string $name
106
     *
107
     * @return OutputFormatterStyleInterface
108
     *
109
     * @throws \InvalidArgumentException When style isn't defined
110
     */
111
    public function getStyle($name)
112
    {
113
        if (!$this->hasStyle($name)) {
114
            throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name));
115
        }
116
117
        return $this->styles[strtolower($name)];
118
    }
119
120
    /**
121
     * Formats a message according to the given styles.
122
     *
123
     * @param string $message The message to style
124
     *
125
     * @return string The styled message
126
     */
127
    public function format($message)
128
    {
129
        $message = (string) $message;
130
        $offset = 0;
131
        $output = '';
132
        $tagRegex = '[a-z][a-z0-9_=;-]*';
133
        preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
134
        foreach ($matches[0] as $i => $match) {
135
            $pos = $match[1];
136
            $text = $match[0];
137
138
            if (0 != $pos && '\\' == $message[$pos - 1]) {
139
                continue;
140
            }
141
142
            // add the text up to the next tag
143
            $output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
144
            $offset = $pos + strlen($text);
145
146
            // opening tag?
147
            if ($open = '/' != $text[1]) {
148
                $tag = $matches[1][$i][0];
149
            } else {
150
                $tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
151
            }
152
153
            if (!$open && !$tag) {
154
                // </>
155
                $this->styleStack->pop();
156
            } elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
157
                $output .= $this->applyCurrentStyle($text);
158
            } elseif ($open) {
159
                $this->styleStack->push($style);
160
            } else {
161
                $this->styleStack->pop($style);
162
            }
163
        }
164
165
        $output .= $this->applyCurrentStyle(substr($message, $offset));
166
167
        return str_replace('\\<', '<', $output);
168
    }
169
170
    /**
171
     * @return OutputFormatterStyleStack
172
     */
173
    public function getStyleStack()
174
    {
175
        return $this->styleStack;
176
    }
177
178
    /**
179
     * Tries to create new style instance from string.
180
     *
181
     * @param string $string
182
     *
183
     * @return OutputFormatterStyle|bool false if string is not format string
184
     */
185
    private function createStyleFromString($string)
186
    {
187
        if (isset($this->styles[$string])) {
188
            return $this->styles[$string];
189
        }
190
191
        if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
192
            return false;
193
        }
194
195
        $style = new OutputFormatterStyle();
196
        foreach ($matches as $match) {
197
            array_shift($match);
198
199
            if ('fg' == $match[0]) {
200
                $style->setForeground($match[1]);
201
            } elseif ('bg' == $match[0]) {
202
                $style->setBackground($match[1]);
203
            } else {
204
                try {
205
                    $style->setOption($match[1]);
206
                } catch (\InvalidArgumentException $e) {
207
                    return false;
208
                }
209
            }
210
        }
211
212
        return $style;
213
    }
214
215
    /**
216
     * Applies current style from stack to text, if must be applied.
217
     *
218
     * @param string $text Input text
219
     *
220
     * @return string Styled text
221
     */
222
    private function applyCurrentStyle($text)
223
    {
224
        return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
225
    }
226
}
227