1<?php
2
3/**
4 * `VALUES` keyword parser.
5 */
6
7namespace PhpMyAdmin\SqlParser\Components;
8
9use PhpMyAdmin\SqlParser\Component;
10use PhpMyAdmin\SqlParser\Parser;
11use PhpMyAdmin\SqlParser\Token;
12use PhpMyAdmin\SqlParser\TokensList;
13use PhpMyAdmin\SqlParser\Translator;
14
15/**
16 * `VALUES` keyword parser.
17 *
18 * @category   Keywords
19 *
20 * @license    https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
21 */
22class Array2d extends Component
23{
24    /**
25     * @param Parser     $parser  the parser that serves as context
26     * @param TokensList $list    the list of tokens that are being parsed
27     * @param array      $options parameters for parsing
28     *
29     * @return ArrayObj[]
30     */
31    public static function parse(Parser $parser, TokensList $list, array $options = array())
32    {
33        $ret = array();
34
35        /**
36         * The number of values in each set.
37         *
38         * @var int
39         */
40        $count = -1;
41
42        /**
43         * The state of the parser.
44         *
45         * Below are the states of the parser.
46         *
47         *      0 ----------------------[ array ]----------------------> 1
48         *
49         *      1 ------------------------[ , ]------------------------> 0
50         *      1 -----------------------[ else ]----------------------> (END)
51         *
52         * @var int
53         */
54        $state = 0;
55
56        for (; $list->idx < $list->count; ++$list->idx) {
57            /**
58             * Token parsed at this moment.
59             *
60             * @var Token
61             */
62            $token = $list->tokens[$list->idx];
63
64            // End of statement.
65            if ($token->type === Token::TYPE_DELIMITER) {
66                break;
67            }
68
69            // Skipping whitespaces and comments.
70            if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
71                continue;
72            }
73
74            // No keyword is expected.
75            if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
76                break;
77            }
78
79            if ($state === 0) {
80                if ($token->value === '(') {
81                    $arr = ArrayObj::parse($parser, $list, $options);
82                    $arrCount = count($arr->values);
83                    if ($count === -1) {
84                        $count = $arrCount;
85                    } elseif ($arrCount !== $count) {
86                        $parser->error(
87                            sprintf(
88                                Translator::gettext('%1$d values were expected, but found %2$d.'),
89                                $count,
90                                $arrCount
91                            ),
92                            $token
93                        );
94                    }
95                    $ret[] = $arr;
96                    $state = 1;
97                } else {
98                    break;
99                }
100            } elseif ($state === 1) {
101                if ($token->value === ',') {
102                    $state = 0;
103                } else {
104                    break;
105                }
106            }
107        }
108
109        if ($state === 0) {
110            $parser->error(
111                'An opening bracket followed by a set of values was expected.',
112                $list->tokens[$list->idx]
113            );
114        }
115
116        --$list->idx;
117
118        return $ret;
119    }
120
121    /**
122     * @param ArrayObj[] $component the component to be built
123     * @param array      $options   parameters for building
124     *
125     * @return string
126     */
127    public static function build($component, array $options = array())
128    {
129        return ArrayObj::build($component);
130    }
131}
132