@@ -1510,15 +1510,11 @@ protected function tokenize(string $code)
15101510 && strpos ($ token [1 ], '#[ ' ) === 0
15111511 ) {
15121512 $ subTokens = $ this ->parsePhpAttribute ($ tokens , $ stackPtr );
1513- if ($ subTokens !== null ) {
1514- array_splice ($ tokens , $ stackPtr , 1 , $ subTokens );
1515- $ numTokens = count ($ tokens );
1513+ array_splice ($ tokens , $ stackPtr , 1 , $ subTokens );
1514+ $ numTokens = count ($ tokens );
15161515
1517- $ tokenIsArray = true ;
1518- $ token = $ tokens [$ stackPtr ];
1519- } else {
1520- $ token [0 ] = T_ATTRIBUTE ;
1521- }
1516+ $ tokenIsArray = true ;
1517+ $ token = $ tokens [$ stackPtr ];
15221518 }
15231519
15241520 if ($ tokenIsArray === true
@@ -3984,11 +3980,10 @@ private function findCloser(array &$tokens, int $start, $openerTokens, string $c
39843980 * @param array $tokens The original array of tokens (as returned by token_get_all).
39853981 * @param int $stackPtr The current position in token array.
39863982 *
3987- * @return array|null The array of parsed attribute tokens
3983+ * @return array The array of parsed attribute tokens
39883984 */
39893985 private function parsePhpAttribute (array &$ tokens , int $ stackPtr )
39903986 {
3991-
39923987 $ token = $ tokens [$ stackPtr ];
39933988
39943989 $ commentBody = substr ($ token [1 ], 2 );
@@ -4000,18 +3995,24 @@ private function parsePhpAttribute(array &$tokens, int $stackPtr)
40003995 && strpos ($ subToken [1 ], '#[ ' ) === 0
40013996 ) {
40023997 $ reparsed = $ this ->parsePhpAttribute ($ subTokens , $ i );
4003- if ($ reparsed !== null ) {
4004- array_splice ($ subTokens , $ i , 1 , $ reparsed );
4005- } else {
4006- $ subToken [0 ] = T_ATTRIBUTE ;
4007- }
3998+ array_splice ($ subTokens , $ i , 1 , $ reparsed );
40083999 }
40094000 }
40104001
40114002 array_splice ($ subTokens , 0 , 1 , [[T_ATTRIBUTE , '#[ ' ]]);
40124003
40134004 // Go looking for the close bracket.
40144005 $ bracketCloser = $ this ->findCloser ($ subTokens , 1 , '[ ' , '] ' );
4006+
4007+ /*
4008+ * No closer bracket found, this might be a multi-line attribute,
4009+ * but it could also be an unfinished attribute (parse error).
4010+ *
4011+ * If it is a multi-line attribute, we need to grab a larger part of the code.
4012+ * If it is a parse error, we need to stick with only handling the line
4013+ * containing the attribute opener.
4014+ */
4015+
40154016 if (PHP_VERSION_ID < 80000 && $ bracketCloser === null ) {
40164017 foreach (array_slice ($ tokens , ($ stackPtr + 1 )) as $ token ) {
40174018 if (is_array ($ token ) === true ) {
@@ -4021,20 +4022,17 @@ private function parsePhpAttribute(array &$tokens, int $stackPtr)
40214022 }
40224023 }
40234024
4024- $ subTokens = @token_get_all ('<?php ' . $ commentBody );
4025- array_splice ($ subTokens , 0 , 1 , [[T_ATTRIBUTE , '#[ ' ]]);
4025+ $ newSubTokens = @token_get_all ('<?php ' . $ commentBody );
4026+ array_splice ($ newSubTokens , 0 , 1 , [[T_ATTRIBUTE , '#[ ' ]]);
40264027
4027- $ bracketCloser = $ this ->findCloser ($ subTokens , 1 , '[ ' , '] ' );
4028+ $ bracketCloser = $ this ->findCloser ($ newSubTokens , 1 , '[ ' , '] ' );
40284029 if ($ bracketCloser !== null ) {
4029- array_splice ($ tokens , ($ stackPtr + 1 ), count ($ tokens ), array_slice ($ subTokens , ($ bracketCloser + 1 )));
4030- $ subTokens = array_slice ($ subTokens , 0 , ($ bracketCloser + 1 ));
4030+ // We found the closer, overwrite the original $subTokens array.
4031+ array_splice ($ tokens , ($ stackPtr + 1 ), count ($ tokens ), array_slice ($ newSubTokens , ($ bracketCloser + 1 )));
4032+ $ subTokens = array_slice ($ newSubTokens , 0 , ($ bracketCloser + 1 ));
40314033 }
40324034 }
40334035
4034- if ($ bracketCloser === null ) {
4035- return null ;
4036- }
4037-
40384036 return $ subTokens ;
40394037 }
40404038
0 commit comments