@@ -50,6 +50,11 @@ class RestXmlParser implements Parser
5050 */
5151 private $ imports = [];
5252
53+ /**
54+ * @var array<string, true>
55+ */
56+ private $ generatedFunctions = [];
57+
5358 public function __construct (NamespaceRegistry $ namespaceRegistry , RequirementsRegistry $ requirementsRegistry , TypeGenerator $ typeGenerator )
5459 {
5560 $ this ->namespaceRegistry = $ namespaceRegistry ;
@@ -61,6 +66,7 @@ public function generate(StructureShape $shape, bool $throwOnError = true): Pars
6166 {
6267 $ properties = [];
6368 $ this ->functions = [];
69+ $ this ->generatedFunctions = [];
6470 $ this ->imports = [];
6571 if (null !== $ payload = $ shape ->getPayload ()) {
6672 $ member = $ shape ->getMember ($ payload );
@@ -80,19 +86,10 @@ public function generate(StructureShape $shape, bool $throwOnError = true): Pars
8086 continue ;
8187 }
8288
83- if (!$ member ->isNullable () && !$ member ->isRequired ()) {
84- $ properties [] = strtr ('if (null !== $v = (PROPERTY_ACCESSOR)) {
85- $this->PROPERTY_NAME = $v;
86- } ' , [
87- 'PROPERTY_NAME ' => GeneratorHelper::normalizeName ($ member ->getName ()),
88- 'PROPERTY_ACCESSOR ' => $ this ->parseXmlElement ($ this ->getInputAccessor ('$data ' , $ member ), $ member ->getShape (), $ member ->isRequired (), false ),
89- ]);
90- } else {
91- $ properties [] = strtr ('$this->PROPERTY_NAME = PROPERTY_ACCESSOR; ' , [
92- 'PROPERTY_NAME ' => GeneratorHelper::normalizeName ($ member ->getName ()),
93- 'PROPERTY_ACCESSOR ' => $ this ->parseXmlElement ($ this ->getInputAccessor ('$data ' , $ member ), $ member ->getShape (), $ member ->isRequired (), false ),
94- ]);
95- }
89+ $ properties [] = strtr ('$this->PROPERTY_NAME = PROPERTY_ACCESSOR; ' , [
90+ 'PROPERTY_NAME ' => GeneratorHelper::normalizeName ($ member ->getName ()),
91+ 'PROPERTY_ACCESSOR ' => $ this ->parseXmlElement ($ this ->getInputAccessor ('$data ' , $ member ), $ member ->getShape (), $ member ->isRequired (), false ),
92+ ]);
9693 }
9794 }
9895
@@ -204,20 +201,35 @@ private function parseXmlElement(string $input, Shape $shape, bool $required, bo
204201
205202 private function parseXmlResponseStructure (StructureShape $ shape , string $ input , bool $ required ): string
206203 {
207- $ properties = [];
208- foreach ($ shape ->getMembers () as $ member ) {
209- $ properties [] = strtr ('PROPERTY_NAME => PROPERTY_ACCESSOR, ' , [
210- 'PROPERTY_NAME ' => var_export ($ member ->getName (), true ),
211- 'PROPERTY_ACCESSOR ' => $ this ->parseXmlElement ($ this ->getInputAccessor ($ input , $ member ), $ member ->getShape (), $ member ->isRequired (), true ),
212- ]);
204+ $ functionName = 'populateResult ' . ucfirst ($ shape ->getName ());
205+ if (!isset ($ this ->generatedFunctions [$ functionName ])) {
206+ // prevent recursion
207+ $ this ->generatedFunctions [$ functionName ] = true ;
208+
209+ $ properties = [];
210+ foreach ($ shape ->getMembers () as $ member ) {
211+ $ properties [] = strtr ('PROPERTY_NAME => PROPERTY_ACCESSOR, ' , [
212+ 'PROPERTY_NAME ' => var_export ($ member ->getName (), true ),
213+ 'PROPERTY_ACCESSOR ' => $ this ->parseXmlElement ($ this ->getInputAccessor ('$xml ' , $ member ), $ member ->getShape (), $ member ->isRequired (), true ),
214+ ]);
215+ }
216+
217+ $ body = 'return new CLASS_NAME([
218+ PROPERTIES
219+ ]); ' ;
220+
221+ $ className = $ this ->namespaceRegistry ->getObject ($ shape );
222+ $ this ->imports [] = $ className ;
223+
224+ $ this ->functions [$ functionName ] = $ this ->createPopulateMethod ($ functionName , strtr ($ body , [
225+ 'CLASS_NAME ' => $ className ->getName (),
226+ 'PROPERTIES ' => implode ("\n" , $ properties ),
227+ ]), $ shape );
213228 }
214229
215- return strtr ('REQUIRED new CLASS_NAME([
216- PROPERTIES
217- ]) ' , [
218- 'REQUIRED ' => $ required ? '' : '! ' . $ input . ' ? null : ' ,
219- 'CLASS_NAME ' => $ this ->namespaceRegistry ->getObject ($ shape )->getName (),
220- 'PROPERTIES ' => implode ("\n" , $ properties ),
230+ return strtr ($ required ? '$this->FUNCTION_NAME(INPUT) ' : '0 === INPUT->count() ? null : $this->FUNCTION_NAME(INPUT) ' , [
231+ 'INPUT ' => $ input ,
232+ 'FUNCTION_NAME ' => $ functionName ,
221233 ]);
222234 }
223235
@@ -227,7 +239,7 @@ private function parseXmlResponseString(string $input, bool $required): string
227239 return strtr ('(string) INPUT ' , ['INPUT ' => $ input ]);
228240 }
229241
230- return strtr ('($v = INPUT) ? (string) $v : null ' , ['INPUT ' => $ input ]);
242+ return strtr ('(null !== $v = INPUT[0] ) ? (string) $v : null ' , ['INPUT ' => $ input ]);
231243 }
232244
233245 private function parseXmlResponseInteger (string $ input , bool $ required ): string
@@ -236,7 +248,7 @@ private function parseXmlResponseInteger(string $input, bool $required): string
236248 return strtr ('(int) (string) INPUT ' , ['INPUT ' => $ input ]);
237249 }
238250
239- return strtr ('($v = INPUT) ? (int) (string) $v : null ' , ['INPUT ' => $ input ]);
251+ return strtr ('(null !== $v = INPUT[0] ) ? (int) (string) $v : null ' , ['INPUT ' => $ input ]);
240252 }
241253
242254 private function parseXmlResponseFloat (string $ input , bool $ required ): string
@@ -245,7 +257,7 @@ private function parseXmlResponseFloat(string $input, bool $required): string
245257 return strtr ('(float) (string) INPUT ' , ['INPUT ' => $ input ]);
246258 }
247259
248- return strtr ('($v = INPUT) ? (float) (string) $v : null ' , ['INPUT ' => $ input ]);
260+ return strtr ('(null !== $v = INPUT[0] ) ? (float) (string) $v : null ' , ['INPUT ' => $ input ]);
249261 }
250262
251263 private function parseXmlResponseBool (string $ input , bool $ required ): string
@@ -256,7 +268,7 @@ private function parseXmlResponseBool(string $input, bool $required): string
256268 return strtr ('filter_var((string) INPUT, FILTER_VALIDATE_BOOLEAN) ' , ['INPUT ' => $ input ]);
257269 }
258270
259- return strtr ('($v = INPUT) ? filter_var((string) $v, FILTER_VALIDATE_BOOLEAN) : null ' , ['INPUT ' => $ input ]);
271+ return strtr ('(null !== $v = INPUT[0] ) ? filter_var((string) $v, FILTER_VALIDATE_BOOLEAN) : null ' , ['INPUT ' => $ input ]);
260272 }
261273
262274 private function parseXmlResponseBlob (string $ input , bool $ required ): string
@@ -265,7 +277,7 @@ private function parseXmlResponseBlob(string $input, bool $required): string
265277 return strtr ('base64_decode((string) INPUT) ' , ['INPUT ' => $ input ]);
266278 }
267279
268- return strtr ('($v = INPUT) ? base64_decode((string) $v) : null ' , ['INPUT ' => $ input ]);
280+ return strtr ('(null !== $v = INPUT[0] ) ? base64_decode((string) $v) : null ' , ['INPUT ' => $ input ]);
269281 }
270282
271283 private function parseXmlResponseTimestamp (Shape $ shape , string $ input , bool $ required ): string
@@ -281,47 +293,52 @@ private function parseXmlResponseTimestamp(Shape $shape, string $input, bool $re
281293 }
282294
283295 if (!$ required ) {
284- $ body = '($v = INPUT) ? ' . strtr ($ body , ['INPUT ' => '$v ' ]) . ' : null ' ;
296+ $ body = '(null !== $v = INPUT[0] ) ? ' . strtr ($ body , ['INPUT ' => '$v ' ]) . ' : null ' ;
285297 }
286298
287299 return strtr ($ body , ['INPUT ' => $ input ]);
288300 }
289301
290302 private function parseXmlResponseList (ListShape $ shape , string $ input , bool $ required , bool $ inObject ): string
291303 {
292- $ shapeMember = $ shape ->getMember ();
293- if ($ shapeMember ->getShape () instanceof StructureShape) {
294- $ listAccessorRequired = true ;
295- $ body = '
296- $items = [];
297- foreach (INPUT_PROPERTY as $item) {
298- $items[] = LIST_ACCESSOR;
299- }
304+ $ functionName = 'populateResult ' . ucfirst ($ shape ->getName ());
305+ if (!isset ($ this ->generatedFunctions [$ functionName ])) {
306+ // prevent recursion
307+ $ this ->generatedFunctions [$ functionName ] = true ;
308+
309+ $ shapeMember = $ shape ->getMember ();
310+ if ($ shapeMember ->getShape () instanceof ListShape || $ shapeMember ->getShape () instanceof MapShape) {
311+ $ listAccessorRequired = false ;
312+ $ body = '
313+ $items = [];
314+ foreach (INPUT_PROPERTY as $item) {
315+ $a = LIST_ACCESSOR;
316+ if (null !== $a) {
317+ $items[] = $a;
318+ }
319+ }
300320
301- return $items;
302- ' ;
303- } else {
304- $ listAccessorRequired = false ;
305- $ body = '
306- $items = [];
307- foreach (INPUT_PROPERTY as $item) {
308- $a = LIST_ACCESSOR;
309- if (null !== $a) {
310- $items[] = $a;
321+ return $items;
322+ ' ;
323+ } else {
324+ $ listAccessorRequired = true ;
325+ $ body = '
326+ $items = [];
327+ foreach (INPUT_PROPERTY as $item) {
328+ $items[] = LIST_ACCESSOR;
311329 }
312- }
313330
314- return $items;
315- ' ;
316- }
331+ return $items;
332+ ' ;
333+ }
317334
318- $ functionName = ' populateResult ' . ucfirst ( $ shape -> getName ());
319- $ this -> functions [ $ functionName ] = $ this ->createPopulateMethod ( $ functionName , strtr ( $ body , [
320- ' LIST_ACCESSOR ' => $ this -> parseXmlElement ( ' $item ' , $ shapeMember ->getShape (), $ listAccessorRequired , $ inObject ),
321- ' INPUT_PROPERTY ' => $ shape-> isFlattened () ? ' $xml ' : ( ' $xml-> ' . ( $ shapeMember -> getLocationName () ? $ shapeMember -> getLocationName () : ' member ' )),
322- ]), $ shape );
335+ $ this -> functions [ $ functionName] = $ this -> createPopulateMethod ( $ functionName , strtr ( $ body , [
336+ ' LIST_ACCESSOR ' => $ this ->parseXmlElement ( ' $item ' , $ shapeMember -> getShape (), $ listAccessorRequired , $ inObject ),
337+ ' INPUT_PROPERTY ' => $ shape -> isFlattened () ? ' $xml ' : ( ' $xml-> ' . ( $ shapeMember ->getLocationName () ? $ shapeMember -> getLocationName () : ' member ' ) ),
338+ ]), $ shape);
339+ }
323340
324- return strtr ($ required ? '$this->FUNCTION_NAME(INPUT) ' : '! INPUT ? EMPTY : $this->FUNCTION_NAME(INPUT ) ' , [
341+ return strtr ($ required ? '$this->FUNCTION_NAME(INPUT) ' : '(0 === ($v = INPUT)->count()) ? EMPTY : $this->FUNCTION_NAME($v ) ' , [
325342 'EMPTY ' => !$ inObject ? '[] ' : 'null ' ,
326343 'INPUT ' => $ input ,
327344 'FUNCTION_NAME ' => $ functionName ,
@@ -330,26 +347,31 @@ private function parseXmlResponseList(ListShape $shape, string $input, bool $req
330347
331348 private function parseXmlResponseMap (MapShape $ shape , string $ input , bool $ required , bool $ inObject ): string
332349 {
333- $ shapeValue = $ shape ->getValue ();
334- $ body = '
335- $items = [];
336- foreach (INPUT as $item) {
337- $a = $item->MAP_VALUE;
338- $items[$item->MAP_KEY->__toString()] = MAP_ACCESSOR;
339- }
350+ $ functionName = 'populateResult ' . ucfirst ($ shape ->getName ());
351+ if (!isset ($ this ->generatedFunctions [$ functionName ])) {
352+ // prevent recursion
353+ $ this ->generatedFunctions [$ functionName ] = true ;
340354
341- return $items;
342- ' ;
355+ $ shapeValue = $ shape ->getValue ();
356+ $ body = '
357+ $items = [];
358+ foreach (INPUT as $item) {
359+ $a = $item->MAP_VALUE;
360+ $items[$item->MAP_KEY->__toString()] = MAP_ACCESSOR;
361+ }
343362
344- $ functionName = 'populateResult ' . ucfirst ($ shape ->getName ());
345- $ this ->functions [$ functionName ] = $ this ->createPopulateMethod ($ functionName , strtr ($ body , [
346- 'INPUT ' => $ shape ->isFlattened () ? '$xml ' : '$xml->entry ' ,
347- 'MAP_KEY ' => $ shape ->getKey ()->getLocationName () ?? 'key ' ,
348- 'MAP_VALUE ' => $ shape ->getValue ()->getLocationName () ?? 'value ' ,
349- 'MAP_ACCESSOR ' => $ this ->parseXmlElement ('$a ' , $ shapeValue ->getShape (), true , $ inObject ),
350- ]), $ shape );
351-
352- return strtr ($ required ? '$this->FUNCTION_NAME(INPUT) ' : '!INPUT ? EMPTY : $this->FUNCTION_NAME(INPUT) ' , [
363+ return $items;
364+ ' ;
365+
366+ $ this ->functions [$ functionName ] = $ this ->createPopulateMethod ($ functionName , strtr ($ body , [
367+ 'INPUT ' => $ shape ->isFlattened () ? '$xml ' : '$xml->entry ' ,
368+ 'MAP_KEY ' => $ shape ->getKey ()->getLocationName () ?? 'key ' ,
369+ 'MAP_VALUE ' => $ shape ->getValue ()->getLocationName () ?? 'value ' ,
370+ 'MAP_ACCESSOR ' => $ this ->parseXmlElement ('$a ' , $ shapeValue ->getShape (), true , $ inObject ),
371+ ]), $ shape );
372+ }
373+
374+ return strtr ($ required ? '$this->FUNCTION_NAME(INPUT) ' : '(0 === ($v = INPUT)->count()) ? EMPTY : $this->FUNCTION_NAME($v) ' , [
353375 'EMPTY ' => !$ inObject ? '[] ' : 'null ' ,
354376 'INPUT ' => $ input ,
355377 'FUNCTION_NAME ' => $ functionName ,
@@ -368,6 +390,7 @@ private function createPopulateMethod(string $functionName, string $body, Shape
368390
369391 [$ returnType , $ parameterType , $ memberClassNames ] = $ this ->typeGenerator ->getPhpType ($ shape );
370392 $ method
393+ ->setReturnType ($ returnType )
371394 ->setComment ('@return ' . $ parameterType );
372395 $ this ->imports = array_merge ($ this ->imports , $ memberClassNames );
373396
0 commit comments