Test Failed
Push — develop ( 90366f...812a46 )
by Adrien
28:16
created

FormulaParser::getFormula()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
/*
6
PARTLY BASED ON:
7
    Copyright (c) 2007 E. W. Bachtal, Inc.
8
9
    Permission is hereby granted, free of charge, to any person obtaining a copy of this software
10
    and associated documentation files (the "Software"), to deal in the Software without restriction,
11
    including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
    and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
13
    subject to the following conditions:
14
15
      The above copyright notice and this permission notice shall be included in all copies or substantial
16
      portions of the Software.
17
18
    The software is provided "as is", without warranty of any kind, express or implied, including but not
19
    limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In
20
    no event shall the authors or copyright holders be liable for any claim, damages or other liability,
21
    whether in an action of contract, tort or otherwise, arising from, out of or in connection with the
22
    software or the use or other dealings in the software.
23
24
    http://ewbi.blogs.com/develops/2007/03/excel_formula_p.html
25
    http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
26
*/
27
class FormulaParser
28
{
29
    // Character constants
30
    const QUOTE_DOUBLE = '"';
31
    const QUOTE_SINGLE = '\'';
32
    const BRACKET_CLOSE = ']';
33
    const BRACKET_OPEN = '[';
34
    const BRACE_OPEN = '{';
35
    const BRACE_CLOSE = '}';
36
    const PAREN_OPEN = '(';
37
    const PAREN_CLOSE = ')';
38
    const SEMICOLON = ';';
39
    const WHITESPACE = ' ';
40
    const COMMA = ',';
41
    const ERROR_START = '#';
42
43
    const OPERATORS_SN = '+-';
44
    const OPERATORS_INFIX = '+-*/^&=><';
45
    const OPERATORS_POSTFIX = '%';
46
47
    /**
48
     * Formula.
49
     *
50
     * @var string
51
     */
52
    private $formula;
53
54
    /**
55
     * Tokens.
56
     *
57
     * @var FormulaToken[]
58
     */
59
    private $tokens = [];
60
61
    /**
62
     * Create a new FormulaParser.
63
     *
64
     * @param string $pFormula Formula to parse
65
     *
66
     * @throws Exception
67
     */
68
    public function __construct($pFormula = '')
69
    {
70
        // Check parameters
71
        if ($pFormula === null) {
72
            throw new Exception('Invalid parameter passed: formula');
73
        }
74
75
        // Initialise values
76
        $this->formula = trim($pFormula);
77
        // Parse!
78
        $this->parseToTokens();
79
    }
80
81
    /**
82
     * Get Formula.
83
     *
84
     * @return string
85
     */
86
    public function getFormula()
87
    {
88
        return $this->formula;
89
    }
90
91
    /**
92
     * Get Token.
93
     *
94
     * @param int $pId Token id
95
     *
96
     * @throws Exception
97
     *
98
     * @return string
99
     */
100
    public function getToken($pId = 0)
101
    {
102
        if (isset($this->tokens[$pId])) {
103
            return $this->tokens[$pId];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->tokens[$pId] returns the type PhpOffice\PhpSpreadsheet\Calculation\FormulaToken which is incompatible with the documented return type string.
Loading history...
104
        }
105
106
        throw new Exception("Token with id $pId does not exist.");
107
    }
108
109
    /**
110
     * Get Token count.
111
     *
112
     * @return int
113
     */
114
    public function getTokenCount()
115
    {
116
        return count($this->tokens);
117
    }
118
119
    /**
120
     * Get Tokens.
121
     *
122
     * @return FormulaToken[]
123
     */
124
    public function getTokens()
125
    {
126
        return $this->tokens;
127
    }
128
129
    /**
130
     * Parse to tokens.
131
     */
132
    private function parseToTokens()
133
    {
134
        // No attempt is made to verify formulas; assumes formulas are derived from Excel, where
135
        // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions.
136
137
        // Check if the formula has a valid starting =
138
        $formulaLength = strlen($this->formula);
139
        if ($formulaLength < 2 || $this->formula[0] != '=') {
140
            return;
141
        }
142
143
        // Helper variables
144
        $tokens1 = $tokens2 = $stack = [];
145
        $inString = $inPath = $inRange = $inError = false;
146
        $token = $previousToken = $nextToken = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $previousToken is dead and can be removed.
Loading history...
Unused Code introduced by
The assignment to $token is dead and can be removed.
Loading history...
147
148
        $index = 1;
149
        $value = '';
150
151
        $ERRORS = ['#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!', '#N/A'];
152
        $COMPARATORS_MULTI = ['>=', '<=', '<>'];
153
154
        while ($index < $formulaLength) {
155
            // state-dependent character evaluation (order is important)
156
157
            // double-quoted strings
158
            // embeds are doubled
159
            // end marks token
160
            if ($inString) {
161
                if ($this->formula[$index] == self::QUOTE_DOUBLE) {
162
                    if ((($index + 2) <= $formulaLength) && ($this->formula[$index + 1] == self::QUOTE_DOUBLE)) {
163
                        $value .= self::QUOTE_DOUBLE;
164
                        ++$index;
165
                    } else {
166
                        $inString = false;
167
                        $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND, FormulaToken::TOKEN_SUBTYPE_TEXT);
168
                        $value = '';
169
                    }
170
                } else {
171
                    $value .= $this->formula[$index];
172
                }
173
                ++$index;
174
175
                continue;
176
            }
177
178
            // single-quoted strings (links)
179
            // embeds are double
180
            // end does not mark a token
181
            if ($inPath) {
182
                if ($this->formula[$index] == self::QUOTE_SINGLE) {
183
                    if ((($index + 2) <= $formulaLength) && ($this->formula[$index + 1] == self::QUOTE_SINGLE)) {
184
                        $value .= self::QUOTE_SINGLE;
185
                        ++$index;
186
                    } else {
187
                        $inPath = false;
188
                    }
189
                } else {
190
                    $value .= $this->formula[$index];
191
                }
192
                ++$index;
193
194
                continue;
195
            }
196
197
            // bracked strings (R1C1 range index or linked workbook name)
198
            // no embeds (changed to "()" by Excel)
199
            // end does not mark a token
200
            if ($inRange) {
201
                if ($this->formula[$index] == self::BRACKET_CLOSE) {
202
                    $inRange = false;
203
                }
204
                $value .= $this->formula[$index];
205
                ++$index;
206
207
                continue;
208
            }
209
210
            // error values
211
            // end marks a token, determined from absolute list of values
212
            if ($inError) {
213
                $value .= $this->formula[$index];
214
                ++$index;
215
                if (in_array($value, $ERRORS)) {
216
                    $inError = false;
217
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND, FormulaToken::TOKEN_SUBTYPE_ERROR);
218
                    $value = '';
219
                }
220
221
                continue;
222
            }
223
224
            // scientific notation check
225
            if (strpos(self::OPERATORS_SN, $this->formula[$index]) !== false) {
226
                if (strlen($value) > 1) {
227
                    if (preg_match("/^[1-9]{1}(\.[0-9]+)?E{1}$/", $this->formula[$index]) != 0) {
228
                        $value .= $this->formula[$index];
229
                        ++$index;
230
231
                        continue;
232
                    }
233
                }
234
            }
235
236
            // independent character evaluation (order not important)
237
238
            // establish state-dependent character evaluations
239 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...
240
                if (strlen($value) > 0) {
241
                    // unexpected
242
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
243
                    $value = '';
244
                }
245
                $inString = true;
246
                ++$index;
247
248
                continue;
249
            }
