Skip to content
This repository

Issue-109: Fixed infinite recursion on JMS Types that reference themselves or their parents #121

Merged
merged 1 commit into from about 1 year ago

2 participants

Boy Baukema William Durand
Boy Baukema

New pull request everything in one commit, I ran php-cs-fixer which seems fine, I ran unit tests which ran fine (after disabling the deprecation warnings from Symfony 2.1).

William Durand willdurand merged commit fe76b6d into from March 16, 2013
William Durand willdurand closed this March 16, 2013
William Durand
Collaborator

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Dec 20, 2012
Boy Baukema Fixed infinite recursion on JMS Types that reference themselves or th…
…eir parents.
fe76b6d
This page is out of date. Refresh to see the latest.
28  Parser/JmsMetadataParser.php
@@ -32,8 +32,6 @@ class JmsMetadataParser implements ParserInterface
32 32
      */
33 33
     private $commentExtractor;
34 34
 
35  
-    private $parsedClasses = array();
36  
-
37 35
     /**
38 36
      * Constructor, requires JMS Metadata factory
39 37
      */
@@ -63,10 +61,23 @@ public function supports($input)
63 61
      */
64 62
     public function parse($input)
65 63
     {
66  
-        $meta = $this->factory->getMetadataForClass($input);
  64
+        return $this->doParse($input);
  65
+    }
  66
+
  67
+    /**
  68
+     * Recursively parse all metadata for a class
  69
+     *
  70
+     * @param  string                    $className Class to get all metadata for
  71
+     * @param  array                     $visited   Classes we've already visited to prevent infinite recursion.
  72
+     * @return array                     metadata for given class
  73
+     * @throws \InvalidArgumentException
  74
+     */
  75
+    protected function doParse($className, $visited = array())
  76
+    {
  77
+        $meta = $this->factory->getMetadataForClass($className);
67 78
 
68 79
         if (null === $meta) {
69  
-            throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $input));
  80
+            throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $className));
70 81
         }
71 82
 
72 83
         $params = array();
@@ -82,23 +93,22 @@ public function parse($input)
82 93
                 $params[$name] = array(
83 94
                     'dataType' => $dataType['normalized'],
84 95
                     'required'      => false,   //TODO: can't think of a good way to specify this one, JMS doesn't have a setting for this
85  
-                    'description'   => $this->getDescription($input, $item),
  96
+                    'description'   => $this->getDescription($className, $item),
86 97
                     'readonly' => $item->readOnly
87 98
                 );
88 99
 
89 100
                 // if class already parsed, continue, to avoid infinite recursion
90  
-                if (in_array($dataType['class'], $this->parsedClasses)) {
  101
+                if (in_array($dataType['class'], $visited)) {
91 102
                     continue;
92 103
                 }
93 104
 
94 105
                 //check for nested classes with JMS metadata
95 106
                 if ($dataType['class'] && null !== $this->factory->getMetadataForClass($dataType['class'])) {
96  
-                    $this->parsedClasses[] = $dataType['class'];
97  
-                    $params[$name]['children'] = $this->parse($dataType['class']);
  107
+                    $visited[] = $dataType['class'];
  108
+                    $params[$name]['children'] = $this->doParse($dataType['class'], $visited);
98 109
                 }
99 110
             }
100 111
         }
101  
-        $this->parsedClasses = array();
102 112
 
103 113
         return $params;
104 114
     }
10  Tests/Fixtures/Model/JmsNested.php
@@ -26,4 +26,14 @@ class JmsNested
26 26
      */
27 27
     public $baz;
28 28
 
  29
+    /**
  30
+     * @JMS\Type("Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested");
  31
+     */
  32
+    public $circular;
  33
+
  34
+    /**
  35
+     * @JMS\Type("Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsTest");
  36
+     */
  37
+    public $parent;
  38
+
29 39
 }
46  Tests/Formatter/MarkdownFormatterTest.php
@@ -199,25 +199,53 @@ public function testFormat()
199 199
 
200 200
 With multiple lines.
201 201
 
202  
-nestedArray[]:
  202
+nested[circular]:
203 203
 
204  
-  * type: array of objects (JmsNested)
  204
+  * type: object (JmsNested)
  205
