These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php declare(strict_types=1); |
||
2 | |||
3 | /** |
||
4 | * @copyright (c) 2019-present brian ridley |
||
5 | * @author brian ridley <[email protected]> |
||
6 | * @license http://opensource.org/licenses/MIT MIT |
||
7 | */ |
||
8 | |||
9 | namespace ptlis\SerializedDataEditor\Parser; |
||
10 | |||
11 | use ptlis\SerializedDataEditor\Type\ArrayType; |
||
12 | use ptlis\SerializedDataEditor\Type\BoolType; |
||
13 | use ptlis\SerializedDataEditor\Type\FloatType; |
||
14 | use ptlis\SerializedDataEditor\Type\IntegerType; |
||
15 | use ptlis\SerializedDataEditor\Type\NullType; |
||
16 | use ptlis\SerializedDataEditor\Type\ObjectCustomSerializedType; |
||
17 | use ptlis\SerializedDataEditor\Type\ObjectDefaultSerializedType; |
||
18 | use ptlis\SerializedDataEditor\Type\ReferenceType; |
||
19 | use ptlis\SerializedDataEditor\Type\StringType; |
||
20 | use ptlis\SerializedDataEditor\Type\Type; |
||
21 | use ptlis\SerializedDataEditor\TypeFragment\ArrayElementIntegerIndex; |
||
22 | use ptlis\SerializedDataEditor\TypeFragment\ArrayElementStringIndex; |
||
23 | use ptlis\SerializedDataEditor\TypeFragment\ObjectProperty; |
||
24 | |||
25 | final class Parser |
||
26 | { |
||
27 | /** |
||
28 | * @param Token[] $tokenList |
||
29 | * @return Type |
||
30 | */ |
||
31 | 15 | public function parse(array $tokenList): Type |
|
32 | { |
||
33 | 15 | return $this->internalParse($tokenList); |
|
34 | } |
||
35 | |||
36 | /** |
||
37 | * @param Token[] $tokenList |
||
38 | * @param int $tokenOffset Tracks offset when iterating through token list. |
||
39 | * @return Type |
||
40 | */ |
||
41 | 15 | private function internalParse(array $tokenList, int &$tokenOffset = 0): Type |
|
42 | { |
||
43 | // Handle simple types |
||
44 | 15 | if ($this->isSimpleType($tokenList[$tokenOffset])) { |
|
45 | 14 | $type = $this->parseSimple($tokenList[$tokenOffset], $tokenOffset); |
|
46 | |||
47 | // Handle complex types (arrays, objects) |
||
48 | } else { |
||
49 | 9 | $type = $this->parseComplex($tokenList, $tokenOffset); |
|
50 | } |
||
51 | |||
52 | 15 | return $type; |
|
53 | } |
||
54 | |||
55 | 15 | private function isSimpleType(Token $token): bool |
|
56 | { |
||
57 | 15 | return in_array( |
|
58 | 15 | $token->getType(), |
|
59 | [ |
||
60 | 15 | Token::NULL, |
|
61 | Token::BOOL, |
||
62 | Token::INTEGER, |
||
63 | Token::FLOAT, |
||
64 | Token::STRING, |
||
65 | Token::REFERENCE |
||
66 | ] |
||
67 | ); |
||
68 | } |
||
69 | |||
70 | /** |
||
71 | * Parse a simple (single token) type. |
||
72 | * |
||
73 | * @param Token $tokenList |
||
0 ignored issues
–
show
|
|||
74 | * @param int $tokenOffset Tracks offset when iterating through token list. |
||
75 | * @return Type |
||
76 | */ |
||
77 | 14 | public function parseSimple(Token $token, int &$tokenOffset): Type |
|
78 | { |
||
79 | 14 | switch ($token->getType()) { |
|
80 | 14 | case Token::NULL: |
|
81 | 1 | $type = new NullType(); |
|
82 | 1 | break; |
|
83 | |||
84 | 13 | case Token::BOOL: |
|
85 | 1 | $type = new BoolType('1' === $token->getValue() ? true : false); |
|
86 | 1 | break; |
|
87 | |||
88 | 12 | case Token::INTEGER: |
|
89 | 1 | $type = new IntegerType(intval($token->getValue())); |
|
90 | 1 | break; |
|
91 | |||
92 | 11 | case Token::FLOAT: |
|
93 | 1 | $type = new FloatType(floatval($token->getValue())); |
|
94 | 1 | break; |
|
95 | |||
96 | 10 | case Token::STRING: |
|
97 | 9 | $type = new StringType($token->getValue()); |
|
98 | 9 | break; |
|
99 | |||
100 | 1 | case Token::REFERENCE: |
|
101 | 1 | $type = new ReferenceType(intval($token->getValue())); |
|
102 | 1 | break; |
|
103 | |||
104 | default: |
||
105 | throw new \RuntimeException('Could not parse simple type "' . $token->getType() . '"'); |
||
106 | } |
||
107 | |||
108 | 14 | $tokenOffset++; |
|
109 | |||
110 | 14 | return $type; |
|
111 | } |
||
112 | |||
113 | /** |
||
114 | * Parse a complex (multi token) type. |
||
115 | * |
||
116 | * @param Token[] $tokenList |
||
117 | * @param int $tokenOffset Tracks offset when iterating through token list. |
||
118 | * @return Type |
||
119 | */ |
||
120 | 9 | private function parseComplex(array $tokenList, int &$tokenOffset): Type |
|
121 | { |
||
122 | 9 | switch ($tokenList[$tokenOffset]->getType()) { |
|
123 | 9 | case Token::ARRAY_START: |
|
124 | 6 | $type = $this->parseArray($tokenList, $tokenOffset); |
|
125 | 6 | break; |
|
126 | |||
127 | 6 | case Token::OBJECT_DEFAULT_NAME: |
|
128 | 4 | $type = $this->parseObjectDefaultSerialization($tokenList, $tokenOffset); |
|
129 | 4 | break; |
|
130 | |||
131 | 2 | case Token::OBJECT_CUSTOM_NAME: |
|
132 | 2 | $type = $this->parseObjectCustomSerialization($tokenList, $tokenOffset); |
|
133 | 2 | break; |
|
134 | |||
135 | default: |
||
136 | throw new \RuntimeException('Could not parse complex type "' . $tokenList[$tokenOffset]->getType() . '"'); |
||
137 | } |
||
138 | |||
139 | 9 | return $type; |
|
140 | } |
||
141 | |||
142 | /** |
||
143 | * Parse an array type. |
||
144 | * |
||
145 | * @param Token[] $tokenList |
||
146 | * @param int $tokenOffset Tracks offset when iterating through token list. |
||
147 | * @return Type |
||
148 | */ |
||
149 | 6 | private function parseArray(array $tokenList, int &$tokenOffset): Type |
|
150 | { |
||
151 | // Skip array open |
||
152 | 6 | $tokenOffset++; |
|
153 | |||
154 | /** @var Token $indexToken */ |
||
155 | 6 | $indexToken = null; |
|
156 | |||
157 | // Iterate through tokens array elements |
||
158 | 6 | $arrayElementList = []; |
|
159 | 6 | while ($tokenOffset < count($tokenList)) { |
|
160 | switch (true) { |
||
161 | // Do nothing, we're done here |
||
162 | 6 | case Token::COMPOUND_END === $tokenList[$tokenOffset]->getType(): |
|
163 | 5 | $tokenOffset++; |
|
164 | 5 | break; |
|
165 | |||
166 | // Array index |
||
167 | 6 | case is_null($indexToken): |
|
168 | 6 | $indexToken = $tokenList[$tokenOffset]; |
|
169 | 6 | $tokenOffset++; |
|
170 | 6 | break; |
|
171 | |||
172 | // Element value |
||
173 | 6 | case !is_null($indexToken): |
|
174 | 6 | $type = $this->internalParse($tokenList, $tokenOffset); |
|
175 | |||
176 | 6 | if (Token::INTEGER === $indexToken->getType()) { |
|
177 | 5 | $arrayElementList[] = new ArrayElementIntegerIndex(intval($indexToken->getValue()), $type); |
|
178 | } else { |
||
179 | 2 | $arrayElementList[] = new ArrayElementStringIndex($indexToken->getValue(), $type); |
|
180 | } |
||
181 | |||
182 | 6 | $indexToken = null; |
|
183 | 6 | break; |
|
184 | } |
||
185 | } |
||
186 | |||
187 | 6 | return new ArrayType($arrayElementList); |
|
188 | } |
||
189 | |||
190 | /** |
||
191 | * Parse a PHP-serialized object. |
||
192 | * |
||
193 | * @param Token[] $tokenList |
||
194 | * @param int $tokenOffset Tracks offset when iterating through token list. |
||
195 | * @return Type |
||
196 | */ |
||
197 | 4 | public function parseObjectDefaultSerialization(array $tokenList, int &$tokenOffset): Type |
|
198 | { |
||
199 | 4 | $className = $tokenList[$tokenOffset]->getValue(); |
|
200 | |||
201 | // Skip object open and property count |
||
202 | 4 | $tokenOffset += 2; |
|
203 | |||
204 | /** @var Token $propertyNameToken */ |
||
205 | 4 | $propertyNameToken = null; |
|
206 | |||
207 | // Iterate through tokens building object properties |
||
208 | 4 | $propertyList = []; |
|
209 | 4 | while ($tokenOffset < count($tokenList)) { |
|
210 | switch (true) { |
||
211 | // Do nothing, we're done here |
||
212 | 4 | case Token::COMPOUND_END === $tokenList[$tokenOffset]->getType(): |
|
213 | 3 | $tokenOffset++; |
|
214 | 3 | break; |
|
215 | |||
216 | // Property name |
||
217 | 4 | case is_null($propertyNameToken): |
|
218 | 4 | $propertyNameToken = $tokenList[$tokenOffset]; |
|
219 | 4 | $tokenOffset++; |
|
220 | 4 | break; |
|
221 | |||
222 | // Property Value |
||
223 | 4 | case !is_null($propertyNameToken): |
|
224 | 4 | $propertyList[] = $this->parseProperty( |
|
225 | 4 | $className, |
|
226 | 4 | $propertyNameToken, |
|
227 | 4 | $this->internalParse($tokenList, $tokenOffset) |
|
228 | ); |
||
229 | |||
230 | 4 | $propertyNameToken = null; |
|
231 | 4 | break; |
|
232 | } |
||
233 | } |
||
234 | |||
235 | 4 | return new ObjectDefaultSerializedType( |
|
236 | 4 | $className, |
|
237 | 4 | $propertyList |
|
238 | ); |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Parse a property of a PHP-serialized object. |
||
243 | * |
||
244 | * @param Token[] $tokenList |
||
0 ignored issues
–
show
There is no parameter named
$tokenList . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not.
Loading history...
|
|||
245 | * @param int $tokenOffset Tracks offset when iterating through token list. |
||
0 ignored issues
–
show
There is no parameter named
$tokenOffset . Was it maybe removed?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. Consider the following example. The parameter /**
* @param array $germany
* @param array $island
* @param array $italy
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was removed, but the annotation was not.
Loading history...
|
|||
246 | * @return Type |
||
247 | */ |
||
248 | 4 | private function parseProperty( |
|
249 | string $className, |
||
250 | Token $propertyNameToken, |
||
251 | Type $type |
||
252 | ): ObjectProperty { |
||
253 | |||
254 | // Default to public property |
||
255 | 4 | $visibility = ObjectProperty::PUBLIC; |
|
256 | 4 | $propertyName = $propertyNameToken->getValue(); |
|
257 | |||
258 | // Split property name on NUL character |
||
259 | 4 | $parts = array_values(array_filter(explode("\0", $propertyNameToken->getValue()))); |
|
260 | |||
261 | // Protected or private property |
||
262 | 4 | if (count($parts) > 1) { |
|
263 | 2 | $propertyName = $parts[1]; |
|
264 | 2 | $visibility = ObjectProperty::PROTECTED; |
|
265 | |||
266 | // Private property |
||
267 | 2 | if ($className === $parts[0]) { |
|
268 | 2 | $visibility = ObjectProperty::PRIVATE; |
|
269 | } |
||
270 | } |
||
271 | |||
272 | 4 | return new ObjectProperty( |
|
273 | 4 | $visibility, |
|
274 | 4 | $className, |
|
275 | 4 | $propertyName, |
|
276 | 4 | $type |
|
277 | ); |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * Parse a custom serialized object. |
||
282 | * |
||
283 | * @param Token[] $tokenList |
||
284 | * @param int $tokenOffset Tracks offset when iterating through token list. |
||
285 | * @return Type |
||
286 | */ |
||
287 | 2 | public function parseObjectCustomSerialization(array $tokenList, int &$tokenOffset): Type |
|
288 | { |
||
289 | 2 | $className = $tokenList[$tokenOffset]->getValue(); |
|
290 | 2 | $tokenOffset++; |
|
291 | |||
292 | 2 | $customSerializedData = $tokenList[$tokenOffset]->getValue(); |
|
293 | 2 | $tokenOffset++; |
|
294 | |||
295 | // Skip end |
||
296 | 2 | $tokenOffset++; |
|
297 | |||
298 | 2 | return new ObjectCustomSerializedType($className, $customSerializedData); |
|
299 | } |
||
300 | } |
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.
Consider the following example. The parameter
$italy
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was removed, but the annotation was not.