250
251 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...
252
                if (strlen($value) > 0) {
253
                    // unexpected
254
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
255
                    $value = '';
256
                }
257
                $inPath = true;
258
                ++$index;
259
260
                continue;
261
            }
262
263
            if ($this->formula[$index] == self::BRACKET_OPEN) {
264
                $inRange = true;
265
                $value .= self::BRACKET_OPEN;
266
                ++$index;
267
268
                continue;
269
            }
270
271 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...
272
                if (strlen($value) > 0) {
273
                    // unexpected
274
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
275
                    $value = '';
276
                }
277
                $inError = true;
278
                $value .= self::ERROR_START;
279
                ++$index;
280
281
                continue;
282
            }
283
284
            // mark start and end of arrays and array rows
285
            if ($this->formula[$index] == self::BRACE_OPEN) {
286
                if (strlen($value) > 0) {
287
                    // unexpected
288
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_UNKNOWN);
289
                    $value = '';
290
                }
291
292
                $tmp = new FormulaToken('ARRAY', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
293
                $tokens1[] = $tmp;
294
                $stack[] = clone $tmp;
295
296
                $tmp = new FormulaToken('ARRAYROW', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
297
                $tokens1[] = $tmp;
298
                $stack[] = clone $tmp;
299
300
                ++$index;
301
302
                continue;
303
            }
304
305
            if ($this->formula[$index] == self::SEMICOLON) {
306
                if (strlen($value) > 0) {
307
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
308
                    $value = '';
309
                }
310
311
                $tmp = array_pop($stack);
312
                $tmp->setValue('');
313
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
314
                $tokens1[] = $tmp;
315
316
                $tmp = new FormulaToken(',', FormulaToken::TOKEN_TYPE_ARGUMENT);
317
                $tokens1[] = $tmp;
318
319
                $tmp = new FormulaToken('ARRAYROW', FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
320
                $tokens1[] = $tmp;
321
                $stack[] = clone $tmp;
322
323
                ++$index;
324
325
                continue;
326
            }
327
328
            if ($this->formula[$index] == self::BRACE_CLOSE) {
329
                if (strlen($value) > 0) {
330
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
331
                    $value = '';
332
                }
333
334
                $tmp = array_pop($stack);
335
                $tmp->setValue('');
336
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
337
                $tokens1[] = $tmp;
338
339
                $tmp = array_pop($stack);
340
                $tmp->setValue('');
341
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
342
                $tokens1[] = $tmp;
343
344
                ++$index;
345
346
                continue;
347
            }
348
349
            // trim white-space
350
            if ($this->formula[$index] == self::WHITESPACE) {
351
                if (strlen($value) > 0) {
352
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
353
                    $value = '';
354
                }
355
                $tokens1[] = new FormulaToken('', FormulaToken::TOKEN_TYPE_WHITESPACE);
356
                ++$index;
357
                while (($this->formula[$index] == self::WHITESPACE) && ($index < $formulaLength)) {
358
                    ++$index;
359
                }
360
361
                continue;
362
            }
363
364
            // multi-character comparators
365
            if (($index + 2) <= $formulaLength) {
366
                if (in_array(substr($this->formula, $index, 2), $COMPARATORS_MULTI)) {
367
                    if (strlen($value) > 0) {
368
                        $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
369
                        $value = '';
370
                    }
371
                    $tokens1[] = new FormulaToken(substr($this->formula, $index, 2), FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_LOGICAL);
1 ignored issue
show
Bug introduced by
It seems like substr($this->formula, $index, 2) can also be of type false; however, parameter $pValue of PhpOffice\PhpSpreadsheet...ulaToken::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

371
                    $tokens1[] = new FormulaToken(/** @scrutinizer ignore-type */ substr($this->formula, $index, 2), FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_LOGICAL);
Loading history...
372
                    $index += 2;
373
374
                    continue;
375
                }
376
            }
377
378
            // standard infix operators
379 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...
380
                if (strlen($value) > 0) {
381
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
382
                    $value = '';
383
                }
384
                $tokens1[] = new FormulaToken($this->formula[$index], FormulaToken::TOKEN_TYPE_OPERATORINFIX);
385
                ++$index;
386
387
                continue;
388
            }
389
390
            // standard postfix operators (only one)
391 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...
392
                if (strlen($value) > 0) {
393
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
394
                    $value = '';
395
                }
396
                $tokens1[] = new FormulaToken($this->formula[$index], FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX);
397
                ++$index;
398
399
                continue;
400
            }
