Completed
Push — develop ( e1f81f...539a89 )
by Adrien
16:11
created

FormulaParser   D

Complexity

Total Complexity 109

Size/Duplication

Total Lines 570
Duplicated Lines 14.56 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 83
loc 570
rs 4.8717
c 0
b 0
f 0
wmc 109
lcom 1
cbo 2

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 2
A getFormula() 0 4 1
A getToken() 0 8 2
A getTokenCount() 0 4 1
A getTokens() 0 4 1
F parseToTokens() 83 467 102

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like FormulaParser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FormulaParser, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpSpreadsheet\Calculation;
4
5
/**
6
 * Copyright (c) 2006 - 2016 PhpSpreadsheet
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
 *
22
 * @category   PhpSpreadsheet
23
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
24
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
25
 * @version    ##VERSION##, ##DATE##
26
 */
27
/*
28
PARTLY BASED ON:
29
    Copyright (c) 2007 E. W. Bachtal, Inc.
30
31
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
32
    and associated documentation files (the "Software"), to deal in the Software without restriction,
33
    including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
34
    and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
35
    subject to the following conditions:
36
37
      The above copyright notice and this permission notice shall be included in all copies or substantial
38
      portions of the Software.
39
40
    The software is provided "as is", without warranty of any kind, express or implied, including but not
41
    limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In
42
    no event shall the authors or copyright holders be liable for any claim, damages or other liability,
43
    whether in an action of contract, tort or otherwise, arising from, out of or in connection with the
44
    software or the use or other dealings in the software.
45
46
    http://ewbi.blogs.com/develops/2007/03/excel_formula_p.html
47
    http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
48
*/
49
class FormulaParser
50
{
51
    /* Character constants */
52
    const QUOTE_DOUBLE  = '"';
53
    const QUOTE_SINGLE  = '\'';
54
    const BRACKET_CLOSE = ']';
55
    const BRACKET_OPEN  = '[';
56
    const BRACE_OPEN    = '{';
57
    const BRACE_CLOSE   = '}';
58
    const PAREN_OPEN    = '(';
59
    const PAREN_CLOSE   = ')';
60
    const SEMICOLON     = ';';
61
    const WHITESPACE    = ' ';
62
    const COMMA         = ',';
63
    const ERROR_START   = '#';
64
65
    const OPERATORS_SN             = "+-";
66
    const OPERATORS_INFIX         = "+-*/^&=><";
67
    const OPERATORS_POSTFIX     = "%";
68
69
    /**
70
     * Formula
71
     *
72
     * @var string
73
     */
74
    private $formula;
75
76
    /**
77
     * Tokens
78
     *
79
     * @var FormulaToken[]
80
     */
81
    private $tokens = array();
82
83
    /**
84
     * Create a new FormulaParser
85
     *
86
     * @param     string        $pFormula    Formula to parse
87
     * @throws     Exception
88
     */
89
    public function __construct($pFormula = '')
90
    {
91
        // Check parameters
92
        if (is_null($pFormula)) {
93
            throw new Exception("Invalid parameter passed: formula");
94
        }
95
96
        // Initialise values
97
        $this->formula = trim($pFormula);
98
        // Parse!
99
        $this->parseToTokens();
100
    }
101
102
    /**
103
     * Get Formula
104
     *
105
     * @return string
106
     */
107
    public function getFormula()
108
    {
109
        return $this->formula;
110
    }
111
112
    /**
113
     * Get Token
114
     *
115
     * @param     int        $pId    Token id
116
     * @return    string
117
     * @throws  Exception
118
     */
119
    public function getToken($pId = 0)
120
    {
121
        if (isset($this->tokens[$pId])) {
122
            return $this->tokens[$pId];
123
        } else {
124
            throw new Exception("Token with id $pId does not exist.");
125
        }
126
    }
127
128
    /**
129
     * Get Token count
130
     *
131
     * @return string
132
     */
133
    public function getTokenCount()
134
    {
135
        return count($this->tokens);
136
    }
137
138
    /**
139
     * Get Tokens
140
     *
141
     * @return FormulaToken[]
142
     */
143
    public function getTokens()
144
    {
145
        return $this->tokens;
146
    }
147
148
    /**
149
     * Parse to tokens
150
     */
151
    private function parseToTokens()
152
    {
153
        // No attempt is made to verify formulas; assumes formulas are derived from Excel, where
154
        // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions.
155
156
        // Check if the formula has a valid starting =
157
        $formulaLength = strlen($this->formula);
158
        if ($formulaLength < 2 || $this->formula{0} != '=') {
159
            return;
160
        }
161
162
        // Helper variables
163
        $tokens1    = $tokens2     = $stack = array();
164
        $inString    = $inPath     = $inRange     = $inError = false;
165
        $token        = $previousToken    = $nextToken    = null;
0 ignored issues
show
Unused Code introduced by
$nextToken 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...
Unused Code introduced by
$previousToken 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...
Unused Code introduced by
$token 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...
166
167
        $index    = 1;
168
        $value    = '';
169
170
        $ERRORS             = array("#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A");
171
        $COMPARATORS_MULTI     = array(">=", "<=", "<>");
172
173
        while ($index < $formulaLength) {
174
            // state-dependent character evaluation (order is important)
175
176
            // double-quoted strings
177
            // embeds are doubled
178
            // end marks token
179
            if ($inString) {
180
                if ($this->formula{$index} == self::QUOTE_DOUBLE) {
181
                    if ((($index + 2) <= $formulaLength) && ($this->formula{$index + 1} == self::QUOTE_DOUBLE)) {
182
                        $value .= self::QUOTE_DOUBLE;
183
                        ++$index;
184
                    } else {
185
                        $inString = false;
186
                        $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND, FormulaToken::TOKEN_SUBTYPE_TEXT);
187
                        $value = "";
188
                    }
189
                } else {
190
                    $value .= $this->formula{$index};
191
                }
192
                ++$index;
193
                continue;
194
            }
195
196
            // single-quoted strings (links)
197
            // embeds are double
198
            // end does not mark a token
199
            if ($inPath) {
200
                if ($this->formula{$index} == self::QUOTE_SINGLE) {
201
                    if ((($index + 2) <= $formulaLength) && ($this->formula{$index + 1} == self::QUOTE_SINGLE)) {
202
                        $value .= self::QUOTE_SINGLE;
203
                        ++$index;
204
                    } else {
205
                        $inPath = false;
206
                    }
207
                } else {
208
                    $value .= $this->formula{$index};
209
                }
210
                ++$index;
211
                continue;
212
            }
213
214
            // bracked strings (R1C1 range index or linked workbook name)
215
            // no embeds (changed to "()" by Excel)
216
            // end does not mark a token
217
            if ($inRange) {
218
                if ($this->formula{$index} == self::BRACKET_CLOSE) {
219
                    $inRange = false;
220
                }
221
                $value .= $this->formula{$index};
222
                ++$index;
223
                continue;
224
            }
225
226
            // error values
227
            // end marks a token, determined from absolute list of values
228
            if ($inError) {
229
                $value .= $this->formula{$index};
230
                ++$index;
231
                if (in_array($value, $ERRORS)) {
232
                    $inError = false;
233
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND, FormulaToken::TOKEN_SUBTYPE_ERROR);
234
                    $value = "";
235
                }
236
                continue;
237
            }
238
239
            // scientific notation check
240
            if (strpos(self::OPERATORS_SN, $this->formula{$index}) !== false) {
241
                if (strlen($value) > 1) {
242
                    if (preg_match("/^[1-9]{1}(\.[0-9]+)?E{1}$/", $this->formula{$index}) != 0) {
243
                        $value .= $this->formula{$index};
244
                        ++$index;
245
                        continue;
246
                    }
247
                }
248
            }
249
250
            // independent character evaluation (order not important)
251
252
            // establish state-dependent character evaluations
253 View Code Duplication
            if ($this->formula{$index} == self::QUOTE_DOUBLE) {
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...
254
                if (strlen($value > 0)) {
255
                    // unexpected
256
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
257
                    $value = "";
258
                }
259
                $inString = true;
260
                ++$index;
261
                continue;
262
            }
263
264 View Code Duplication
            if ($this->formula{$index} == self::QUOTE_SINGLE) {
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...
265
                if (strlen($value) > 0) {
266
                    // unexpected
267
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
268
                    $value = "";
269
                }
270
                $inPath = true;
271
                ++$index;
272
                continue;
273
            }
274
275
            if ($this->formula{$index} == self::BRACKET_OPEN) {
276
                $inRange = true;
277
                $value .= self::BRACKET_OPEN;
278
                ++$index;
279
                continue;
280
            }
281
282 View Code Duplication
            if ($this->formula{$index} == self::ERROR_START) {
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...
283
                if (strlen($value) > 0) {
284
                    // unexpected
285
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
286
                    $value = "";
287
                }
288
                $inError = true;
289
                $value .= self::ERROR_START;
290
                ++$index;
291
                continue;
292
            }
293
294
            // mark start and end of arrays and array rows
295
            if ($this->formula{$index} == self::BRACE_OPEN) {
296
                if (strlen($value) > 0) {
297
                    // unexpected
298
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
299
                    $value = "";
300
                }
301
302
                $tmp = new FormulaToken("ARRAY", FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
303
                $tokens1[] = $tmp;
304
                $stack[] = clone $tmp;
305
306
                $tmp = new FormulaToken("ARRAYROW", FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
307
                $tokens1[] = $tmp;
308
                $stack[] = clone $tmp;
309
310
                ++$index;
311
                continue;
312
            }
313
314
            if ($this->formula{$index} == self::SEMICOLON) {
315
                if (strlen($value) > 0) {
316
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
317
                    $value = "";
318
                }
319
320
                $tmp = array_pop($stack);
321
                $tmp->setValue("");
322
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
323
                $tokens1[] = $tmp;
324
325
                $tmp = new FormulaToken(",", FormulaToken::TOKEN_TYPE_ARGUMENT);
326
                $tokens1[] = $tmp;
327
328
                $tmp = new FormulaToken("ARRAYROW", FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
329
                $tokens1[] = $tmp;
330
                $stack[] = clone $tmp;
331
332
                ++$index;
333
                continue;
334
            }
335
336
            if ($this->formula{$index} == self::BRACE_CLOSE) {
337
                if (strlen($value) > 0) {
338
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
339
                    $value = "";
340
                }
341
342
                $tmp = array_pop($stack);
343
                $tmp->setValue("");
344
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
345
                $tokens1[] = $tmp;
346
347
                $tmp = array_pop($stack);
348
                $tmp->setValue("");
349
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
350
                $tokens1[] = $tmp;
351
352
                ++$index;
353
                continue;
354
            }
355
356
            // trim white-space
357
            if ($this->formula{$index} == self::WHITESPACE) {
358
                if (strlen($value) > 0) {
359
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
360
                    $value = "";
361
                }
362
                $tokens1[] = new FormulaToken("", FormulaToken::TOKEN_TYPE_WHITESPACE);
363
                ++$index;
364
                while (($this->formula{$index} == self::WHITESPACE) && ($index < $formulaLength)) {
365
                    ++$index;
366
                }
367
                continue;
368
            }
369
370
            // multi-character comparators
371
            if (($index + 2) <= $formulaLength) {
372
                if (in_array(substr($this->formula, $index, 2), $COMPARATORS_MULTI)) {
373
                    if (strlen($value) > 0) {
374
                        $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
375
                        $value = "";
376
                    }
377
                    $tokens1[] = new FormulaToken(substr($this->formula, $index, 2), FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_LOGICAL);
378
                    $index += 2;
379
                    continue;
380
                }
381
            }
382
383
            // standard infix operators
384 View Code Duplication
            if (strpos(self::OPERATORS_INFIX, $this->formula{$index}) !== false) {
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...
385
                if (strlen($value) > 0) {
386
                    $tokens1[] =new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
387
                    $value = "";
388
                }
389
                $tokens1[] = new FormulaToken($this->formula{$index}, FormulaToken::TOKEN_TYPE_OPERATORINFIX);
390
                ++$index;
391
                continue;
392
            }
393
394
            // standard postfix operators (only one)
395 View Code Duplication
            if (strpos(self::OPERATORS_POSTFIX, $this->formula{$index}) !== false) {
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...
396
                if (strlen($value) > 0) {
397
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
398
                    $value = "";
399
                }
400
                $tokens1[] = new FormulaToken($this->formula{$index}, FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX);
401
                ++$index;
402
                continue;
403
            }
404
405
            // start subexpression or function
406
            if ($this->formula{$index} == self::PAREN_OPEN) {
407
                if (strlen($value) > 0) {
408
                    $tmp = new FormulaToken($value, FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
409
                    $tokens1[] = $tmp;
410
                    $stack[] = clone $tmp;
411
                    $value = "";
412
                } else {
413
                    $tmp = new FormulaToken("", FormulaToken::TOKEN_TYPE_SUBEXPRESSION, FormulaToken::TOKEN_SUBTYPE_START);
414
                    $tokens1[] = $tmp;
415
                    $stack[] = clone $tmp;
416
                }
417
                ++$index;
418
                continue;
419
            }
420
421
            // function, subexpression, or array parameters, or operand unions
422
            if ($this->formula{$index} == self::COMMA) {
423
                if (strlen($value) > 0) {
424
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
425
                    $value = "";
426
                }
427
428
                $tmp = array_pop($stack);
429
                $tmp->setValue("");
430
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
431
                $stack[] = $tmp;
432
433
                if ($tmp->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
434
                    $tokens1[] = new FormulaToken(",", FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_UNION);
435
                } else {
436
                    $tokens1[] = new FormulaToken(",", FormulaToken::TOKEN_TYPE_ARGUMENT);
437
                }
438
                ++$index;
439
                continue;
440
            }
441
442
            // stop subexpression
443
            if ($this->formula{$index} == self::PAREN_CLOSE) {
444
                if (strlen($value) > 0) {
445
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
446
                    $value = "";
447
                }
448
449
                $tmp = array_pop($stack);
450
                $tmp->setValue("");
451
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
452
                $tokens1[] = $tmp;
453
454
                ++$index;
455
                continue;
456
            }
457
458
            // token accumulation
459
            $value .= $this->formula{$index};
460
            ++$index;
461
        }
462
463
        // dump remaining accumulation
464
        if (strlen($value) > 0) {
465
            $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
466
        }
467
468
        // move tokenList to new set, excluding unnecessary white-space tokens and converting necessary ones to intersections
469
        $tokenCount = count($tokens1);
470
        for ($i = 0; $i < $tokenCount; ++$i) {
471
            $token = $tokens1[$i];
472
            if (isset($tokens1[$i - 1])) {
473
                $previousToken = $tokens1[$i - 1];
474
            } else {
475
                $previousToken = null;
476
            }
477
            if (isset($tokens1[$i + 1])) {
478
                $nextToken = $tokens1[$i + 1];
479
            } else {
480
                $nextToken = null;
481
            }
482
483
            if (is_null($token)) {
484
                continue;
485
            }
486
487
            if ($token->getTokenType() != FormulaToken::TOKEN_TYPE_WHITESPACE) {
488
                $tokens2[] = $token;
489
                continue;
490
            }
491
492
            if (is_null($previousToken)) {
493
                continue;
494
            }
495
496 View Code Duplication
            if (! (
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...
497
                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
498
                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
499
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
500
                  ) ) {
501
                continue;
502
            }
503
504
            if (is_null($nextToken)) {
505
                continue;
506
            }
507
508 View Code Duplication
            if (! (
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...
509
                    (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
510
                    (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
511
                    ($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
512
                  ) ) {
513
                continue;
514
            }
515
516
            $tokens2[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_INTERSECTION);
517
        }
518
519
        // move tokens to final list, switching infix "-" operators to prefix when appropriate, switching infix "+" operators
520
        // to noop when appropriate, identifying operand and infix-operator subtypes, and pulling "@" from function names
521
        $this->tokens = array();
522
523
        $tokenCount = count($tokens2);
524
        for ($i = 0; $i < $tokenCount; ++$i) {
525
            $token = $tokens2[$i];
526
            if (isset($tokens2[$i - 1])) {
527
                $previousToken = $tokens2[$i - 1];
528
            } else {
529
                $previousToken = null;
530
            }
531
            if (isset($tokens2[$i + 1])) {
532
                $nextToken = $tokens2[$i + 1];
0 ignored issues
show
Unused Code introduced by
$nextToken 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...
533
            } else {
534
                $nextToken = null;
0 ignored issues
show
Unused Code introduced by
$nextToken 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...
535
            }
536
537
            if (is_null($token)) {
538
                continue;
539
            }
540
541
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == "-") {
542
                if ($i == 0) {
543
                    $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
544 View Code Duplication
                } elseif ((($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
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...
545
                    ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
546
                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
547
                    ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
548
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
549
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)) {
550
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
551
                } else {
552
                    $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
553
                }
554
555
                $this->tokens[] = $token;
556
                continue;
557
            }
558
559
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == "+") {
560 View Code Duplication
                if ($i == 0) {
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...
561
                    continue;
562
                } elseif ((($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
563
                    ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
564
                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
565
                    ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
566
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
567
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)) {
568
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
569
                } else {
570
                    continue;
571
                }
572
573
                $this->tokens[] = $token;
574
                continue;
575
            }
576
577
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX &&
578
                $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING) {
579
                if (strpos("<>=", substr($token->getValue(), 0, 1)) !== false) {
580
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
581
                } elseif ($token->getValue() == "&") {
582
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
583
                } else {
584
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
585
                }
586
587
                $this->tokens[] = $token;
588
                continue;
589
            }
590
591
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND &&
592
                $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING) {
593
                if (!is_numeric($token->getValue())) {
594
                    if (strtoupper($token->getValue()) == "TRUE" || strtoupper($token->getValue() == "FALSE")) {
595
                        $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
596
                    } else {
597
                        $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_RANGE);
598
                    }
599
                } else {
600
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_NUMBER);
601
                }
602
603
                $this->tokens[] = $token;
604
                continue;
605
            }
606
607
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
608
                if (strlen($token->getValue() > 0)) {
609
                    if (substr($token->getValue(), 0, 1) == "@") {
610
                        $token->setValue(substr($token->getValue(), 1));
611
                    }
612
                }
613
            }
614
615
            $this->tokens[] = $token;
616
        }
617
    }
618
}
619