GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 05dd66...ce9b2d )
by
unknown
03:03
created

Lexer   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 4

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 42
c 3
b 0
f 0
lcom 0
cbo 4
dl 0
loc 165
rs 8.295

2 Methods

Rating   Name   Duplication   Size   Complexity  
D tokenize() 0 68 22
C tokenizeExpression() 0 75 20

How to fix   Complexity   

Complex Class

Complex classes like Lexer 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 Lexer, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * See class comment
4
 *
5
 * PHP Version 5
6
 *
7
 * @category   Netresearch
8
 * @package    Netresearch\Kite
9
 * @subpackage ExpressionLanguage
10
 * @author     Christian Opitz <[email protected]>
11
 * @license    http://www.netresearch.de Netresearch Copyright
12
 * @link       http://www.netresearch.de
13
 */
14
15
namespace Netresearch\Kite\ExpressionLanguage;
16
use Symfony\Component\ExpressionLanguage\SyntaxError;
17
use Symfony\Component\ExpressionLanguage\Token;
18
use Symfony\Component\ExpressionLanguage\TokenStream;
19
20
/**
21
 * Extended expression lexer - handles strings with nested expressions:
22
 * For example:
23
 *   "The job {job.name} is {job.getWorkflow().isRunning() ? 'currently' : 'not'} running"
24
 * will be transformed into that:
25
 *   "'The job ' ~ (variables.getExpanded('job.name')) ~ ' is ' ~ (variables.getExpanded('job').getWorkflow().isRunning() ? 'currently' : 'not') ~ 'running'"
26
 *
27
 * When there is only one expression, the expression type is not casted to string
28
 * "{{foo: 'bar'}}" will return the specified hash
29
 *
30
 * Also this rewrites objects and properties access in order to use the
31
 * variableRepository getExpanded method - actually this would have been
32
 * a parser or node task but here we can do that in the quickest possible
33
 * way
34
 *
35
 * @category   Netresearch
36
 * @package    Netresearch\Kite
37
 * @subpackage ExpressionLanguage
38
 * @author     Christian Opitz <[email protected]>
39
 * @license    http://www.netresearch.de Netresearch Copyright
40
 * @link       http://www.netresearch.de
41
 */