401
402
            // start subexpression or function
403
            if ($this->formula[$index] == self::PAREN_OPEN) {
404
                if (strlen($value) > 0) {
405
                    $tmp = new FormulaToken($value, FormulaToken::TOKEN_TYPE_FUNCTION, FormulaToken::TOKEN_SUBTYPE_START);
406
                    $tokens1[] = $tmp;
407
                    $stack[] = clone $tmp;
408
                    $value = '';
409
                } else {
410
                    $tmp = new FormulaToken('', FormulaToken::TOKEN_TYPE_SUBEXPRESSION, FormulaToken::TOKEN_SUBTYPE_START);
411
                    $tokens1[] = $tmp;
412
                    $stack[] = clone $tmp;
413
                }
414
                ++$index;
415
416
                continue;
417
            }
418
419
            // function, subexpression, or array parameters, or operand unions
420
            if ($this->formula[$index] == self::COMMA) {
421
                if (strlen($value) > 0) {
422
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
423
                    $value = '';
424
                }
425
426
                $tmp = array_pop($stack);
427
                $tmp->setValue('');
428
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
429
                $stack[] = $tmp;
430
431
                if ($tmp->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
432
                    $tokens1[] = new FormulaToken(',', FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_UNION);
433
                } else {
434
                    $tokens1[] = new FormulaToken(',', FormulaToken::TOKEN_TYPE_ARGUMENT);
435
                }
436
                ++$index;
437
438
                continue;
439
            }
