Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x

ComparisonOperatorUsageSniff.php

Namespace

PHP_CodeSniffer\Standards\Squiz\Sniffs\Operators

File

vendor/squizlabs/php_codesniffer/src/Standards/Squiz/Sniffs/Operators/ComparisonOperatorUsageSniff.php

View source
<?php


/**
 * A Sniff to enforce the use of IDENTICAL type operators rather than EQUAL operators.
 *
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
 */
namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\Operators;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class ComparisonOperatorUsageSniff implements Sniff {
    
    /**
     * A list of tokenizers this sniff supports.
     *
     * @var array
     */
    public $supportedTokenizers = [
        'PHP',
        'JS',
    ];
    
    /**
     * A list of valid comparison operators.
     *
     * @var array
     */
    private static $validOps = [
        T_IS_IDENTICAL => true,
        T_IS_NOT_IDENTICAL => true,
        T_LESS_THAN => true,
        T_GREATER_THAN => true,
        T_IS_GREATER_OR_EQUAL => true,
        T_IS_SMALLER_OR_EQUAL => true,
        T_INSTANCEOF => true,
    ];
    
    /**
     * A list of invalid operators with their alternatives.
     *
     * @var array<string, array<int, string>>
     */
    private static $invalidOps = [
        'PHP' => [
            T_IS_EQUAL => '===',
            T_IS_NOT_EQUAL => '!==',
            T_BOOLEAN_NOT => '=== FALSE',
        ],
        'JS' => [
            T_IS_EQUAL => '===',
            T_IS_NOT_EQUAL => '!==',
        ],
    ];
    
    /**
     * Registers the token types that this sniff wishes to listen to.
     *
     * @return array<int|string>
     */
    public function register() {
        return [
            T_IF,
            T_ELSEIF,
            T_INLINE_THEN,
            T_WHILE,
            T_FOR,
        ];
    }
    
    //end register()
    
    /**
     * Process the tokens that this sniff is listening for.
     *
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
     * @param int                         $stackPtr  The position in the stack where the token
     *                                               was found.
     *
     * @return void
     */
    public function process(File $phpcsFile, $stackPtr) {
        $tokens = $phpcsFile->getTokens();
        $tokenizer = $phpcsFile->tokenizerType;
        if ($tokens[$stackPtr]['code'] === T_INLINE_THEN) {
            $end = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true);
            if ($tokens[$end]['code'] !== T_CLOSE_PARENTHESIS) {
                // This inline IF statement does not have its condition
                // bracketed, so we need to guess where it starts.
                for ($i = $end - 1; $i >= 0; $i--) {
                    if ($tokens[$i]['code'] === T_SEMICOLON) {
                        // Stop here as we assume it is the end
                        // of the previous statement.
                        break;
                    }
                    else {
                        if ($tokens[$i]['code'] === T_OPEN_TAG) {
                            // Stop here as this is the start of the file.
                            break;
                        }
                        else {
                            if ($tokens[$i]['code'] === T_CLOSE_CURLY_BRACKET) {
                                // Stop if this is the closing brace of
                                // a code block.
                                if (isset($tokens[$i]['scope_opener']) === true) {
                                    break;
                                }
                            }
                            else {
                                if ($tokens[$i]['code'] === T_OPEN_CURLY_BRACKET) {
                                    // Stop if this is the opening brace of
                                    // a code block.
                                    if (isset($tokens[$i]['scope_closer']) === true) {
                                        break;
                                    }
                                }
                                else {
                                    if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
                                        // Stop if this is the start of a pair of
                                        // parentheses that surrounds the inline
                                        // IF statement.
                                        if (isset($tokens[$i]['parenthesis_closer']) === true && $tokens[$i]['parenthesis_closer'] >= $stackPtr) {
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    
                    //end if
                }
                
                //end for
                $start = $phpcsFile->findNext(Tokens::$emptyTokens, $i + 1, null, true);
            }
            else {
                if (isset($tokens[$end]['parenthesis_opener']) === false) {
                    return;
                }
                $start = $tokens[$end]['parenthesis_opener'];
            }
            
            //end if
        }
        else {
            if ($tokens[$stackPtr]['code'] === T_FOR) {
                if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
                    return;
                }
                $openingBracket = $tokens[$stackPtr]['parenthesis_opener'];
                $closingBracket = $tokens[$stackPtr]['parenthesis_closer'];
                $start = $phpcsFile->findNext(T_SEMICOLON, $openingBracket, $closingBracket);
                $end = $phpcsFile->findNext(T_SEMICOLON, $start + 1, $closingBracket);
                if ($start === false || $end === false) {
                    return;
                }
            }
            else {
                if (isset($tokens[$stackPtr]['parenthesis_opener']) === false) {
                    return;
                }
                $start = $tokens[$stackPtr]['parenthesis_opener'];
                $end = $tokens[$stackPtr]['parenthesis_closer'];
            }
        }
        
        //end if
        $requiredOps = 0;
        $foundOps = 0;
        $foundBooleans = 0;
        $lastNonEmpty = $start;
        for ($i = $start; $i <= $end; $i++) {
            $type = $tokens[$i]['code'];
            if (isset(self::$invalidOps[$tokenizer][$type]) === true) {
                $error = 'Operator %s prohibited; use %s instead';
                $data = [
                    $tokens[$i]['content'],
                    self::$invalidOps[$tokenizer][$type],
                ];
                $phpcsFile->addError($error, $i, 'NotAllowed', $data);
                $foundOps++;
            }
            else {
                if (isset(self::$validOps[$type]) === true) {
                    $foundOps++;
                }
            }
            if ($type === T_OPEN_PARENTHESIS && isset($tokens[$i]['parenthesis_closer']) === true && isset(Tokens::$functionNameTokens[$tokens[$lastNonEmpty]['code']]) === true) {
                $i = $tokens[$i]['parenthesis_closer'];
                $lastNonEmpty = $i;
                continue;
            }
            if ($tokens[$i]['code'] === T_TRUE || $tokens[$i]['code'] === T_FALSE) {
                $foundBooleans++;
            }
            if ($phpcsFile->tokenizerType !== 'JS' && ($tokens[$i]['code'] === T_BOOLEAN_AND || $tokens[$i]['code'] === T_BOOLEAN_OR)) {
                $requiredOps++;
                // When the instanceof operator is used with another operator
                // like ===, you can get more ops than are required.
                if ($foundOps > $requiredOps) {
                    $foundOps = $requiredOps;
                }
                // If we get to here and we have not found the right number of
                // comparison operators, then we must have had an implicit
                // true operation i.e., if ($a) instead of the required
                // if ($a === true), so let's add an error.
                if ($requiredOps !== $foundOps) {
                    $error = 'Implicit true comparisons prohibited; use === TRUE instead';
                    $phpcsFile->addError($error, $stackPtr, 'ImplicitTrue');
                    $foundOps++;
                }
            }
            if (isset(Tokens::$emptyTokens[$type]) === false) {
                $lastNonEmpty = $i;
            }
        }
        
        //end for
        $requiredOps++;
        if ($phpcsFile->tokenizerType !== 'JS' && $foundOps < $requiredOps && $requiredOps !== $foundBooleans) {
            $error = 'Implicit true comparisons prohibited; use === TRUE instead';
            $phpcsFile->addError($error, $stackPtr, 'ImplicitTrue');
        }
    }
    
    //end process()

}

//end class

Classes

Title Deprecated Summary
ComparisonOperatorUsageSniff

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal