Completed
Push — master ( 93176b...dc92f6 )
by Oscar
01:21
created

JsFunctionsScanner::convertString()   B

Complexity

Conditions 10
Paths 2

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 2
nop 1
dl 0
loc 31
rs 7.6666
c 0
b 0
f 0

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
namespace Gettext\Utils;
4
5
class JsFunctionsScanner extends FunctionsScanner
6
{
7
    protected $code;
8
    protected $status = [];
9
10
    /**
11
     * Constructor.
12
     *
13
     * @param string $code The php code to scan
14
     */
15
    public function __construct($code)
16
    {
17
        // Normalize newline characters
18
        $this->code = str_replace(["\r\n", "\n\r", "\r"], "\n", $code);
19
    }
20
21
    /**
22
     * {@inheritdoc}
23
     */
24
    public function getFunctions(array $constants = [])
25
    {
26
        $length = strlen($this->code);
27
        $line = 1;
28
        $buffer = '';
29
        $functions = [];
30
        $bufferFunctions = [];
31
        $char = null;
32
33
        for ($pos = 0; $pos < $length; ++$pos) {
34
            $prev = $char;
35
            $char = $this->code[$pos];
36
            $next = isset($this->code[$pos + 1]) ? $this->code[$pos + 1] : null;
37
38
            switch ($char) {
39
                case '\\':
40
                    switch ($this->status()) {
41
                        case 'simple-quote':
42
                            if ($next !== "'") {
43
                                break 2;
44
                            }
45
                            break;
46
47
                        case 'double-quote':
48
                            if ($next !== '"') {
49
                                break 2;
50
                            }
51
                            break;
52
                    }
53
                    
54
                    $prev = $char;
0 ignored issues
show
Unused Code introduced by
$prev is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
55
                    $char = $next;
56
                    $pos++;
57
                    $next = isset($this->code[$pos]) ? $this->code[$pos] : null;
0 ignored issues
show
Unused Code introduced by
$next is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
58
                    break;
59
60
                case "\n":
61
                    ++$line;
62
63
                    if ($this->status('line-comment')) {
64
                        $this->upStatus();
65
                    }
66
                    break;
67
68
                case '/':
69
                    switch ($this->status()) {
70
                        case 'simple-quote':
71
                        case 'double-quote':
72
                        case 'line-comment':
73
                            break;
74
75
                        case 'block-comment':
76
                            if ($prev === '*') {
77
                                $this->upStatus();
78
                            }
79
                            break;
80
81
                        default:
82
                            if ($next === '/') {
83
                                $this->downStatus('line-comment');
84
                            } elseif ($next === '*') {
85
                                $this->downStatus('block-comment');
86
                            }
87
                            break;
88
                    }
89
                    break;
90
91 View Code Duplication
                case "'":
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...
92
                    switch ($this->status()) {
93
                        case 'simple-quote':
94
                            $this->upStatus();
95
                            break;
96
97
                        case 'line-comment':
98
                        case 'block-comment':
99
                        case 'double-quote':
100
                            break;
101
102
                        default:
103
                            $this->downStatus('simple-quote');
104
                            break;
105
                    }
106
                    break;
107
108 View Code Duplication
                case '"':
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...
109
                    switch ($this->status()) {
110
                        case 'double-quote':
111
                            $this->upStatus();
112
                            break;
113
114
                        case 'line-comment':
115
                        case 'block-comment':
116
                        case 'simple-quote':
117
                            break;
118
119
                        default:
120
                            $this->downStatus('double-quote');
121
                            break;
122
                    }
123
                    break;
124
125
                case '(':
126
                    switch ($this->status()) {
127
                        case 'simple-quote':
128
                        case 'double-quote':
129
                        case 'line-comment':
130
                        case 'block-comment':
131
                        case 'line-comment':
132
                            break;
133
134
                        default:
135
                            if ($buffer && preg_match('/(\w+)$/', $buffer, $matches)) {
136
                                $this->downStatus('function');
137
                                array_unshift($bufferFunctions, [$matches[1], $line, []]);
138
                                $buffer = '';
139
                                continue 3;
140
                            }
141
                            break;
142
                    }
143
                    break;
144
145
                case ')':
146
                    switch ($this->status()) {
147
                        case 'function':
148
                            if (($argument = self::prepareArgument($buffer))) {
149
                                $bufferFunctions[0][2][] = $argument;
150
                            }
151
152
                            if (!empty($bufferFunctions)) {
153
                                $functions[] = array_shift($bufferFunctions);
154
                            }
155
156
                            $this->upStatus();
157
                            $buffer = '';
158
                            continue 3;
159
                    }
160
                    break;
161
162
                case ',':
163
                    switch ($this->status()) {
164
                        case 'function':
165
                            if (($argument = self::prepareArgument($buffer))) {
166
                                $bufferFunctions[0][2][] = $argument;
167
                            }
168
169
                            $buffer = '';
170
                            continue 3;
171
                    }
172
                    break;
173
174
                case ' ':
175
                case '\t':
176
                    switch ($this->status()) {
177
                        case 'double-quote':
178
                        case 'simple-quote':
179
                            break;
180
181
                        default:
182
                            $buffer = '';
183
                            continue 3;
184
                    }
185
                    break;
186
            }
187
188
            switch ($this->status()) {
189
                case 'line-comment':
190
                case 'block-comment':
191
                    break;
192
193
                default:
194
                    $buffer .= $char;
195
                    break;
196
            }
197
        }
198
199
        return $functions;
200
    }
201
202
    /**
203
     * Get the current context of the scan.
204
     *
205
     * @param null|string $match To check whether the current status is this value
206
     *
207
     * @return string|bool
208
     */
209
    protected function status($match = null)
210
    {
211
        $status = isset($this->status[0]) ? $this->status[0] : null;
212
213
        if ($match !== null) {
214
            return $status === $match;
215
        }
216
217
        return $status;
218
    }
219
220
    /**
221
     * Add a new status to the stack.
222
     *
223
     * @param string $status
224
     */
225
    protected function downStatus($status)
226
    {
227
        array_unshift($this->status, $status);
228
    }
229
230
    /**
231
     * Removes and return the current status.
232
     *
233
     * @return string|null
234
     */
235
    protected function upStatus()
236
    {
237
        return array_shift($this->status);
238
    }
239
240
    /**
241
     * Prepares the arguments found in functions.
242
     *
243
     * @param string $argument
244
     *
245
     * @return string
246
     */
247
    protected static function prepareArgument($argument)
248
    {
249
        if ($argument && ($argument[0] === '"' || $argument[0] === "'")) {
250
            return static::convertString(substr($argument, 1, -1));
251
        }
252
    }
253
254
    /**
255
     * Decodes a string with an argument.
256
     *
257
     * @param string $value
258
     *
259
     * @return string
260
     */
261
    protected static function convertString($value)
262
    {
263
        if (strpos($value, '\\') === false) {
264
            return $value;
265
        }
266
267
        return preg_replace_callback(
268
            '/\\\(n|r|t|v|e|f|"|\\\)/',
269
            function ($match) {
270
                switch ($match[1][0]) {
271
                    case 'n':
272
                        return "\n";
273
                    case 'r':
274
                        return "\r";
275
                    case 't':
276
                        return "\t";
277
                    case 'v':
278
                        return "\v";
279
                    case 'e':
280
                        return "\e";
281
                    case 'f':
282
                        return "\f";
283
                    case '"':
284
                        return '"';
285
                    case '\\':
286
                        return '\\';
287
                }
288
            },
289
            $value
290
        );
291
    }
292
}
293