@@ -27,6 +27,12 @@ final class TypeResolver
27
27
/** @var string Definition of the NAMESPACE operator in PHP */
28
28
const OPERATOR_NAMESPACE = '\\' ;
29
29
30
+ /** @var integer the iterator parser is inside a compound context */
31
+ const PARSER_IN_COMPOUND = 0 ;
32
+
33
+ /** @var integer the iterator parser is inside a nullable expression context */
34
+ const PARSER_IN_NULLABLE = 1 ;
35
+
30
36
/** @var string[] List of recognized keywords and unto which Value Object they map */
31
37
private $ keywords = array (
32
38
'string ' => Types \String_::class,
@@ -84,7 +90,7 @@ public function __construct(FqsenResolver $fqsenResolver = null)
84
90
* @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be
85
91
* replaced with another namespace.
86
92
*
87
- * @return Type|null
93
+ * @return Type
88
94
*/
89
95
public function resolve ($ type , Context $ context = null )
90
96
{
@@ -103,13 +109,83 @@ public function resolve($type, Context $context = null)
103
109
$ context = new Context ('' );
104
110
}
105
111
112
+ // split the type string into tokens `|`, `?` and type names
113
+ $ tokens = preg_split ('/(\||\?)/ ' , $ type , -1 , PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
114
+ $ tokenIterator = new \ArrayIterator ($ tokens );
115
+ return $ this ->parseTypes ($ tokenIterator , $ context , self ::PARSER_IN_COMPOUND );
116
+ }
117
+
118
+ /**
119
+ * Analyse each tokens and creates types
120
+ *
121
+ * @param \ArrayIterator $tokens the iterator on tokens
122
+ * @param Context $context
123
+ * @param integer $parserContext on of self::PARSER_* constants, indicating
124
+ * the context where we are in the parsing
125
+ *
126
+ * @return Type
127
+ */
128
+ private function parseTypes (\ArrayIterator $ tokens , Context $ context , $ parserContext )
129
+ {
130
+ $ types = array ();
131
+ $ token = '' ;
132
+ while ($ tokens ->valid ()) {
133
+ $ token = $ tokens ->current ();
134
+ if ($ parserContext === self ::PARSER_IN_COMPOUND && $ token == '| ' ) {
135
+ if (count ($ types ) == 0 ) {
136
+ throw new \RuntimeException (
137
+ 'A type is missing before a type separator '
138
+ );
139
+ }
140
+ $ tokens ->next ();
141
+ } else if ($ parserContext === self ::PARSER_IN_COMPOUND
142
+ && $ token == '? '
143
+ ) {
144
+ $ tokens ->next ();
145
+ $ type = $ this ->parseTypes ($ tokens , $ context , self ::PARSER_IN_NULLABLE );
146
+ $ types [] = new Nullable ($ type );
147
+ } else {
148
+ $ type = $ this ->resolveSingleType ($ token , $ context );
149
+ $ tokens ->next ();
150
+ if ($ parserContext === self ::PARSER_IN_NULLABLE ) {
151
+ return $ type ;
152
+ }
153
+ $ types [] = $ type ;
154
+ }
155
+ }
156
+
157
+ if ($ token == '| ' ) {
158
+ throw new \RuntimeException (
159
+ 'A type is missing after a type separator '
160
+ );
161
+ }
162
+ if (count ($ types ) == 0 ) {
163
+ if ($ parserContext == self ::PARSER_IN_NULLABLE ) {
164
+ throw new \RuntimeException (
165
+ 'A type is missing after a nullable character '
166
+ );
167
+ }
168
+ throw new \RuntimeException (
169
+ 'No types in a compound list '
170
+ );
171
+ } else if (count ($ types ) == 1 ) {
172
+ return $ types [0 ];
173
+ }
174
+ return new Compound ($ types );
175
+ }
176
+
177
+ /**
178
+ * resolve the given type into a type object
179
+ *
180
+ * @param string $type the type string, representing a single type
181
+ * @param Context $context
182
+ * @return Type|Array_|Object_
183
+ */
184
+ private function resolveSingleType ($ type , Context $ context )
185
+ {
106
186
switch (true ) {
107
- case $ this ->isNullableType ($ type ):
108
- return $ this ->resolveNullableType ($ type , $ context );
109
187
case $ this ->isKeyword ($ type ):
110
188
return $ this ->resolveKeyword ($ type );
111
- case ($ this ->isCompoundType ($ type )):
112
- return $ this ->resolveCompoundType ($ type , $ context );
113
189
case $ this ->isTypedArray ($ type ):
114
190
return $ this ->resolveTypedArray ($ type , $ context );
115
191
case $ this ->isFqsen ($ type ):
@@ -200,30 +276,6 @@ private function isFqsen($type)
200
276
return strpos ($ type , self ::OPERATOR_NAMESPACE ) === 0 ;
201
277
}
202
278
203
- /**
204
- * Tests whether the given type is a compound type (i.e. `string|int`).
205
- *
206
- * @param string $type
207
- *
208
- * @return bool
209
- */
210
- private function isCompoundType ($ type )
211
- {
212
- return strpos ($ type , '| ' ) !== false ;
213
- }
214
-
215
- /**
216
- * Test whether the given type is a nullable type (i.e. `?string`)
217
- *
218
- * @param string $type
219
- *
220
- * @return bool
221
- */
222
- private function isNullableType ($ type )
223
- {
224
- return $ type [0 ] === '? ' ;
225
- }
226
-
227
279
/**
228
280
* Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set.
229
281
*
@@ -234,7 +286,7 @@ private function isNullableType($type)
234
286
*/
235
287
private function resolveTypedArray ($ type , Context $ context )
236
288
{
237
- return new Array_ ($ this ->resolve (substr ($ type , 0 , -2 ), $ context ));
289
+ return new Array_ ($ this ->resolveSingleType (substr ($ type , 0 , -2 ), $ context ));
238
290
}
239
291
240
292
/**
@@ -263,36 +315,4 @@ private function resolveTypedObject($type, Context $context = null)
263
315
{
264
316
return new Object_ ($ this ->fqsenResolver ->resolve ($ type , $ context ));
265
317
}
266
-
267
- /**
268
- * Resolves a compound type (i.e. `string|int`) into the appropriate Type objects or FQSEN.
269
- *
270
- * @param string $type
271
- * @param Context $context
272
- *
273
- * @return Compound
274
- */
275
- private function resolveCompoundType ($ type , Context $ context )
276
- {
277
- $ types = [];
278
-
279
- foreach (explode ('| ' , $ type ) as $ part ) {
280
- $ types [] = $ this ->resolve ($ part , $ context );
281
- }
282
-
283
- return new Compound ($ types );
284
- }
285
-
286
- /**
287
- * Resolve nullable types (i.e. `?string`) into a Nullable type wrapper
288
- *
289
- * @param string $type
290
- * @param Context $context
291
- *
292
- * @return Nullable
293
- */
294
- private function resolveNullableType ($ type , Context $ context )
295
- {
296
- return new Nullable ($ this ->resolve (ltrim ($ type , '? ' ), $ context ));
297
- }
298
318
}
0 commit comments