From 43299a89463ef0d68cd74f9d3ed7a3b5a3cdd069 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 6 Apr 2025 05:15:03 +0200 Subject: [PATCH] PHP 7.4 | Tokenizer/PHP: handle PHP tag at end of file consistently Prior to PHP 7.4, a PHP open tag at the end of a file was not tokenized correctly in PHP itself. From the PHP 7.4 changelog: > ` interpreted as an opening PHP tag. Previously it was interpreted either as > ` interpreted as a literal `> "$GITHUB_OUTPUT" - elif [[ ${{ matrix.custom_ini }} == true && "${{ matrix.php }}" == '7.0' ]]; then + elif [[ ${{ matrix.custom_ini }} == true && "${{ matrix.php }}" != '5.5' ]]; then echo 'PHP_INI=error_reporting=-1, display_errors=On, date.timezone=Australia/Sydney, short_open_tag=On' >> "$GITHUB_OUTPUT" else echo 'PHP_INI=error_reporting=-1, display_errors=On' >> "$GITHUB_OUTPUT" @@ -233,6 +239,7 @@ jobs: PHP_CODESNIFFER_CBF: '1' - name: 'PHPCS: check code style without cache, no parallel' + if: ${{ matrix.custom_ini == false }} id: phpcs run: > php "bin/phpcs" --no-cache --parallel=1 diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php index 5c5434d5b1..32fefc9473 100644 --- a/src/Tokenizers/PHP.php +++ b/src/Tokenizers/PHP.php @@ -803,6 +803,46 @@ protected function tokenize($string) } }//end if + /* + Prior to PHP 7.4, PHP didn't support stand-alone PHP open tags at the end of a file + (without a new line), so we need to make sure that the tokenization in PHPCS is consistent + cross-version PHP by retokenizing to T_OPEN_TAG. + */ + + if (PHP_VERSION_ID < 70400 + && $tokenIsArray === true + // PHP < 7.4 with short open tags off. + && (($stackPtr === ($numTokens - 1) + && $token[0] === T_INLINE_HTML + && stripos($token[1], ' T_OPEN_TAG, + 'type' => 'T_OPEN_TAG', + 'content' => $token[1], + ]; + } else { + $finalTokens[$newStackPtr] = [ + 'code' => T_OPEN_TAG, + 'type' => 'T_OPEN_TAG', + 'content' => $token[1].$tokens[($stackPtr + 1)][1], + ]; + + $stackPtr++; + } + + $newStackPtr++; + continue; + }//end if + /* Parse doc blocks into something that can be easily iterated over. */ diff --git a/tests/Core/Tokenizers/PHP/PHPOpenTagEOF1Test.inc b/tests/Core/Tokenizers/PHP/PHPOpenTagEOF1Test.inc new file mode 100644 index 0000000000..2244b38915 --- /dev/null +++ b/tests/Core/Tokenizers/PHP/PHPOpenTagEOF1Test.inc @@ -0,0 +1,4 @@ + +phpcsFile->getTokens(); + $stackPtr = $this->getTargetToken('/* testLongOpenTagEndOfFileSpaceNoNewLine */', [T_OPEN_TAG, T_STRING, T_INLINE_HTML]); + + $this->assertSame( + T_OPEN_TAG, + $tokens[$stackPtr]['code'], + 'Token tokenized as '.Tokens::tokenName($tokens[$stackPtr]['code']).', not T_OPEN_TAG (code)' + ); + $this->assertSame( + 'T_OPEN_TAG', + $tokens[$stackPtr]['type'], + 'Token tokenized as '.$tokens[$stackPtr]['type'].', not T_OPEN_TAG (type)' + ); + $this->assertSame('assertArrayNotHasKey(($stackPtr + 1), $tokens); + + }//end testLongOpenTagAtEndOfFile() + + +}//end class diff --git a/tests/Core/Tokenizers/PHP/PHPOpenTagEOF2Test.inc b/tests/Core/Tokenizers/PHP/PHPOpenTagEOF2Test.inc new file mode 100644 index 0000000000..191bdae16f --- /dev/null +++ b/tests/Core/Tokenizers/PHP/PHPOpenTagEOF2Test.inc @@ -0,0 +1,4 @@ + +phpcsFile->getTokens(); + $stackPtr = $this->getTargetToken('/* testLongOpenTagEndOfFileNoSpaceNoNewLine */', [T_OPEN_TAG, T_STRING, T_INLINE_HTML]); + + $this->assertSame( + T_OPEN_TAG, + $tokens[$stackPtr]['code'], + 'Token tokenized as '.Tokens::tokenName($tokens[$stackPtr]['code']).', not T_OPEN_TAG (code)' + ); + $this->assertSame( + 'T_OPEN_TAG', + $tokens[$stackPtr]['type'], + 'Token tokenized as '.$tokens[$stackPtr]['type'].', not T_OPEN_TAG (type)' + ); + $this->assertSame('assertArrayNotHasKey(($stackPtr + 1), $tokens); + + }//end testLongOpenTagAtEndOfFile() + + +}//end class diff --git a/tests/Core/Tokenizers/PHP/PHPOpenTagEOF3Test.inc b/tests/Core/Tokenizers/PHP/PHPOpenTagEOF3Test.inc new file mode 100644 index 0000000000..583314db33 --- /dev/null +++ b/tests/Core/Tokenizers/PHP/PHPOpenTagEOF3Test.inc @@ -0,0 +1,4 @@ + +phpcsFile->getTokens(); + $stackPtr = $this->getTargetToken('/* testLongOpenTagEndOfFileNoSpaceNoNewLineUppercase */', [T_OPEN_TAG, T_STRING, T_INLINE_HTML]); + + $this->assertSame( + T_OPEN_TAG, + $tokens[$stackPtr]['code'], + 'Token tokenized as '.Tokens::tokenName($tokens[$stackPtr]['code']).', not T_OPEN_TAG (code)' + ); + $this->assertSame( + 'T_OPEN_TAG', + $tokens[$stackPtr]['type'], + 'Token tokenized as '.$tokens[$stackPtr]['type'].', not T_OPEN_TAG (type)' + ); + $this->assertSame('assertArrayNotHasKey(($stackPtr + 1), $tokens); + + }//end testLongOpenTagAtEndOfFile() + + +}//end class