Passed
Pull Request — master (#315)
by Théo
02:34
created

Sequence::__construct()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 11
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Annotation;
16
17
use Doctrine\Common\Annotations\DocLexer;
18
use KevinGH\Box\Annotation\Exception\Exception;
19
use KevinGH\Box\Annotation\Exception\InvalidArgumentException;
20
use KevinGH\Box\Annotation\Exception\UnexpectedTokenException;
21
22
/**
23
 * An extension of Tokens that performs sequence validation.
24
 *
25
 * The Tokens class will only validate individual tokens. This class extends
26
 * that functionality, and also validates the order in which the tokens are
27
 * used. This can be considered the equivalent of performing a syntax check
28
 * on a docblock, but after it has been parsed.
29
 *
30
 * This class is probably only really useful for debugging, or if you are
31
 * using tokens that did not come directly from the tokenizer. If you are
32
 * using tokens directly from the tokenizer, there is no need to use this
33
 * class over the Tokens class.
34
 *
35
 * @author Kevin Herrera <[email protected]>
36
 */
37
class Sequence extends Tokens
38
{
39
    /**
40
     * The list of valid sequences for a token.
41
     *
42
     * @var array
43
     */
44
    private static $sequences = [
45
        DocLexer::T_AT => [
46
            DocLexer::T_CLOSE_PARENTHESIS => true,
47
            DocLexer::T_COLON => true,
48
            DocLexer::T_COMMA => true,
49
            DocLexer::T_EQUALS => true,
50
            DocLexer::T_IDENTIFIER => DocLexer::T_AT,
51
            DocLexer::T_OPEN_CURLY_BRACES => true,
52
            DocLexer::T_OPEN_PARENTHESIS => true,
53
        ],
54
        DocLexer::T_CLOSE_CURLY_BRACES => [
55
            DocLexer::T_CLOSE_CURLY_BRACES => true,
56
            DocLexer::T_CLOSE_PARENTHESIS => true,
57
            DocLexer::T_COMMA => true,
58
            DocLexer::T_FALSE => true,
59
            DocLexer::T_FLOAT => true,
60
            DocLexer::T_IDENTIFIER => true,
61
            DocLexer::T_INTEGER => true,
62
            DocLexer::T_NULL => true,
63
            DocLexer::T_OPEN_CURLY_BRACES => true,
64
            DocLexer::T_STRING => true,
65
            DocLexer::T_TRUE => true,
66
        ],
67
        DocLexer::T_CLOSE_PARENTHESIS => [
68
            DocLexer::T_CLOSE_CURLY_BRACES => true,
69
            DocLexer::T_CLOSE_PARENTHESIS => true,
70
            DocLexer::T_COMMA => true,
71
            DocLexer::T_FALSE => true,
72
            DocLexer::T_FLOAT => true,
73
            DocLexer::T_IDENTIFIER => true,
74
            DocLexer::T_INTEGER => true,
75
            DocLexer::T_NULL => true,
76
            DocLexer::T_OPEN_PARENTHESIS => true,
77
            DocLexer::T_STRING => true,
78
            DocLexer::T_TRUE => true,
79
        ],
80
        DocLexer::T_COLON => [
81
            DocLexer::T_IDENTIFIER => true,
82
            DocLexer::T_INTEGER => true,
83
            DocLexer::T_STRING => true,
84
        ],
85
        DocLexer::T_COMMA => [
86
            DocLexer::T_CLOSE_CURLY_BRACES => true,
87
            DocLexer::T_CLOSE_PARENTHESIS => true,
88
            DocLexer::T_FALSE => true,
89
            DocLexer::T_FLOAT => true,
90
            DocLexer::T_IDENTIFIER => true,
91
            DocLexer::T_INTEGER => true,
92
            DocLexer::T_NULL => true,
93
            DocLexer::T_STRING => true,
94
            DocLexer::T_TRUE => true,
95
        ],
96
        DocLexer::T_EQUALS => [
97
            DocLexer::T_IDENTIFIER => true,
98
            DocLexer::T_INTEGER => true,
99
            DocLexer::T_STRING => true,
100
        ],
101
        DocLexer::T_FALSE => [
102
            DocLexer::T_COLON => true,
103
            DocLexer::T_COMMA => true,
104
            DocLexer::T_EQUALS => true,
105
            DocLexer::T_OPEN_CURLY_BRACES => true,
106
            DocLexer::T_OPEN_PARENTHESIS => true,
107
        ],
108
        DocLexer::T_FLOAT => [
109
            DocLexer::T_COLON => true,
110
            DocLexer::T_COMMA => true,
111
            DocLexer::T_EQUALS => true,
112
            DocLexer::T_OPEN_CURLY_BRACES => true,
113
            DocLexer::T_OPEN_PARENTHESIS => true,
114
        ],
115
        DocLexer::T_IDENTIFIER => [
116
            DocLexer::T_AT => true,
117
            DocLexer::T_COLON => true,
118
            DocLexer::T_COMMA => true,
119
            DocLexer::T_EQUALS => true,
120
            DocLexer::T_OPEN_CURLY_BRACES => true,
121
            DocLexer::T_OPEN_PARENTHESIS => true,
122
        ],
123
        DocLexer::T_INTEGER => [
124
            DocLexer::T_COLON => true,
125
            DocLexer::T_COMMA => true,
126
            DocLexer::T_EQUALS => true,
127
            DocLexer::T_OPEN_CURLY_BRACES => true,
128
            DocLexer::T_OPEN_PARENTHESIS => true,
129
        ],
130
        DocLexer::T_NAMESPACE_SEPARATOR => false,
131
        DocLexer::T_NONE => false,
132
        DocLexer::T_NULL => [
133
            DocLexer::T_COLON => true,
134
            DocLexer::T_COMMA => true,
135
            DocLexer::T_EQUALS => true,
136
            DocLexer::T_OPEN_CURLY_BRACES => true,
137
            DocLexer::T_OPEN_PARENTHESIS => true,
138
        ],
139
        DocLexer::T_OPEN_CURLY_BRACES => [
140
            DocLexer::T_COLON => true,
141
            DocLexer::T_COMMA => true,
142
            DocLexer::T_EQUALS => true,
143
            DocLexer::T_OPEN_CURLY_BRACES => true,
144
            DocLexer::T_OPEN_PARENTHESIS => true,
145
        ],
146
        DocLexer::T_OPEN_PARENTHESIS => [
147
            DocLexer::T_IDENTIFIER => DocLexer::T_AT,
148
        ],
149
        DocLexer::T_STRING => [
150
            DocLexer::T_COLON => true,
151
            DocLexer::T_COMMA => true,
152
            DocLexer::T_EQUALS => true,
153
            DocLexer::T_OPEN_CURLY_BRACES => true,
154
            DocLexer::T_OPEN_PARENTHESIS => true,
155
        ],
156
        DocLexer::T_TRUE => [
157
            DocLexer::T_COLON => true,
158
            DocLexer::T_COMMA => true,
159
            DocLexer::T_EQUALS => true,
160
            DocLexer::T_OPEN_CURLY_BRACES => true,
161
            DocLexer::T_OPEN_PARENTHESIS => true,
162
        ],
163
    ];
164
165
    /**
166
     * {@inheritdoc}
167
     *
168
     * @param array|Tokens $tokens the list of tokens
169
     *
170
     * @throws InvalidArgumentException if the $tokens argument is not either
171
     *                                  an array or instance of Tokens
172
     */
173
    public function __construct($tokens)
174
    {
175
        if ($tokens instanceof Tokens) {
176
            $tokens = $tokens->getArray();
177
        } elseif (!is_array($tokens)) {
0 ignored issues
show
introduced by
The condition is_array($tokens) is always true.
Loading history...
178
            throw InvalidArgumentException::create(
179
                'The $tokens argument must be an array or instance of Tokens.'
180
            );
181
        }
182
183
        parent::__construct($tokens);
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     *
189
     * @throws Exception
190
     * @throws UnexpectedTokenException if the token is not expected
191
     */
192
    public function current()
193
    {
194
        $token = parent::current();
195
        $offset = $this->key();
196
        $before = isset($this[$offset - 1])
197
                ? $this[$offset - 1]
198
                : null;
199
200
        // is it an unused token?
201
        if (false === self::$sequences[$token[0]]) {
202
            throw UnexpectedTokenException::create(
203
                'Token #%d (%d) is not used by this library.',
204
                $offset,
205
                $token[0]
206
            );
207
208
            // not in the list of expected tokens?
209
        }
210
        if ((empty($before) && (DocLexer::T_AT !== $token[0]))
211
            || ($before && !isset(self::$sequences[$token[0]][$before[0]]))) {
212
            throw UnexpectedTokenException::create(
213
                'Token #%d (%d) is not expected here.',
214
                $offset,
215
                $token[0]
216
            );
217
218
            // before token has another before requirement?
219
        }
220
        if (isset(self::$sequences[$token[0]][$before[0]])
221
            && (true !== self::$sequences[$token[0]][$before[0]])) {
222
            $ancestor = isset($this[$offset - 2])
223
                      ? $this[$offset - 2]
224
                      : null;
225
226
            if (!$ancestor
227
                || ($ancestor[0] !== self::$sequences[$token[0]][$before[0]])) {
228
                throw UnexpectedTokenException::create(
229
                    'Token #%d (%d) is not expected here.',
230
                    $offset,
231
                    $token[0]
232
                );
233
            }
234
        }
235
236
        return $token;
237
    }
238
}
239