+  * required: false
  206
+  * description: No description.
  207
+
  208
+nested[parent]:
  209
+
  210
+  * type: object (JmsTest)
205 211
   * required: false
206 212
   * description: No description.
207 213
 
208  
-nestedArray[][bar]:
  214
+nested[parent][foo]:
209 215
 
210 216
   * type: string
211 217
   * required: false
212 218
   * description: No description.
213 219
 
214  
-nestedArray[][baz][]:
  220
+nested[parent][number]:
215 221
 
216  
-  * type: array of integers
  222
+  * type: double
217 223
   * required: false
218  
-  * description: Epic description.
  224
+  * description: No description.
219 225
 
220  
-With multiple lines.
  226
+nested[parent][arr]:
  227
+
  228
+  * type: array
  229
+  * required: false
  230
+  * description: No description.
  231
+
  232
+nested[parent][nested]:
  233
+
  234
+  * type: object (JmsNested)
  235
+  * required: false
  236
+  * description: No description.
  237
+
  238
+nested[parent][nestedArray][]:
  239
+
  240
+  * type: array of objects (JmsNested)
  241
+  * required: false
  242
+  * description: No description.
  243
+
  244
+nestedArray[]:
  245
+
  246
+  * type: array of objects (JmsNested)
  247
+  * required: false
  248
+  * description: No description.
221 249
 
222 250
 
223 251
 ### `GET` /jms-return-test ###
@@ -264,7 +292,7 @@ public function testFormat()
264 292
 
265 293
 **id**
266 294
 
267  
-  - Requirement: \d+
  295
+  - Requirement: \\d+
268 296
 
269 297
 
270 298
 ### `GET` /z-action-with-query-param ###
@@ -274,7 +302,7 @@ public function testFormat()
274 302
 
275 303
 page:
276 304
 
277  
-  * Requirement: \d+
  305
+  * Requirement: \\d+
278 306
   * Description: Page of the overview.
279 307
 
280 308
 
334  Tests/Formatter/SimpleFormatterTest.php
@@ -23,331 +23,431 @@ public function testFormat()
23 23
         $data      = $extractor->all();
24 24
         $result    = $container->get('nelmio_api_doc.formatter.simple_formatter')->format($data);
25 25
 
