Completed
Push — master ( 65f66e...428edc )
by Michal
04:14
created

JoinKeyword::parse()   D

Complexity

Conditions 19
Paths 16

Size

Total Lines 104
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 58
CRAP Score 19

Importance

Changes 0
Metric Value
cc 19
eloc 51
nc 16
nop 3
dl 0
loc 104
ccs 58
cts 58
cp 1
crap 19
rs 4.764
c 0
b 0
f 0

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
 * `JOIN` keyword parser.
5
 */
6
7
namespace PhpMyAdmin\SqlParser\Components;
8
9
use PhpMyAdmin\SqlParser\Component;
10
use PhpMyAdmin\SqlParser\Parser;
11
use PhpMyAdmin\SqlParser\Token;
12
use PhpMyAdmin\SqlParser\TokensList;
13
14
/**
15
 * `JOIN` keyword parser.
16
 *
17
 * @category   Keywords
18
 *
19
 * @license    https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
20
 */
21
class JoinKeyword extends Component
22
{
23
    /**
24
     * Types of join.
25
     *
26
     * @var array
27
     */
28
    public static $JOINS = array(
29
        'CROSS JOIN' => 'CROSS',
30
        'FULL JOIN' => 'FULL',
31
        'FULL OUTER JOIN' => 'FULL',
32
        'INNER JOIN' => 'INNER',
33
        'JOIN' => 'JOIN',
34
        'LEFT JOIN' => 'LEFT',
35
        'LEFT OUTER JOIN' => 'LEFT',
36
        'RIGHT JOIN' => 'RIGHT',
37
        'RIGHT OUTER JOIN' => 'RIGHT',
38
        'NATURAL JOIN' => 'NATURAL',
39
        'NATURAL LEFT JOIN' => 'NATURAL LEFT',
40
        'NATURAL RIGHT JOIN' => 'NATURAL RIGHT',
41
        'NATURAL LEFT OUTER JOIN' => 'NATURAL LEFT OUTER',
42
        'NATURAL RIGHT OUTER JOIN' => 'NATURAL RIGHT OUTER',
43
        'STRAIGHT_JOIN' => 'STRAIGHT',
44
    );
45
46
    /**
47
     * Type of this join.
48
     *
49
     * @see static::$JOINS
50
     *
51
     * @var string
52
     */
53
    public $type;
54
55
    /**
56
     * Join expression.
57
     *
58
     * @var Expression
59
     */
60
    public $expr;
61
62
    /**
63
     * Join conditions.
64
     *
65
     * @var Condition[]
66
     */
67
    public $on;
68
69
    /**
70
     * Columns in Using clause.
71
     *
72
     * @var ArrayObj
73
     */
74
    public $using;
75
76
    /**
77
     * @param Parser     $parser  the parser that serves as context
78
     * @param TokensList $list    the list of tokens that are being parsed
79
     * @param array      $options parameters for parsing
80
     *
81
     * @return JoinKeyword[]
82
     */
83 19
    public static function parse(Parser $parser, TokensList $list, array $options = array())
84
    {
85 19
        $ret = array();
86
87 19
        $expr = new self();
88
89
        /**
90
         * The state of the parser.
91
         *
92
         * Below are the states of the parser.
93
         *
94
         *      0 -----------------------[ JOIN ]----------------------> 1
95
         *
96
         *      1 -----------------------[ expr ]----------------------> 2
97
         *
98
         *      2 ------------------------[ ON ]-----------------------> 3
99
         *      2 -----------------------[ USING ]---------------------> 4
100
         *
101
         *      3 --------------------[ conditions ]-------------------> 0
102
         *
103
         *      4 ----------------------[ columns ]--------------------> 0
104
         *
105
         * @var int
106
         */
107 19
        $state = 0;
108
109
        // By design, the parser will parse first token after the keyword.
110
        // In this case, the keyword must be analyzed too, in order to determine
111
        // the type of this join.
112 19
        if ($list->idx > 0) {
113 16
            --$list->idx;
114 16
        }
115
116 19
        for (; $list->idx < $list->count; ++$list->idx) {
117
            /**
118
             * Token parsed at this moment.
119
             *
120
             * @var Token
121
             */
122 19
            $token = $list->tokens[$list->idx];
123
124
            // End of statement.
125 19
            if ($token->type === Token::TYPE_DELIMITER) {
126 14
                break;
127
            }
128
129
            // Skipping whitespaces and comments.
130 19
            if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
131 19
                continue;
132
            }
133
134 19
            if ($state === 0) {
135 19
                if (($token->type === Token::TYPE_KEYWORD)
136 19
                    && (!empty(static::$JOINS[$token->value]))
137 19
                ) {
138 19
                    $expr->type = static::$JOINS[$token->value];
139 19
                    $state = 1;
140 19
                } else {
141 3
                    break;
142
                }
143 19
            } elseif ($state === 1) {
144 19
                $expr->expr = Expression::parse($parser, $list, array('field' => 'table'));
145 19
                $state = 2;
146 19
            } elseif ($state === 2) {
147 12
                if ($token->type === Token::TYPE_KEYWORD) {
148 12
                    if ($token->value === 'ON') {
149 9
                        $state = 3;
150 12
                    } elseif ($token->value === 'USING') {
151 1
                        $state = 4;
152 1
                    } else {
153 3
                        if (($token->type === Token::TYPE_KEYWORD)
154 3
                            && (!empty(static::$JOINS[$token->value]))
155 3
                        ) {
156 2
                            $ret[] = $expr;
157 2
                            $expr = new self();
158 2
                            $expr->type = static::$JOINS[$token->value];
159 2
                            $state = 1;
160 2
                        } else {
161
                            /* Next clause is starting */
162 2
                            break;
163
                        }
164
                    }
165 11
                }
166 11
            } elseif ($state === 3) {
167 9
                $expr->on = Condition::parse($parser, $list);
168 9
                $ret[] = $expr;
169 9
                $expr = new self();
170 9
                $state = 0;
171 10
            } elseif ($state === 4) {
172 1
                $expr->using = ArrayObj::parse($parser, $list);
0 ignored issues
show
Documentation Bug introduced by
It seems like \PhpMyAdmin\SqlParser\Co...::parse($parser, $list) can also be of type array. However, the property $using is declared as type object<PhpMyAdmin\SqlParser\Components\ArrayObj>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
173 1
                $ret[] = $expr;
174 1
                $expr = new self();
175 1
                $state = 0;
176 1
            }
177 19
        }
178
179 19
        if (!empty($expr->type)) {
180 9
            $ret[] = $expr;
181 9
        }
182
183 19
        --$list->idx;
184
185 19
        return $ret;
186
    }
187
188
    /**
189
     * @param JoinKeyword[] $component the component to be built
190
     * @param array         $options   parameters for building
191
     *
192
     * @return string
193
     */
194 3
    public static function build($component, array $options = array())
195
    {
196 3
        $ret = array();
197 3
        foreach ($component as $c) {
198 3
            $ret[] = array_search($c->type, static::$JOINS) . ' ' . $c->expr
199 3
                . (!empty($c->on)
200 3
                    ? ' ON ' . Condition::build($c->on) : '')
201 3
                . (!empty($c->using)
202 3
                    ? ' USING ' . ArrayObj::build($c->using) : '');
203 3
        }
204
205 3
        return implode(' ', $ret);
206
    }
207
}
208