440
441
            // stop subexpression
442
            if ($this->formula[$index] == self::PAREN_CLOSE) {
443
                if (strlen($value) > 0) {
444
                    $tokens1[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERAND);
445
                    $value = '';
446
                }
447
448
                $tmp = array_pop($stack);
449
                $tmp->setValue('');
450
                $tmp->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_STOP);
451
                $tokens1[] = $tmp;
452
453
                ++$index;
454
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 ($token === null) {
484
                continue;
485
            }
486
487
            if ($token->getTokenType() != FormulaToken::TOKEN_TYPE_WHITESPACE) {
488
                $tokens2[] = $token;
489
490
                continue;
491
            }
492
493
            if ($previousToken === null) {
494
                continue;
495
            }
496
497 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...
498
                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
499
                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
500
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
501
                    )) {
502
                continue;
503
            }
504
505
            if ($nextToken === null) {
506
                continue;
507
            }
508
509 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...
510
                    (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
511
                    (($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_START)) ||
512
                    ($nextToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)
513
                    )) {
514
                continue;
515
            }
516
517
            $tokens2[] = new FormulaToken($value, FormulaToken::TOKEN_TYPE_OPERATORINFIX, FormulaToken::TOKEN_SUBTYPE_INTERSECTION);
518
        }
519
520
        // move tokens to final list, switching infix "-" operators to prefix when appropriate, switching infix "+" operators
521
        // to noop when appropriate, identifying operand and infix-operator subtypes, and pulling "@" from function names
522
        $this->tokens = [];
523
524
        $tokenCount = count($tokens2);
525
        for ($i = 0; $i < $tokenCount; ++$i) {
526
            $token = $tokens2[$i];
527
            if (isset($tokens2[$i - 1])) {
528
                $previousToken = $tokens2[$i - 1];
529
            } else {
530
                $previousToken = null;
531
            }
532
            if (isset($tokens2[$i + 1])) {
533
                $nextToken = $tokens2[$i + 1];
0 ignored issues
show
Unused Code introduced by
The assignment to $nextToken is dead and can be removed.
Loading history...
534
            } else {
535
                $nextToken = null;
536
            }
537
538
            if ($token === null) {
539
                continue;
540
            }
541
542
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '-') {
543
                if ($i == 0) {
544
                    $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
545 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...
546
                    ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
547
                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
548
                    ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
549
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
550
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)) {
551
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
552
                } else {
553
                    $token->setTokenType(FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
554
                }
555
556
                $this->tokens[] = $token;
557
558
                continue;
559
            }
560
561
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == '+') {
562 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...
563
                    continue;
564
                } elseif ((($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) &&
565
                    ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
566
                    (($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
567
                    ($previousToken->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_STOP)) ||
568
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
569
                    ($previousToken->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND)) {
570
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
571
                } else {
572
                    continue;
573
                }
574
575
                $this->tokens[] = $token;
576
577
                continue;
578
            }
579
580
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERATORINFIX &&
581
                $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING) {
582
                if (strpos('<>=', substr($token->getValue(), 0, 1)) !== false) {
583
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
584
                } elseif ($token->getValue() == '&') {
585
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
586
                } else {
587
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_MATH);
588
                }
589
590
                $this->tokens[] = $token;
591
592
                continue;
593
            }
594
595
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_OPERAND &&
596
                $token->getTokenSubType() == FormulaToken::TOKEN_SUBTYPE_NOTHING) {
597
                if (!is_numeric($token->getValue())) {
598
                    if (strtoupper($token->getValue()) == 'TRUE' || strtoupper($token->getValue()) == 'FALSE') {
599
                        $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_LOGICAL);
600
                    } else {
601
                        $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_RANGE);
602
                    }
603
                } else {
604
                    $token->setTokenSubType(FormulaToken::TOKEN_SUBTYPE_NUMBER);
605
                }
606
607
                $this->tokens[] = $token;
608
609
                continue;
610
            }
611
612
            if ($token->getTokenType() == FormulaToken::TOKEN_TYPE_FUNCTION) {
613
                if (strlen($token->getValue()) > 0) {
614
                    if (substr($token->getValue(), 0, 1) == '@') {
615
                        $token->setValue(substr($token->getValue(), 1));
616
                    }
617
                }
618
            }
619
620
            $this->tokens[] = $token;
621
        }
622
    }
623
}
624