26  
-        $expected = array(
  26
+        $expected = array (
27 27
             '/tests' =>
28  
-            array(
  28
+            array (
29 29
                 0 =>
30  
-                array(
  30
+                array (
31 31
                     'method' => 'GET',
32 32
                     'uri' => '/tests.{_format}',
  33
+                    'description' => 'index action',
33 34
                     'filters' =>
34  
-                    array(
  35
+                    array (
35 36
                         'a' =>
36  
-                        array(
  37
+                        array (
37 38
                             'dataType' => 'integer',
38 39
                         ),
39 40
                         'b' =>
40  
-                        array(
  41
+                        array (
41 42
                             'dataType' => 'string',
42 43
                             'arbitrary' =>
43  
-                            array(
  44
+                            array (
44 45
                                 0 => 'arg1',
45 46
                                 1 => 'arg2',
46 47
                             ),
47 48
                         ),
48 49
                     ),
49  
-                    'description' => 'index action',
50  
-                    'requirements' => array(
51  
-                        '_format' => array('dataType' => '', 'description' => '', 'requirement' => ''),
  50
+                    'requirements' =>
  51
+                    array (
  52
+                        '_format' =>
  53
+                        array (
  54
+                            'requirement' => '',
  55
+                            'dataType' => '',
  56
+                            'description' => '',
  57
+                        ),
52 58
                     ),
53 59
                     'https' => false,
54 60
                 ),
55 61
                 1 =>
56  
-                array(
  62
+                array (
57 63
                     'method' => 'GET',
58 64
                     'uri' => '/tests.{_format}',
  65
+                    'description' => 'index action',
59 66
                     'filters' =>
60  
-                    array(
  67
+                    array (
61 68
                         'a' =>
62  
-                        array(
  69
+                        array (
63 70
                             'dataType' => 'integer',
64 71
                         ),
65 72
                         'b' =>
66  
-                        array(
  73
+                        array (
67 74
                             'dataType' => 'string',
68 75
                             'arbitrary' =>
69  
-                            array(
  76
+                            array (
70 77
                                 0 => 'arg1',
71 78
                                 1 => 'arg2',
72 79
                             ),
73 80
                         ),
74 81
                     ),
75  
-                    'description' => 'index action',
76  
-                    'requirements' => array(
77  
-                        '_format' => array('dataType' => '', 'description' => '', 'requirement' => ''),
  82
+                    'requirements' =>
  83
+                    array (
  84
+                        '_format' =>
  85
+                        array (
  86
+                            'requirement' => '',
  87
+                            'dataType' => '',
  88
+                            'description' => '',
  89
+                        ),
78 90
                     ),
79 91
                     'https' => false,
80 92
                 ),
81 93
                 2 =>
82  
-                array(
  94
+                array (
83 95
                     'method' => 'POST',
84 96
                     'uri' => '/tests.{_format}',
  97
+                    'description' => 'create test',
85 98
                     'parameters' =>
86  
-                    array(
  99
+                    array (
87 100
                         'a' =>
88  
-                        array(
  101
+                        array (
89 102
                             'dataType' => 'string',
90 103
                             'required' => true,
91 104
                             'description' => 'A nice description',
92  
-                            'readonly' => false
  105
+                            'readonly' => false,
93 106
                         ),
94 107
                         'b' =>
95  
-                        array(
  108
+                        array (
96 109
                             'dataType' => 'string',
97 110
                             'required' => false,
98 111
                             'description' => '',
99  
-                            'readonly' => false
  112
+                            'readonly' => false,
100 113
                         ),
101 114
                         'c' =>
102  
-                        array(
  115
+                        array (
103 116
                             'dataType' => 'boolean',
104 117
                             'required' => true,
105 118
                             'description' => '',
106  
-                            'readonly' => false
  119
+                            'readonly' => false,
107 120
                         ),
108 121
                     ),
109  
-                    'description' => 'create test',
110  
-                    'requirements' => array(
111  
-                        '_format' => array('dataType' => '', 'description' => '', 'requirement' => ''),
  122
+                    'requirements' =>
  123
+                    array (
  124
+                        '_format' =>
  125
+                        array (
  126
+                            'requirement' => '',
  127
+                            'dataType' => '',
  128
+                            'description' => '',
  129
+                        ),
112 130
                     ),
113 131
                     'https' => false,
114 132
                 ),
115 133
                 3 =>
116  
-                array(
  134
+                array (
117 135
                     'method' => 'POST',
118 136
                     'uri' => '/tests.{_format}',
  137
+                    'description' => 'create test',
119 138
                     'parameters' =>
120  
-                    array(
  139
+                    array (
121 140
                         'a' =>
122  
-                        array(
  141
+                        array (
123 142
                             'dataType' => 'string',
124 143
                             'required' => true,
125 144
                             'description' => 'A nice description',
126  
-                            'readonly' => false
  145
+                            'readonly' => false,
127 146
                         ),
128 147
                         'b' =>
129  
-                        array(
  148
+                        array (
130 149
                             'dataType' => 'string',
131 150
                             'required' => false,
132 151
                             'description' => '',
133  
-                            'readonly' => false
  152
+                            'readonly' => false,
134 153
                         ),
135 154
                         'c' =>
136  
-                        array(
  155
+                        array (
137 156
                             'dataType' => 'boolean',
138 157
                             'required' => true,
139 158
                             'description' => '',
140  
-                            'readonly' => false
  159
+                            'readonly' => false,
141 160
                         ),
142 161
                     ),
143  
-                    'description' => 'create test',
144  
-                    'requirements' => array(
145  
-                        '_format' => array('dataType' => '', 'description' => '', 'requirement' => ''),
  162
+                    'requirements' =>
  163
+                    array (
  164
+                        '_format' =>
  165
+                        array (
  166
+                            'requirement' => '',
  167
+                            'dataType' => '',
  168
+                            'description' => '',
  169
+                        ),
146 170
                     ),
147 171
                     'https' => false,
148 172
                 ),
149 173
             ),
150 174
             'others' =>
151  
-            array(
  175
+            array (
152 176
                 0 =>
153  
-                array(
  177
+                array (
154 178
                     'method' => 'POST',
155 179
                     'uri' => '/another-post',
  180
+                    'description' => 'create another test',
156 181
                     'parameters' =>
157  
-                    array(
  182
+                    array (
158 183
                         'a' =>
159  
-                        array(
  184
+                        array (
160 185
                             'dataType' => 'string',
161 186
                             'required' => true,
162 187
                             'description' => 'A nice description',
163  
-                            'readonly' => false
  188
+                            'readonly' => false,
164 189
                         ),
165 190
                     ),
166  
-                    'description' => 'create another test',
167 191
                     'https' => false,
168 192
                 ),
169 193
                 1 =>
170  
-                array(
  194
+                array (
171 195
                     'method' => 'ANY',
172 196
                     'uri' => '/any',
173 197
                     'description' => 'Action without HTTP verb',
174 198
                     'https' => false,
175 199
                 ),
176 200
                 2 =>
177  
-                array(
  201
+                array (
178 202
                     'method' => 'ANY',
179 203
                     'uri' => '/any/{foo}',
  204
+                    'description' => 'Action without HTTP verb',
180 205
                     'requirements' =>
181  
-                    array(
182  
-                        'foo' => array('dataType' => '', 'description' => '', 'requirement' => ''),
  206
+                    array (
  207
+                        'foo' =>
  208
+                        array (
  209
+                            'requirement' => '',
  210
+                            'dataType' => '',
  211
+                            'description' => '',
  212
+                        ),
183 213
                     ),
184  
-                    'description' => 'Action without HTTP verb',
185 214
                     'https' => false,
186 215
                 ),
187 216
                 3 =>
188  
-                array(
  217
+                array (
189 218
                     'method' => 'POST',
190 219
                     'uri' => '/jms-input-test',
  220
+                    'description' => 'Testing JMS',
191 221
                     'parameters' =>
192  
-                    array(
  222
+                    array (
193 223
                         'foo' =>
194  
-                        array(
  224
+                        array (
195 225
                             'dataType' => 'string',
196 226
                             'required' => false,
197 227
                             'description' => 'No description.',
198  
-                            'readonly' => false
  228
+                            'readonly' => false,
199 229
                         ),
200 230
                         'bar' =>
201  
-                        array(
  231
+                        array (
202 232
                             'dataType' => 'DateTime',
203 233
                             'required' => false,
204 234
                             'description' => 'No description.',
205  
-                            'readonly' => true
  235
+                            'readonly' => true,
206 236
                         ),
207 237
                         'number' =>
208  
-                        array(
  238
+                        array (
209 239
                             'dataType' => 'double',
210 240
                             'required' => false,
211 241
                             'description' => 'No description.',
212  
-                            'readonly' => false
  242
+                            'readonly' => false,
213 243
                         ),
214 244
                         'arr' =>
215  
-                        array(
  245
+                        array (
216 246
                             'dataType' => 'array',
217 247
                             'required' => false,
218 248
                             'description' => 'No description.',
219  
-                            'readonly' => false
  249
+                            'readonly' => false,
220 250
                         ),
221  
-                        'nested' => array(
  251
+                        'nested' =>
  252
+                        array (
222 253
                             'dataType' => 'object (JmsNested)',
223 254
                             'required' => false,
224 255
                             'description' => 'No description.',
225 256
                             'readonly' => false,
226  
-                            'children' => array(
227  
-                                'foo' => array(
  257
+                            'children' =>
  258
+                            array (
  259
+                                'foo' =>
  260
+                                array (
228 261
                                     'dataType' => 'DateTime',
229 262
                                     'required' => false,
230 263
                                     'description' => 'No description.',
231 264
                                     'readonly' => true,
232 265
                                 ),
233  
-                                'bar' => array(
  266
+                                'bar' =>
  267
+                                array (
234 268
                                     'dataType' => 'string',
235 269
                                     'required' => false,
236 270
                                     'description' => 'No description.',
237 271
                                     'readonly' => false,
238 272
                                 ),
239  
-                                'baz' => array(
  273
+                                'baz' =>
  274
+                                array (
240 275
                                     'dataType' => 'array of integers',
241 276
                                     'required' => false,
242 277
                                     'description' => 'Epic description.
243 278
 
244 279
 With multiple lines.',
245 280
                                     'readonly' => false,
246  
-                                )
247  
-                            )
248  
-                        ),
249  
-                        'nestedArray' => array(
250  
-                            'dataType' => 'array of objects (JmsNested)',
251  
-                            'required' => false,
252  
-                            'description' => 'No description.',
253  
-                            'readonly' => false,
254  
-                            'children' => array(
255  
-                                'foo' => array(
256  
-                                    'dataType' => 'DateTime',
257  
-                                    'required' => false,
258  
-                                    'description' => 'No description.',
259  
-                                    'readonly' => true,
260 281
                                 ),
261  
-                                'bar' => array(
262  
-                                    'dataType' => 'string',
  282
+                                'circular' =>
  283
+                                array (
  284
+                                    'dataType' => 'object (JmsNested)',
263 285
                                     'required' => false,
264 286
                                     'description' => 'No description.',
265 287
                                     'readonly' => false,
266 288
                                 ),
267  
-                                'baz' => array(
268  
-                                    'dataType' => 'array of integers',
  289
+                                'parent' =>
  290
+                                array (
  291
+                                    'dataType' => 'object (JmsTest)',
269 292
                                     'required' => false,
270  
-                                    'description' => 'Epic description.
271  
-
272  
-With multiple lines.',
  293
+                                    'description' => 'No description.',
273 294
                                     'readonly' => false,
274  
-                                )
275  
-                            )
  295
+                                    'children' =>
  296
+                                    array (
  297
+                                        'foo' =>
  298
+                                        array (
  299
+                                            'dataType' => 'string',
  300
+                                            'required' => false,
  301
+                                            'description' => 'No description.',
  302
+                                            'readonly' => false,
  303
+                                        ),
  304
+                                        'bar' =>
  305
+                                        array (
  306
+                                            'dataType' => 'DateTime',
  307
+                                            'required' => false,
  308
+                                            'description' => 'No description.',
  309
+                                            'readonly' => true,
  310
+                                        ),
  311
+                                        'number' =>
  312
+                                        array (
  313
+                                            'dataType' => 'double',
  314
+                                            'required' => false,
  315
+                                            'description' => 'No description.',
  316
+                                            'readonly' => false,
  317
+                                        ),
  318
+                                        'arr' =>
  319
+                                        array (
  320
+                                            'dataType' => 'array',
  321
+                                            'required' => false,
  322
+                                            'description' => 'No description.',
  323
+                                            'readonly' => false,
  324
+                                        ),
  325
+                                        'nested' =>
  326
+                                        array (
  327
+                                            'dataType' => 'object (JmsNested)',
  328
+                                            'required' => false,
  329
+                                            'description' => 'No description.',
  330
+                                            'readonly' => false,
  331
+                                        ),
  332
+                                        'nestedArray' =>
  333
+                                        array (
  334
+                                            'dataType' => 'array of objects (JmsNested)',
  335
+                                            'required' => false,
  336
+                                            'description' => 'No description.',
  337
+                                            'readonly' => false,
  338
+                                        ),
  339
+                                    ),
  340
+                                ),
  341
+                            ),
  342
+                        ),
  343
+                        'nestedArray' =>
  344
+                        array (
  345
+                            'dataType' => 'array of objects (JmsNested)',
  346
+                            'required' => false,
  347
+                            'description' => 'No description.',
  348
+                            'readonly' => false,
276 349
                         ),
277 350
                     ),
278  
-                    'description' => 'Testing JMS',
279 351
                     'https' => false,
280 352
                 ),
281 353
                 4 =>
282  
-                array(
  354
+                array (
283 355
                     'method' => 'GET',
284 356
                     'uri' => '/jms-return-test',
285 357
                     'description' => 'Testing return',
286  
-                    'response' => array(
287  
-                        'a' => array(
  358
+                    'response' =>
  359
+                    array (
  360
+                        'a' =>
  361
+                        array (
288 362
                             'dataType' => 'string',
289 363
                             'required' => true,
290 364
                             'description' => 'A nice description',
291  
-                            'readonly' => false
292  
-                        )
  365
+                            'readonly' => false,
  366
+                        ),
293 367
                     ),
294 368
                     'https' => false,
295 369
                 ),
296 370
                 5 =>
297  
-                array(
  371
+                array (
298 372
                     'method' => 'ANY',
299 373
                     'uri' => '/my-commented/{id}/{page}',
  374
+                    'description' => 'This method is useful to test if the getDocComment works.',
  375
+                    'documentation' => 'This method is useful to test if the getDocComment works.
  376
+And, it supports multilines until the first \'@\' char.',
300 377
                     'requirements' =>
301  
-                    array(
302  
-                        'id' => array('dataType' => 'int', 'description' => 'A nice comment', 'requirement' => ''),
303  
-                        'page' => array('dataType' => 'int', 'description' => '', 'requirement' => ''),
  378
+                    array (
  379
+                        'id' =>
  380
+                        array (
  381
+                            'dataType' => 'int',
  382
+                            'description' => 'A nice comment',
  383
+                            'requirement' => '',
  384
+                        ),
  385
+                        'page' =>
  386
+                        array (
  387
+                            'dataType' => 'int',
  388
+                            'description' => '',
  389
+                            'requirement' => '',
  390
+                        ),
304 391
                     ),
305 392
                     'https' => false,
306  
-                    'description' => 'This method is useful to test if the getDocComment works.',
307  
-                    'documentation' => "This method is useful to test if the getDocComment works.\nAnd, it supports multilines until the first '@' char."
308 393
                 ),
309 394
                 6 =>
310  
-                array(
  395
+                array (
311 396
                     'method' => 'ANY',
312 397
                     'uri' => '/secure-route',
313  
-                    // 'description' => '[secureRouteAction description]',
314  
-                    // 'documentation' => '[secureRouteAction description]',
315  
-                    'requirements' => array(
316  
-                        '_scheme' => array(
  398
+                    'requirements' =>
  399
+                    array (
  400
+                        '_scheme' =>
  401
+                        array (
317 402
                             'requirement' => 'https',
318  
-                            'dataType' => null,
319  
-                            'description' => null,
  403
+                            'dataType' => '',
  404
+                            'description' => '',
320 405
                         ),
321 406
                     ),
322 407
                     'https' => true,
323 408
                 ),
324 409
                 7 =>
325  
-                array(
  410
+                array (
326 411
                     'method' => 'ANY',
327 412
                     'uri' => '/yet-another/{id}',
328 413
                     'requirements' =>
329  
-                    array(
330  
-                        'id' => array('dataType' => '', 'description' => '', 'requirement' => '\d+')
  414
+                    array (
  415
+                        'id' =>
  416
+                        array (
  417
+                            'requirement' => '\\d+',
  418
+                            'dataType' => '',
  419
+                            'description' => '',
  420
+                        ),
331 421
                     ),
332 422
                     'https' => false,
333 423
                 ),
334 424
                 8 =>
335  
-                array(
  425
+                array (
336 426
                     'method' => 'GET',
337 427
                     'uri' => '/z-action-with-query-param',
338 428
                     'filters' =>
339  
-                    array(
340  
-                        'page' => array('description' => 'Page of the overview.', 'requirement' => '\d+')
  429
+                    array (
  430
+                        'page' =>
  431
+                        array (
  432
+                            'requirement' => '\\d+',
  433
+                            'description' => 'Page of the overview.',
  434
+                        ),
341 435
                     ),
342 436
                     'https' => false,
343 437
                 ),
344 438
                 9 =>
345  
-                array(
  439
+                array (
346 440
                     'method' => 'POST',
347 441
                     'uri' => '/z-action-with-request-param',
348 442
                     'parameters' =>
349  
-                    array(
350  
-                        'param1' => array('description' => 'Param1 description.', 'required' => true, 'dataType' => 'string', 'readonly' => false)
  443
+                    array (
  444
+                        'param1' =>
  445
+                        array (
  446
+                            'required' => true,
  447
+                            'dataType' => 'string',
  448
+                            'description' => 'Param1 description.',
  449
+                            'readonly' => false,
  450
+                        ),
351 451
                     ),
352 452
                     'https' => false,
353 453
                 ),
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.