123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- <?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\FunctionNameRestrictions;
- use Generic_Sniffs_NamingConventions_CamelCapsFunctionNameSniff as PHPCS_CamelCapsFunctionNameSniff;
- use PHP_CodeSniffer_File as File;
- use PHP_CodeSniffer_Standards_AbstractScopeSniff as PHPCS_AbstractScopeSniff;
- use PHP_CodeSniffer_Tokens as Tokens;
- /**
- * All function and method names starting with double underscore are reserved by PHP.
- *
- * PHP version All
- *
- * {@internal Extends an upstream sniff to benefit from the properties contained therein.
- * The properties are lists of valid PHP magic function and method names, which
- * should be ignored for the purposes of this sniff.
- * As this sniff is not PHP version specific, we don't need access to the utility
- * methods in the PHPCompatibility\Sniff, so extending the upstream sniff is fine.
- * As the upstream sniff checks the same (and more, but we don't need the rest),
- * the logic in this sniff is largely the same as used upstream.
- * Extending the upstream sniff instead of including it via the ruleset, however,
- * prevents hard to debug issues of errors not being reported from the upstream sniff
- * if this library is used in combination with other rulesets.}
- *
- * @link https://www.php.net/manual/en/language.oop5.magic.php
- *
- * @since 8.2.0 This was previously, since 7.0.3, checked by the upstream sniff.
- * @since 9.3.2 The sniff will now ignore functions marked as `@deprecated` by design.
- */
- class ReservedFunctionNamesSniff extends PHPCS_CamelCapsFunctionNameSniff
- {
- /**
- * Overload the constructor to work round various PHPCS cross-version compatibility issues.
- *
- * @since 8.2.0
- */
- public function __construct()
- {
- $scopeTokens = array(\T_CLASS, \T_INTERFACE, \T_TRAIT);
- if (\defined('T_ANON_CLASS')) {
- $scopeTokens[] = \T_ANON_CLASS;
- }
- // Call the grand-parent constructor directly.
- PHPCS_AbstractScopeSniff::__construct($scopeTokens, array(\T_FUNCTION), true);
- // Make sure debuginfo is included in the array. Upstream only includes it since 2.5.1.
- $this->magicMethods['debuginfo'] = true;
- }
- /**
- * Processes the tokens within the scope.
- *
- * @since 8.2.0
- *
- * @param \PHP_CodeSniffer_File $phpcsFile The file being processed.
- * @param int $stackPtr The position where this token was
- * found.
- * @param int $currScope The position of the current scope.
- *
- * @return void
- */
- protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
- {
- $tokens = $phpcsFile->getTokens();
- /*
- * Determine if this is a function which needs to be examined.
- * The `processTokenWithinScope()` is called for each valid scope a method is in,
- * so for nested classes, we need to make sure we only examine the token for
- * the lowest level valid scope.
- */
- $conditions = $tokens[$stackPtr]['conditions'];
- end($conditions);
- $deepestScope = key($conditions);
- if ($deepestScope !== $currScope) {
- return;
- }
- if ($this->isFunctionDeprecated($phpcsFile, $stackPtr) === true) {
- /*
- * Deprecated functions don't have to comply with the naming conventions,
- * otherwise functions deprecated in favour of a function with a compliant
- * name would still trigger an error.
- */
- return;
- }
- $methodName = $phpcsFile->getDeclarationName($stackPtr);
- if ($methodName === null) {
- // Ignore closures.
- return;
- }
- // Is this a magic method. i.e., is prefixed with "__" ?
- if (preg_match('|^__[^_]|', $methodName) > 0) {
- $magicPart = strtolower(substr($methodName, 2));
- if (isset($this->magicMethods[$magicPart]) === false
- && isset($this->methodsDoubleUnderscore[$magicPart]) === false
- ) {
- $className = '[anonymous class]';
- $scopeNextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($currScope + 1), null, true);
- if ($scopeNextNonEmpty !== false && $tokens[$scopeNextNonEmpty]['code'] === \T_STRING) {
- $className = $tokens[$scopeNextNonEmpty]['content'];
- }
- $phpcsFile->addWarning(
- 'Method name "%s" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.',
- $stackPtr,
- 'MethodDoubleUnderscore',
- array($className . '::' . $methodName)
- );
- }
- }
- }
- /**
- * Processes the tokens outside the scope.
- *
- * @since 8.2.0
- *
- * @param \PHP_CodeSniffer_File $phpcsFile The file being processed.
- * @param int $stackPtr The position where this token was
- * found.
- *
- * @return void
- */
- protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
- {
- if ($this->isFunctionDeprecated($phpcsFile, $stackPtr) === true) {
- /*
- * Deprecated functions don't have to comply with the naming conventions,
- * otherwise functions deprecated in favour of a function with a compliant
- * name would still trigger an error.
- */
- return;
- }
- $functionName = $phpcsFile->getDeclarationName($stackPtr);
- if ($functionName === null) {
- // Ignore closures.
- return;
- }
- // Is this a magic function. i.e., it is prefixed with "__".
- if (preg_match('|^__[^_]|', $functionName) > 0) {
- $magicPart = strtolower(substr($functionName, 2));
- if (isset($this->magicFunctions[$magicPart]) === false) {
- $phpcsFile->addWarning(
- 'Function name "%s" is discouraged; PHP has reserved all method names with a double underscore prefix for future use.',
- $stackPtr,
- 'FunctionDoubleUnderscore',
- array($functionName)
- );
- }
- }
- }
- /**
- * Check whether a function has been marked as deprecated via a @deprecated tag
- * in the function docblock.
- *
- * @since 9.3.2
- *
- * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
- * @param int $stackPtr The position of a T_FUNCTION
- * token in the stack.
- *
- * @return bool
- */
- private function isFunctionDeprecated(File $phpcsFile, $stackPtr)
- {
- $tokens = $phpcsFile->getTokens();
- $find = Tokens::$methodPrefixes;
- $find[] = \T_WHITESPACE;
- $commentEnd = $phpcsFile->findPrevious($find, ($stackPtr - 1), null, true);
- if ($tokens[$commentEnd]['code'] !== \T_DOC_COMMENT_CLOSE_TAG) {
- // Function doesn't have a doc comment or is using the wrong type of comment.
- return false;
- }
- $commentStart = $tokens[$commentEnd]['comment_opener'];
- foreach ($tokens[$commentStart]['comment_tags'] as $tag) {
- if ($tokens[$tag]['content'] === '@deprecated') {
- return true;
- }
- }
- return false;
- }
- }
|