Passed
Push — master ( b16987...8b6d77 )
by Maurício
03:49 queued 13s
created

JoinKeywords   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 51
dl 0
loc 115
ccs 51
cts 51
cp 1
rs 10
c 0
b 0
f 0
wmc 18

2 Methods

Rating   Name   Duplication   Size   Complexity  
C parse() 0 100 17
A buildAll() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\SqlParser\Parsers;
6
7
use PhpMyAdmin\SqlParser\Components\JoinKeyword;
8
use PhpMyAdmin\SqlParser\Parseable;
9
use PhpMyAdmin\SqlParser\Parser;
10
use PhpMyAdmin\SqlParser\TokensList;
11
use PhpMyAdmin\SqlParser\TokenType;
12
13
use function implode;
14
15
/**
16
 * `JOIN` keyword parser.
17
 */
18
final class JoinKeywords implements Parseable
19
{
20
    /**
21
     * @param Parser               $parser  the parser that serves as context
22
     * @param TokensList           $list    the list of tokens that are being parsed
23
     * @param array<string, mixed> $options parameters for parsing
24
     *
25
     * @return JoinKeyword[]
26
     */
27 78
    public static function parse(Parser $parser, TokensList $list, array $options = []): array
28
    {
29 78
        $ret = [];
30
31 78
        $expr = new JoinKeyword();
32
33
        /**
34
         * The state of the parser.
35
         *
36
         * Below are the states of the parser.
37
         *
38
         *      0 -----------------------[ JOIN ]----------------------> 1
39
         *
40
         *      1 -----------------------[ expr ]----------------------> 2
41
         *
42
         *      2 ------------------------[ ON ]-----------------------> 3
43
         *      2 -----------------------[ USING ]---------------------> 4
44
         *
45
         *      3 --------------------[ conditions ]-------------------> 0
46
         *
47
         *      4 ----------------------[ columns ]--------------------> 0
48
         */
49 78
        $state = 0;
50
51
        // By design, the parser will parse first token after the keyword.
52
        // In this case, the keyword must be analyzed too, in order to determine
53
        // the type of this join.
54 78
        if ($list->idx > 0) {
55 72
            --$list->idx;
56
        }
57
58 78
        for (; $list->idx < $list->count; ++$list->idx) {
59
            /**
60
             * Token parsed at this moment.
61
             */
62 78
            $token = $list->tokens[$list->idx];
63
64
            // End of statement.
65 78
            if ($token->type === TokenType::Delimiter) {
66 38
                break;
67
            }
68
69
            // Skipping whitespaces and comments.
70 78
            if (($token->type === TokenType::Whitespace) || ($token->type === TokenType::Comment)) {
71 78
                continue;
72
            }
73
74 78
            if ($state === 0) {
75 78
                if (($token->type !== TokenType::Keyword) || empty(JoinKeyword::JOINS[$token->keyword])) {
76 34
                    break;
77
                }
78
79 78
                $expr->type = JoinKeyword::JOINS[$token->keyword];
80 78
                $state = 1;
81 78
            } elseif ($state === 1) {
82 78
                $expr->expr = Expressions::parse($parser, $list, ['field' => 'table']);
83 78
                $state = 2;
84 60
            } elseif ($state === 2) {
85 60
                if ($token->type === TokenType::Keyword) {
86 60
                    switch ($token->keyword) {
87 60
                        case 'ON':
88 50
                            $state = 3;
89 50
                            break;
90 12
                        case 'USING':
91 2
                            $state = 4;
92 2
                            break;
93
                        default:
94 10
                            if (empty(JoinKeyword::JOINS[$token->keyword])) {
95
                                /* Next clause is starting */
96 8
                                break 2;
97
                            }
98
99 8
                            $ret[] = $expr;
100 8
                            $expr = new JoinKeyword();
101 8
                            $expr->type = JoinKeyword::JOINS[$token->keyword];
102 8
                            $state = 1;
103
104 8
                            break;
105
                    }
106
                }
107 52
            } elseif ($state === 3) {
108 50
                $expr->on = Conditions::parse($parser, $list);
109 50
                $ret[] = $expr;
110 50
                $expr = new JoinKeyword();
111 50
                $state = 0;
112
            } else {
113 2
                $expr->using = ArrayObjs::parse($parser, $list);
114 2
                $ret[] = $expr;
115 2
                $expr = new JoinKeyword();
116 2
                $state = 0;
117
            }
118
        }
119
120 78
        if (! empty($expr->type)) {
121 28
            $ret[] = $expr;
122
        }
123
124 78
        --$list->idx;
125
126 78
        return $ret;
127
    }
128
129
    /** @param JoinKeyword[] $component the component to be built */
130 22
    public static function buildAll(array $component): string
131
    {
132 22
        return implode(' ', $component);
133
    }
134
}
135