diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e32587624..83da62422a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,6 +92,12 @@ jobs: - php: '7.0' os: 'ubuntu-latest' custom_ini: true + - php: '8.0' + os: 'ubuntu-latest' + custom_ini: true + - php: '8.2' + os: 'ubuntu-latest' + custom_ini: true # yamllint disable-line rule:line-length name: "PHP: ${{ matrix.php }} ${{ matrix.custom_ini && ' with custom ini settings' || '' }}${{ matrix.libxml_minor && format( ' with libxml {0}', matrix.libxml_minor ) || '' }} (${{ matrix.os == 'ubuntu-latest' && 'Linux' || 'Win' }})" @@ -177,7 +183,7 @@ jobs: # Also turn on error_reporting to ensure all notices are shown. if [[ ${{ 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, asp_tags=On' >> "$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