=> */ public static $phpReservedVars = [ '_SERVER' => true, '_GET' => true, '_POST' => true, '_REQUEST' => true, '_SESSION' => true, '_ENV' => true, '_COOKIE' => true, '_FILES' => true, 'GLOBALS' => true, 'http_response_header' => false, 'argc' => false, 'argv' => false, // Deprecated. 'php_errormsg' => false, // Removed PHP 5.4.0. 'HTTP_SERVER_VARS' => false, 'HTTP_GET_VARS' => false, 'HTTP_POST_VARS' => false, 'HTTP_SESSION_VARS' => false, 'HTTP_ENV_VARS' => false, 'HTTP_COOKIE_VARS' => false, 'HTTP_POST_FILES' => false, // Removed PHP 5.6.0. 'HTTP_RAW_POST_DATA' => false, ]; /** * Retrieve the visibility and implementation properties of a class member variable. * * Main differences with the PHPCS version: * - Removed the parse error warning for properties in interfaces. * This will now throw the same _"$stackPtr is not a class member var"_ runtime exception as * other non-property variables passed to the method. * - Defensive coding against incorrect calls to this method. * - Support PHP 8.0 identifier name tokens in property types, cross-version PHP & PHPCS. * - Support for the PHP 8.2 `true` type. * - The results of this function call are cached during a PHPCS run for faster response times. * * @see \PHP_CodeSniffer\Files\File::getMemberProperties() Original source. * @see \PHPCSUtils\BackCompat\BCFile::getMemberProperties() Cross-version compatible version of the original. * * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. * @param int $stackPtr The position in the stack of the `T_VARIABLE` token * to acquire the properties for. * * @return array Array with information about the class member variable. * The format of the return value is: * ```php * array( * 'scope' => string, // Public, private, or protected. * 'scope_specified' => boolean, // TRUE if the scope was explicitly specified. * 'is_static' => boolean, // TRUE if the static keyword was found. * 'is_readonly' => boolean, // TRUE if the readonly keyword was found. * 'type' => string, // The type of the var (empty if no type specified). * 'type_token' => integer|false, // The stack pointer to the start of the type * // or FALSE if there is no type. * 'type_end_token' => integer|false, // The stack pointer to the end of the type * // or FALSE if there is no type. * 'nullable_type' => boolean, // TRUE if the type is preceded by the * // nullability operator. * ); * ``` * * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a * `T_VARIABLE` token. * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified position is not a * class member variable. */ public static function getMemberProperties(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]) === false || $tokens[$stackPtr]['code'] !== \T_VARIABLE) { throw new RuntimeException('$stackPtr must be of type T_VARIABLE'); } if (Scopes::isOOProperty($phpcsFile, $stackPtr) === false) { throw new RuntimeException('$stackPtr is not a class member var'); } if (Cache::isCached($phpcsFile, __METHOD__, $stackPtr) === true) { return Cache::get($phpcsFile, __METHOD__, $stackPtr); } $valid = Collections::propertyModifierKeywords() + Tokens::$emptyTokens; $scope = 'public'; $scopeSpecified = false; $isStatic = false; $isReadonly = false; $startOfStatement = $phpcsFile->findPrevious( [ \T_SEMICOLON, \T_OPEN_CURLY_BRACKET, \T_CLOSE_CURLY_BRACKET, \T_ATTRIBUTE_END, ], ($stackPtr - 1) ); for ($i = ($startOfStatement + 1); $i < $stackPtr; $i++) { if (isset($valid[$tokens[$i]['code']]) === false) { break; } switch ($tokens[$i]['code']) { case \T_PUBLIC: $scope = 'public'; $scopeSpecified = true; break; case \T_PRIVATE: $scope = 'private'; $scopeSpecified = true; break; case \T_PROTECTED: $scope = 'protected'; $scopeSpecified = true; break; case \T_STATIC: $isStatic = true; break; case \T_READONLY: $isReadonly = true; break; } } $type = ''; $typeToken = false; $typeEndToken = false; $nullableType = false; $propertyTypeTokens = Collections::propertyTypeTokens(); /* * BC PHPCS < 3.x.x: The union type separator is not (yet) retokenized correctly * for union types containing the `true` type. */ $propertyTypeTokens[\T_BITWISE_OR] = \T_BITWISE_OR; if ($i < $stackPtr) { // We've found a type. for ($i; $i < $stackPtr; $i++) { if ($tokens[$i]['code'] === \T_VARIABLE) { // Hit another variable in a group definition. break; } if ($tokens[$i]['code'] === \T_NULLABLE) { $nullableType = true; } if (isset($propertyTypeTokens[$tokens[$i]['code']]) === true) { $typeEndToken = $i; if ($typeToken === false) { $typeToken = $i; } $type .= $tokens[$i]['content']; } } if ($type !== '' && $nullableType === true) { $type = '?' . $type; } } $returnValue = [ 'scope' => $scope, 'scope_specified' => $scopeSpecified, 'is_static' => $isStatic, 'is_readonly' => $isReadonly, 'type' => $type, 'type_token' => $typeToken, 'type_end_token' => $typeEndToken, 'nullable_type' => $nullableType, ]; Cache::set($phpcsFile, __METHOD__, $stackPtr, $returnValue); return $returnValue; } /** * Verify if a given variable name is the name of a PHP reserved variable. * * @see \PHPCSUtils\Utils\Variables::$phpReservedVars List of variables names reserved by PHP. * * @since 1.0.0 * * @param string $name The full variable name with or without leading dollar sign. * This allows for passing an array key variable name, such as * `'_GET'` retrieved from `$GLOBALS['_GET']`. * > Note: when passing an array key, string quotes are expected * to have been stripped already. * Also see: {@see \PHPCSUtils\Utils\TextStrings::stripQuotes()}. * * @return bool */ public static function isPHPReservedVarName($name) { if (\strpos($name, '$') === 0) { $name = \substr($name, 1); } return (isset(self::$phpReservedVars[$name]) === true); } /** * Verify if a given variable or array key token points to a PHP superglobal. * * @since 1.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found. * @param int $stackPtr The position in the stack of a `T_VARIABLE` * token or of the `T_CONSTANT_ENCAPSED_STRING` * array key to a variable in `$GLOBALS`. * * @return bool `TRUE` if this points to a superglobal; `FALSE` when not. * > Note: This includes returning `FALSE` when an unsupported token has * been passed, when a `T_CONSTANT_ENCAPSED_STRING` has been passed which * is not an array index key; or when it is, but is not an index to the * `$GLOBALS` variable. */ public static function isSuperglobal(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$stackPtr]) === false || ($tokens[$stackPtr]['code'] !== \T_VARIABLE && $tokens[$stackPtr]['code'] !== \T_CONSTANT_ENCAPSED_STRING) ) { return false; } $content = $tokens[$stackPtr]['content']; if ($tokens[$stackPtr]['code'] === \T_CONSTANT_ENCAPSED_STRING) { $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); $next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); if (($prev === false || $tokens[$prev]['code'] !== \T_OPEN_SQUARE_BRACKET) || ($next === false || $tokens[$next]['code'] !== \T_CLOSE_SQUARE_BRACKET) ) { // Not a single string array index key. return false; } $pprev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true); if ($pprev === false || $tokens[$pprev]['code'] !== \T_VARIABLE || $tokens[$pprev]['content'] !== '$GLOBALS' ) { // Not accessing the `$GLOBALS` array. return false; } // Strip quotes. $content = TextStrings::stripQuotes($content); } return self::isSuperglobalName($content); } /** * Verify if a given variable name is the name of a PHP superglobal. * * @since 1.0.0 * * @param string $name The full variable name with or without leading dollar sign. * This allows for passing an array key variable name, such as * `'_GET'` retrieved from `$GLOBALS['_GET']`. * > Note: when passing an array key, string quotes are expected * to have been stripped already. * Also see: {@see \PHPCSUtils\Utils\TextStrings::stripQuotes()}. * * @return bool */ public static function isSuperglobalName($name) { if (\strpos($name, '$') === 0) { $name = \substr($name, 1); } if (isset(self::$phpReservedVars[$name]) === false) { return false; } return self::$phpReservedVars[$name]; } }