42
class Lexer extends \Symfony\Component\ExpressionLanguage\Lexer
43
{
44
    /**
45
     * Tokenizes an expression.
46
     *
47
     * @param string $expression The expression to tokenize
48
     *
49
     * @return TokenStream A token stream instance
50
     *
51
     * @throws SyntaxError
52
     */
53
    public function tokenize($expression)
54
    {
55
        $cursor = -1;
56
        $end = strlen($expression);
57
        $expressions = array(array(null, 0, 0));
58
        $current = 0;
59
        $group = 0;
60
        // Split the expression into it's string and actual expression parts
61
        while (++$cursor < $end) {
62
            if ($expression[$cursor] === '{' || $expression[$cursor] === '}') {
63
                if ($cursor && $expression[$cursor - 1] === '\\') {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
64
                    // Escaped parenthesis
65
                    // Let parser unescape them after parsing
66
                } else {
67
                    $type = $expression[$cursor] === '{' ? 1 : -1;
68
                    $group += $type;
69
                    if ($group === 1 && $type === 1) {
70
                        $expressions[++$current] = array(null, 1, $cursor);
71
                        continue;
72
                    } elseif ($group === 0 && $type === -1) {
73
                        $expressions[++$current] = array(null, 0, $cursor);
74
                        continue;
75
                    } elseif ($group < 0) {
76
                        throw new \Exception('Unopened and unescaped closing parenthesis');
77
                    }
78
                }
79
            }
80
            $expressions[$current][0] .= $expression[$cursor];
81
        }
82
        if ($group) {
83
            throw new \Exception('Unclosed and unescaped opening parenthesis');
84
        }
85
86
        // Filter out empty expressions
87
        foreach ($expressions as $i => $properties) {
88
            if ($properties[0] === null) {
89
                unset($expressions[$i]);
90
            }
91
        }
92
93
        // Actually tokenize all remaining expressions
94
        $tokens = array();
95
        $isMultipleExpressions = count($expressions) > 1;
96
        foreach (array_values($expressions) as $i => $properties) {
97
            list($value, $type, $cursor) = $properties;
98
            if ($isMultipleExpressions && $i > 0) {
99
                $tokens[] = new Token(Token::OPERATOR_TYPE, '~', $cursor);
100
            }
101
            if ($type === 0) {
102
                $tokens[] = new Token(Token::STRING_TYPE, $value, $cursor);
103
            } else {
104
                if ($isMultipleExpressions) {
105
                    $tokens[] = new Token(Token::PUNCTUATION_TYPE, '(', $cursor);
106
                }
107
                foreach ($this->tokenizeExpression($value) as $token) {
108
                    $token->cursor += $cursor;
109
                    $tokens[] = $token;
110
                }
111
                if ($isMultipleExpressions) {
112
                    $tokens[] = new Token(Token::PUNCTUATION_TYPE, ')', $cursor);
113
                }
114
            }
115
        }
116
117
        $tokens[] = new Token(Token::EOF_TYPE, null, $end);
118
119
        return new TokenStream($tokens);
120
    }
121
122
    /**
123
     * Actually tokenize an expression - at this point object and property access is
124
     * transformed, so that "this.property" will be "get('this.propery')"
125
     *
126
     * @param string $expression The expression
127
     *
128
     * @return array
129
     */
130
    protected function tokenizeExpression($expression)
131
    {
132
        $stream = parent::tokenize($expression);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (tokenize() instead of tokenizeExpression()). Are you sure this is correct? If so, you might want to change this to $this->tokenize().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
133
        $tokens = array();
134
        $previousWasDot = false;
135
        $ignorePrimaryExpressions = array_flip(['null', 'NULL', 'false', 'FALSE', 'true', 'TRUE']);
136
        while (!$stream->isEOF()) {
137
            /* @var \Symfony\Component\ExpressionLanguage\Token $token */
138
            $token = $stream->current;
139
            $stream->next();
140
            if ($token->type === Token::NAME_TYPE && !$previousWasDot) {
141
                if (array_key_exists($token->value, $ignorePrimaryExpressions)) {
142
                    $tokens[] = $token;
143
                    continue;
144
                }
145
                $isTest = false;
146
                if ($stream->current->test(Token::PUNCTUATION_TYPE, '(')) {
147
                    $tokens[] = $token;
148
                    if ($token->value === 'isset' || $token->value === 'empty') {
149
                        $isTest = true;
150
                        $tokens[] = $stream->current;
151
                        $stream->next();
152
                        $token = $stream->current;
153
                        if ($token->type !== Token::NAME_TYPE) {
154
                            throw new SyntaxError('Expected name', $token->cursor);
155
                        }
156
                        $stream->next();
157
                    } else {
158
                        continue;
159
                    }
160
                }
161
                $names = array($token->value);
162
                $isFunctionCall = false;
163
                while (!$stream->isEOF() && $stream->current->type === Token::PUNCTUATION_TYPE && $stream->current->value === '.') {
164
                    $stream->next();
165
                    $nameToken = $stream->current;
166
                    $stream->next();
167
                    // Operators like "not" and "matches" are valid method or property names - others not
168
                    if ($nameToken->type !== Token::NAME_TYPE
169
                        && ($nameToken->type !== Token::OPERATOR_TYPE || !preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $nameToken->value))
170
                    ) {
171
                        throw new SyntaxError('Expected name', $nameToken->cursor);
172
                    }
173
                    if ($stream->current->test(Token::PUNCTUATION_TYPE, '(')) {
174
                        $isFunctionCall = true;
175
                    } else {
176
                        $names[] = $nameToken->value;
177
                    }
178
                }
179
                if ($isTest) {
180
                    if ($isFunctionCall) {
181
                        throw new SyntaxError('Can\'t use function return value in write context',  $stream->current->cursor);
182
                    }
183
                    if (!$stream->current->test(Token::PUNCTUATION_TYPE, ')')) {
184
                        throw new SyntaxError('Expected )',  $stream->current->cursor);
185
                    }
186
                    $tokens[] = new Token(Token::STRING_TYPE, implode('.', $names), $token->cursor);
187
                } else {
188
                    $tokens[] = new Token(Token::NAME_TYPE, 'get', $token->cursor);
189
                    $tokens[] = new Token(Token::PUNCTUATION_TYPE, '(', $token->cursor);
190
                    $tokens[] = new Token(Token::STRING_TYPE, implode('.', $names), $token->cursor);
191
                    $tokens[] = new Token(Token::PUNCTUATION_TYPE, ')', $token->cursor);
192
                    if ($isFunctionCall) {
193
                        $tokens[] = new Token(Token::PUNCTUATION_TYPE, '.', $nameToken->cursor - strlen($nameToken->value));
0 ignored issues
show
Bug introduced by
The variable $nameToken does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
194
                        $tokens[] = $nameToken;
195
                    }
196
                }
197
            } else {
198
                $tokens[] = $token;
199
                $previousWasDot = $token->test(Token::PUNCTUATION_TYPE, '.');
200
            }
201
        }
202
203
        return $tokens;
204
    }
205
206
}
207
208
?>
1 ignored issue
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
209