1<?php
2
3/**
4 * The definition of a parameter of a function or procedure.
5 */
6
7namespace PhpMyAdmin\SqlParser\Components;
8
9use PhpMyAdmin\SqlParser\Component;
10use PhpMyAdmin\SqlParser\Context;
11use PhpMyAdmin\SqlParser\Parser;
12use PhpMyAdmin\SqlParser\Token;
13use PhpMyAdmin\SqlParser\TokensList;
14
15/**
16 * The definition of a parameter of a function or procedure.
17 *
18 * @category   Components
19 *
20 * @license    https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
21 */
22class ParameterDefinition extends Component
23{
24    /**
25     * The name of the new column.
26     *
27     * @var string
28     */
29    public $name;
30
31    /**
32     * Parameter's direction (IN, OUT or INOUT).
33     *
34     * @var string
35     */
36    public $inOut;
37
38    /**
39     * The data type of thew new column.
40     *
41     * @var DataType
42     */
43    public $type;
44
45    /**
46     * Constructor.
47     *
48     * @param string   $name  parameter's name
49     * @param string   $inOut parameter's directional type (IN / OUT or None)
50     * @param DataType $type  parameter's type
51     */
52    public function __construct($name = null, $inOut = null, $type = null)
53    {
54        $this->name = $name;
55        $this->inOut = $inOut;
56        $this->type = $type;
57    }
58
59    /**
60     * @param Parser     $parser  the parser that serves as context
61     * @param TokensList $list    the list of tokens that are being parsed
62     * @param array      $options parameters for parsing
63     *
64     * @return ParameterDefinition[]
65     */
66    public static function parse(Parser $parser, TokensList $list, array $options = array())
67    {
68        $ret = array();
69
70        $expr = new self();
71
72        /**
73         * The state of the parser.
74         *
75         * Below are the states of the parser.
76         *
77         *      0 -----------------------[ ( ]------------------------> 1
78         *
79         *      1 ----------------[ IN / OUT / INOUT ]----------------> 1
80         *      1 ----------------------[ name ]----------------------> 2
81         *
82         *      2 -------------------[ data type ]--------------------> 3
83         *
84         *      3 ------------------------[ , ]-----------------------> 1
85         *      3 ------------------------[ ) ]-----------------------> (END)
86         *
87         * @var int
88         */
89        $state = 0;
90
91        for (; $list->idx < $list->count; ++$list->idx) {
92            /**
93             * Token parsed at this moment.
94             *
95             * @var Token
96             */
97            $token = $list->tokens[$list->idx];
98
99            // End of statement.
100            if ($token->type === Token::TYPE_DELIMITER) {
101                break;
102            }
103
104            // Skipping whitespaces and comments.
105            if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
106                continue;
107            }
108
109            if ($state === 0) {
110                if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
111                    $state = 1;
112                }
113                continue;
114            } elseif ($state === 1) {
115                if (($token->value === 'IN') || ($token->value === 'OUT') || ($token->value === 'INOUT')) {
116                    $expr->inOut = $token->value;
117                    ++$list->idx;
118                } elseif ($token->value === ')') {
119                    ++$list->idx;
120                    break;
121                } else {
122                    $expr->name = $token->value;
123                    $state = 2;
124                }
125            } elseif ($state === 2) {
126                $expr->type = DataType::parse($parser, $list);
127                $state = 3;
128            } elseif ($state === 3) {
129                $ret[] = $expr;
130                $expr = new self();
131                if ($token->value === ',') {
132                    $state = 1;
133                } elseif ($token->value === ')') {
134                    ++$list->idx;
135                    break;
136                }
137            }
138        }
139
140        // Last iteration was not saved.
141        if (isset($expr->name) && ($expr->name !== '')) {
142            $ret[] = $expr;
143        }
144
145        --$list->idx;
146
147        return $ret;
148    }
149
150    /**
151     * @param ParameterDefinition[] $component the component to be built
152     * @param array                 $options   parameters for building
153     *
154     * @return string
155     */
156    public static function build($component, array $options = array())
157    {
158        if (is_array($component)) {
159            return '(' . implode(', ', $component) . ')';
160        }
161
162        $tmp = '';
163        if (! empty($component->inOut)) {
164            $tmp .= $component->inOut . ' ';
165        }
166
167        return trim(
168            $tmp . Context::escape($component->name) . ' ' . $component->type
169        );
170    }
171}
172