@@ -294,6 +294,7 @@ public function testAttributeAndLineComment()
294294 * @param string $testMarker The comment which prefaces the target token in the test file.
295295 * @param int $position The token position (starting from T_FUNCTION) of T_ATTRIBUTE token.
296296 * @param int $length The number of tokens between opener and closer.
297+ * @param array $tokenCodes The codes of tokens inside the attributes.
297298 *
298299 * @dataProvider dataAttributeOnParameters
299300 *
@@ -303,7 +304,7 @@ public function testAttributeAndLineComment()
303304 *
304305 * @return void
305306 */
306- public function testAttributeOnParameters ($ testMarker , $ position , $ length )
307+ public function testAttributeOnParameters ($ testMarker , $ position , $ length, array $ tokenCodes )
307308 {
308309 $ tokens = self ::$ phpcsFile ->getTokens ();
309310
@@ -312,6 +313,7 @@ public function testAttributeOnParameters($testMarker, $position, $length)
312313
313314 $ this ->assertSame (T_ATTRIBUTE , $ tokens [$ attribute ]['code ' ]);
314315 $ this ->assertArrayHasKey ('attribute_closer ' , $ tokens [$ attribute ]);
316+
315317 $ this ->assertSame (($ attribute + $ length ), $ tokens [$ attribute ]['attribute_closer ' ]);
316318
317319 $ closer = $ tokens [$ attribute ]['attribute_closer ' ];
@@ -322,6 +324,18 @@ public function testAttributeOnParameters($testMarker, $position, $length)
322324 $ this ->assertSame (T_VARIABLE , $ tokens [($ closer + 4 )]['code ' ]);
323325 $ this ->assertSame ('$param ' , $ tokens [($ closer + 4 )]['content ' ]);
324326
327+ $ map = array_map (
328+ function ($ token ) use ($ attribute , $ length ) {
329+ $ this ->assertArrayHasKey ('attribute_closer ' , $ token );
330+ $ this ->assertSame (($ attribute + $ length ), $ token ['attribute_closer ' ]);
331+
332+ return $ token ['code ' ];
333+ },
334+ array_slice ($ tokens , ($ attribute + 1 ), ($ length - 1 ))
335+ );
336+
337+ $ this ->assertSame ($ tokenCodes , $ map );
338+
325339 }//end testAttributeOnParameters()
326340
327341
@@ -339,16 +353,42 @@ public function dataAttributeOnParameters()
339353 '/* testSingleAttributeOnParameter */ ' ,
340354 4 ,
341355 2 ,
356+ [T_STRING ],
342357 ],
343358 [
344359 '/* testMultipleAttributesOnParameter */ ' ,
345360 4 ,
346361 10 ,
362+ [
363+ T_STRING ,
364+ T_COMMA ,
365+ T_WHITESPACE ,
366+ T_STRING ,
367+ T_OPEN_PARENTHESIS ,
368+ T_COMMENT ,
369+ T_WHITESPACE ,
370+ T_CONSTANT_ENCAPSED_STRING ,
371+ T_CLOSE_PARENTHESIS ,
372+ ],
347373 ],
348374 [
349375 '/* testMultilineAttributesOnParameter */ ' ,
350376 4 ,
351377 13 ,
378+ [
379+ T_WHITESPACE ,
380+ T_WHITESPACE ,
381+ T_STRING ,
382+ T_OPEN_PARENTHESIS ,
383+ T_WHITESPACE ,
384+ T_WHITESPACE ,
385+ T_CONSTANT_ENCAPSED_STRING ,
386+ T_WHITESPACE ,
387+ T_WHITESPACE ,
388+ T_CLOSE_PARENTHESIS ,
389+ T_WHITESPACE ,
390+ T_WHITESPACE ,
391+ ],
352392 ],
353393 ];
354394
@@ -376,4 +416,79 @@ public function testInvalidAttribute()
376416 }//end testInvalidAttribute()
377417
378418
419+ /**
420+ * Test that nested attributes are parsed correctly.
421+ *
422+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
423+ * @covers PHP_CodeSniffer\Tokenizers\PHP::findCloser
424+ * @covers PHP_CodeSniffer\Tokenizers\PHP::parsePhpAttribute
425+ *
426+ * @return void
427+ */
428+ public function testNestedAttributes ()
429+ {
430+ $ tokens = self ::$ phpcsFile ->getTokens ();
431+ $ tokenCodes = [
432+ T_STRING ,
433+ T_NS_SEPARATOR ,
434+ T_STRING ,
435+ T_OPEN_PARENTHESIS ,
436+ T_FN ,
437+ T_WHITESPACE ,
438+ T_OPEN_PARENTHESIS ,
439+ T_ATTRIBUTE ,
440+ T_STRING ,
441+ T_OPEN_PARENTHESIS ,
442+ T_CONSTANT_ENCAPSED_STRING ,
443+ T_CLOSE_PARENTHESIS ,
444+ T_ATTRIBUTE_END ,
445+ T_WHITESPACE ,
446+ T_VARIABLE ,
447+ T_CLOSE_PARENTHESIS ,
448+ T_WHITESPACE ,
449+ T_FN_ARROW ,
450+ T_WHITESPACE ,
451+ T_STRING_CAST ,
452+ T_WHITESPACE ,
453+ T_VARIABLE ,
454+ T_CLOSE_PARENTHESIS ,
455+ ];
456+
457+ $ attribute = $ this ->getTargetToken ('/* testNestedAttributes */ ' , T_ATTRIBUTE );
458+ $ this ->assertArrayHasKey ('attribute_closer ' , $ tokens [$ attribute ]);
459+
460+ $ closer = $ tokens [$ attribute ]['attribute_closer ' ];
461+ $ this ->assertSame (($ attribute + 24 ), $ closer );
462+
463+ $ this ->assertSame (T_ATTRIBUTE_END , $ tokens [$ closer ]['code ' ]);
464+
465+ $ this ->assertSame ($ tokens [$ attribute ]['attribute_opener ' ], $ tokens [$ closer ]['attribute_opener ' ]);
466+ $ this ->assertSame ($ tokens [$ attribute ]['attribute_closer ' ], $ tokens [$ closer ]['attribute_closer ' ]);
467+
468+ $ test = function (array $ tokens , $ length ) use ($ attribute ) {
469+ foreach ($ tokens as $ token ) {
470+ $ this ->assertArrayHasKey ('attribute_closer ' , $ token );
471+ $ this ->assertSame (($ attribute + $ length ), $ token ['attribute_closer ' ]);
472+ }
473+ };
474+
475+ $ test (array_slice ($ tokens , ($ attribute + 1 ), 7 ), 24 );
476+
477+ // Length here is 8 (nested attribute offset) + 5 (real length).
478+ $ test (array_slice ($ tokens , ($ attribute + 8 ), 6 ), 8 + 5 );
479+
480+ $ test (array_slice ($ tokens , ($ attribute + 14 ), 11 ), 24 );
481+
482+ $ map = array_map (
483+ static function ($ token ) {
484+ return $ token ['code ' ];
485+ },
486+ array_slice ($ tokens , ($ attribute + 1 ), 23 )
487+ );
488+
489+ $ this ->assertSame ($ tokenCodes , $ map );
490+
491+ }//end testNestedAttributes()
492+
493+
379494}//end class
0 commit comments