Completed
Push — master ( 8956cf...1ecd61 )
by Michal
03:17
created

Condition::parse()   D

Complexity

Conditions 26
Paths 24

Size

Total Lines 118
Code Lines 53

Duplication

Lines 10
Ratio 8.47 %

Code Coverage

Tests 64
CRAP Score 26.0024

Importance

Changes 6
Bugs 1 Features 0
Metric Value
c 6
b 1
f 0
dl 10
loc 118
ccs 64
cts 65
cp 0.9846
rs 4.5382
cc 26
eloc 53
nc 24
nop 3
crap 26.0024

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * `WHERE` keyword parser.
5
 *
6
 * @package    SqlParser
7
 * @subpackage Components
8
 */
9
namespace SqlParser\Components;
10
11
use SqlParser\Component;
12
use SqlParser\Parser;
13
use SqlParser\Token;
14
use SqlParser\TokensList;
15
16
/**
17
 * `WHERE` keyword parser.
18
 *
19
 * @category   Keywords
20
 * @package    SqlParser
21
 * @subpackage Components
22
 * @author     Dan Ungureanu <[email protected]>
23
 * @license    http://opensource.org/licenses/GPL-2.0 GNU Public License
24
 */
25
class Condition extends Component
26
{
27
28
    /**
29
     * Logical operators that can be used to delimit expressions.
30
     *
31
     * @var array
32
     */
33
    public static $DELIMITERS = array('&&', '||', 'AND', 'OR', 'XOR');
34
35
    /**
36
     * List of allowed reserved keywords in conditions.
37
     *
38
     * @var array
39
     */
40
    public static $ALLOWED_KEYWORDS = array(
41
        'ALL'                           => 1,
42
        'AND'                           => 1,
43
        'BETWEEN'                       => 1,
44
        'EXISTS'                        => 1,
45
        'IF'                            => 1,
46
        'IN'                            => 1,
47
        'INTERVAL'                      => 1,
48
        'IS'                            => 1,
49
        'LIKE'                          => 1,
50
        'MATCH'                         => 1,
51
        'NOT IN'                        => 1,
52
        'NOT NULL'                      => 1,
53
        'NOT'                           => 1,
54
        'NULL'                          => 1,
55
        'OR'                            => 1,
56
        'XOR'                           => 1,
57
    );
58
59
    /**
60
     * Identifiers recognized.
61
     *
62
     * @var array
63
     */
64
    public $identifiers = array();
65
66
    /**
67
     * Whether this component is an operator.
68
     *
69
     * @var bool
70
     */
71
    public $isOperator = false;
72
73
    /**
74
     * The condition.
75
     *
76
     * @var string
77
     */
78
    public $expr;
79
80
    /**
81
     * Constructor.
82
     *
83
     * @param string $expr The condition or the operator.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $expr not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
84
     */
85 23
    public function __construct($expr = null)
86
    {
87 23
        $this->expr = trim($expr);
88 23
    }
89
90
    /**
91
     * @param Parser     $parser  The parser that serves as context.
92
     * @param TokensList $list    The list of tokens that are being parsed.
93
     * @param array      $options Parameters for parsing.
94
     *
95
     * @return Condition[]
96
     */
97 22
    public static function parse(Parser $parser, TokensList $list, array $options = array())
98
    {
99 22
        $ret = array();
100
101 22
        $expr = new Condition();
102
103
        /**
104
         * Counts brackets.
105
         *
106
         * @var int $brackets
107
         */
108 22
        $brackets = 0;
109
110
        /**
111
         * Whether there was a `BETWEEN` keyword before or not.
112
         *
113
         * It is required to keep track of them because their structure contains
114
         * the keyword `AND`, which is also an operator that delimits
115
         * expressions.
116
         *
117
         * @var bool $betweenBefore
118
         */
119 22
        $betweenBefore = false;
120
121 22
        for (; $list->idx < $list->count; ++$list->idx) {
122
123
            /**
124
             * Token parsed at this moment.
125
             *
126
             * @var Token $token
127
             */
128 22
            $token = $list->tokens[$list->idx];
129
130
            // End of statement.
131 22
            if ($token->type === Token::TYPE_DELIMITER) {
132 14
                break;
133
            }
134
135
            // Skipping whitespaces and comments.
136 22
            if ($token->type === Token::TYPE_COMMENT) {
137 2
                continue;
138
            }
139
140
            // Replacing all whitespaces (new lines, tabs, etc.) with a single
141
            // space character.
142 22
            if ($token->type === Token::TYPE_WHITESPACE) {
143 21
                $expr->expr .= ' ';
144 21
                continue;
145
            }
146
147
            // Conditions are delimited by logical operators.
148 22
            if (in_array($token->value, static::$DELIMITERS, true)) {
149 6
                if (($betweenBefore) && ($token->value === 'AND')) {
150
                    // The syntax of keyword `BETWEEN` is hard-coded.
151 1
                    $betweenBefore = false;
152 1
                } else {
153
                    // The expression ended.
154 6
                    $expr->expr = trim($expr->expr);
155 6
                    if (!empty($expr->expr)) {
156 6
                        $ret[] = $expr;
157 6
                    }
158
159
                    // Adding the operator.
160 6
                    $expr = new Condition($token->value);
161 6
                    $expr->isOperator = true;
162 6
                    $ret[] = $expr;
163
164
                    // Preparing to parse another condition.
165 6
                    $expr = new Condition();
166 6
                    continue;
167
                }
168 1
            }
169
170 22
            if (($token->type === Token::TYPE_KEYWORD)
171 22
                && ($token->flags & Token::FLAG_KEYWORD_RESERVED)
172 22
                && !($token->flags & Token::FLAG_KEYWORD_FUNCTION)
173 22
            ) {
174 11
                if ($token->value === 'BETWEEN') {
175 1
                    $betweenBefore = true;
176 1
                }
177 11
                if (($brackets === 0) && (empty(static::$ALLOWED_KEYWORDS[$token->value]))) {
178 9
                    break;
179
                }
180 3
            }
181
182 21 View Code Duplication
            if ($token->type === Token::TYPE_OPERATOR) {
183 21
                if ($token->value === '(') {
184 6
                    ++$brackets;
185 21
                } elseif ($token->value === ')') {
186 6
                    if ($brackets == 0) {
187
                        break;
188
                    }
189 6
                    --$brackets;
190 6
                }
191 21
            }
192
193 21
            $expr->expr .= $token->token;
194 21
            if (($token->type === Token::TYPE_NONE)
195 21
                || (($token->type === Token::TYPE_KEYWORD)
196 21
                && (!($token->flags & Token::FLAG_KEYWORD_RESERVED)))
197 21
                || ($token->type === Token::TYPE_STRING)
198 21
                || ($token->type === Token::TYPE_SYMBOL)
199 21
            ) {
200 21
                if (!in_array($token->value, $expr->identifiers)) {
201 21
                    $expr->identifiers[] = $token->value;
202 21
                }
203 21
            }
204 21
        }
205
206
        // Last iteration was not processed.
207 22
        $expr->expr = trim($expr->expr);
208 22
        if (!empty($expr->expr)) {
209 21
            $ret[] = $expr;
210 21
        }
211
212 22
        --$list->idx;
213 22
        return $ret;
214
    }
215
216
    /**
217
     * @param Condition[] $component The component to be built.
218
     * @param array       $options   Parameters for building.
219
     *
220
     * @return string
221
     */
222 4
    public static function build($component, array $options = array())
223
    {
224 4
        if (is_array($component)) {
225 4
            return implode(' ', $component);
226
        } else {
227 4
            return $component->expr;
228
        }
229
    }
230
}
231