123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- <?php
- /**
- * PHPCompatibility, an external standard for PHP_CodeSniffer.
- *
- * @package PHPCompatibility
- * @copyright 2012-2019 PHPCompatibility Contributors
- * @license https://opensource.org/licenses/LGPL-3.0 LGPL3
- * @link https://github.com/PHPCompatibility/PHPCompatibility
- */
- namespace PHPCompatibility\Sniffs\TextStrings;
- use PHPCompatibility\Sniff;
- use PHP_CodeSniffer_Exception as PHPCS_Exception;
- use PHP_CodeSniffer_File as File;
- /**
- * PHP 7.0 introduced a Unicode codepoint escape sequence.
- *
- * Strings containing a literal `\u{` followed by an invalid sequence will cause a
- * fatal error as of PHP 7.0.
- *
- * PHP version 7.0
- *
- * @link https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.unicode-codepoint-escape-syntax
- * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.strings.unicode-escapes
- * @link https://wiki.php.net/rfc/unicode_escape
- * @link https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.double
- *
- * @since 9.3.0
- */
- class NewUnicodeEscapeSequenceSniff extends Sniff
- {
- /**
- * Returns an array of tokens this test wants to listen for.
- *
- * @since 9.3.0
- *
- * @return array
- */
- public function register()
- {
- return array(
- \T_CONSTANT_ENCAPSED_STRING,
- \T_DOUBLE_QUOTED_STRING,
- \T_HEREDOC,
- );
- }
- /**
- * Processes this test, when one of its tokens is encountered.
- *
- * @since 9.3.0
- *
- * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
- * @param int $stackPtr The position of the current token in
- * the stack passed in $tokens.
- *
- * @return void
- */
- public function process(File $phpcsFile, $stackPtr)
- {
- $tokens = $phpcsFile->getTokens();
- // Check whether this is a single quoted or double quoted string.
- if ($tokens[$stackPtr]['code'] === \T_CONSTANT_ENCAPSED_STRING) {
- // Find the start of the - potentially multi-line - text string.
- $start = $stackPtr;
- for ($i = ($stackPtr - 1); $i >= 0; $i--) {
- if ($tokens[$i]['code'] === \T_WHITESPACE) {
- continue;
- }
- if ($tokens[$i]['code'] === \T_CONSTANT_ENCAPSED_STRING) {
- $start = $i;
- continue;
- }
- break;
- }
- try {
- $textString = $this->getCompleteTextString($phpcsFile, $start, false);
- } catch (PHPCS_Exception $e) {
- // Something went wrong determining the start of the text string.
- return;
- }
- $startQuote = $textString[0];
- $endQuote = substr($textString, -1);
- if (($startQuote === "'" && $endQuote === "'")
- || $startQuote !== $endQuote
- ) {
- // Single quoted string, not our concern.
- return;
- }
- }
- $content = $this->stripQuotes($tokens[$stackPtr]['content']);
- $count = preg_match_all('`(?<!\\\\)\\\\u\{([^}\n\r]*)(\})?`', $content, $matches, \PREG_SET_ORDER);
- if ($count === false || $count === 0) {
- return;
- }
- foreach ($matches as $match) {
- $valid = false; // If the close curly is missing, we have an incomplete escape sequence.
- if (isset($match[2])) {
- $valid = $this->isValidUnicodeEscapeSequence($match[1]);
- }
- if ($this->supportsBelow('5.6') === true && $valid === true) {
- $phpcsFile->addError(
- 'Unicode codepoint escape sequences are not supported in PHP 5.6 or earlier. Found: %s',
- $stackPtr,
- 'Found',
- array($match[0])
- );
- }
- if ($this->supportsAbove('7.0') === true && $valid === false) {
- $phpcsFile->addError(
- 'Strings containing a literal \u{ followed by an invalid unicode codepoint escape sequence will cause a fatal error in PHP 7.0 and above. Escape the leading backslash to prevent this. Found: %s',
- $stackPtr,
- 'Invalid',
- array($match[0])
- );
- }
- }
- }
- /**
- * Verify if the codepoint in a unicode escape sequence is valid.
- *
- * @since 9.3.0
- *
- * @param string $codepoint The codepoint as a string.
- *
- * @return bool
- */
- protected function isValidUnicodeEscapeSequence($codepoint)
- {
- if (trim($codepoint) === '') {
- return false;
- }
- // Check if it's a valid hex codepoint.
- if (preg_match('`^[0-9A-F]+$`iD', $codepoint, $match) !== 1) {
- return false;
- }
- if (hexdec($codepoint) > 1114111) {
- // Outside of the maximum permissable range.
- return false;
- }
- return true;
- }
- }
|