PHP_CodeSniffer_Tokenizers_PHP::processAdditional PHP Method

processAdditional() public method

This additional processing checks for CASE statements that are using curly braces for scope openers and closers. It also turns some T_FUNCTION tokens into T_CLOSURE when they are not standard function definitions. It also detects short array syntax and converts those square brackets into new tokens. It also corrects some usage of the static and class keywords. It also assigns tokens to function return types.
public processAdditional ( array &$tokens, string $eolChar ) : void
$tokens array The array of tokens to process.
$eolChar string The EOL character to use for splitting strings.
return void
    public function processAdditional(&$tokens, $eolChar)
    {
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
            echo "\t*** START ADDITIONAL PHP PROCESSING ***" . PHP_EOL;
        }
        $numTokens = count($tokens);
        for ($i = $numTokens - 1; $i >= 0; $i--) {
            // Check for any unset scope conditions due to alternate IF/ENDIF syntax.
            if (isset($tokens[$i]['scope_opener']) === true && isset($tokens[$i]['scope_condition']) === false) {
                $tokens[$i]['scope_condition'] = $tokens[$tokens[$i]['scope_opener']]['scope_condition'];
            }
            if ($tokens[$i]['code'] === T_FUNCTION) {
                // Context sensitive keywords support.
                for ($x = $i + 1; $x < $numTokens; $x++) {
                    if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
                        // Non-whitespace content.
                        break;
                    }
                }
                if ($x === $numTokens) {
                    // We got to the end without finding any more
                    // non-whitespace content.
                    continue;
                }
                if (in_array($tokens[$x]['code'], array(T_STRING, T_OPEN_PARENTHESIS, T_BITWISE_AND), true) === false) {
                    if (PHP_CODESNIFFER_VERBOSITY > 1) {
                        $line = $tokens[$x]['line'];
                        $type = $tokens[$x]['type'];
                        echo "\t* token {$x} on line {$line} changed from {$type} to T_STRING" . PHP_EOL;
                    }
                    $tokens[$x]['code'] = T_STRING;
                    $tokens[$x]['type'] = 'T_STRING';
                }
                /*
                    Detect functions that are actually closures and
                    assign them a different token.
                */
                if (isset($tokens[$i]['scope_opener']) === true) {
                    for ($x = $i + 1; $x < $numTokens; $x++) {
                        if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false && $tokens[$x]['code'] !== T_BITWISE_AND) {
                            break;
                        }
                    }
                    if ($tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
                        $tokens[$i]['code'] = T_CLOSURE;
                        $tokens[$i]['type'] = 'T_CLOSURE';
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
                            $line = $tokens[$i]['line'];
                            echo "\t* token {$i} on line {$line} changed from T_FUNCTION to T_CLOSURE" . PHP_EOL;
                        }
                        for ($x = $tokens[$i]['scope_opener'] + 1; $x < $tokens[$i]['scope_closer']; $x++) {
                            if (isset($tokens[$x]['conditions'][$i]) === false) {
                                continue;
                            }
                            $tokens[$x]['conditions'][$i] = T_CLOSURE;
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
                                $type = $tokens[$x]['type'];
                                echo "\t\t* cleaned {$x} ({$type}) *" . PHP_EOL;
                            }
                        }
                    }
                    $tokenAfterReturnTypeHint = $tokens[$i]['scope_opener'];
                } else {
                    if (isset($tokens[$i]['parenthesis_closer']) === true) {
                        $tokenAfterReturnTypeHint = null;
                        for ($x = $tokens[$i]['parenthesis_closer'] + 1; $x < $numTokens; $x++) {
                            if ($tokens[$x]['code'] === T_SEMICOLON) {
                                $tokenAfterReturnTypeHint = $x;
                                break;
                            }
                        }
                        if ($tokenAfterReturnTypeHint === null) {
                            // Probably a syntax error.
                            continue;
                        }
                    } else {
                        // Probably a syntax error.
                        continue;
                    }
                }
                //end if
                /*
                    Detect function return values and assign them
                    a special token, because PHP doesn't.
                */
                for ($x = $tokenAfterReturnTypeHint - 1; $x > $i; $x--) {
                    if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
                        if (in_array($tokens[$x]['code'], array(T_STRING, T_ARRAY, T_CALLABLE, T_SELF, T_PARENT), true) === true) {
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
                                $line = $tokens[$x]['line'];
                                $type = $tokens[$x]['type'];
                                echo "\t* token {$x} on line {$line} changed from {$type} to T_RETURN_TYPE" . PHP_EOL;
                            }
                            $tokens[$x]['code'] = T_RETURN_TYPE;
                            $tokens[$x]['type'] = 'T_RETURN_TYPE';
                        }
                        break;
                    }
                }
                continue;
            } else {
                if ($tokens[$i]['code'] === T_CLASS && isset($tokens[$i]['scope_opener']) === true) {
                    /*
                        Detect anonymous classes and assign them a different token.
                    */
                    for ($x = $i + 1; $x < $numTokens; $x++) {
                        if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
                            break;
                        }
                    }
                    if ($tokens[$x]['code'] === T_OPEN_PARENTHESIS || $tokens[$x]['code'] === T_OPEN_CURLY_BRACKET || $tokens[$x]['code'] === T_EXTENDS || $tokens[$x]['code'] === T_IMPLEMENTS) {
                        $tokens[$i]['code'] = T_ANON_CLASS;
                        $tokens[$i]['type'] = 'T_ANON_CLASS';
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
                            $line = $tokens[$i]['line'];
                            echo "\t* token {$i} on line {$line} changed from T_CLASS to T_ANON_CLASS" . PHP_EOL;
                        }
                        for ($x = $tokens[$i]['scope_opener'] + 1; $x < $tokens[$i]['scope_closer']; $x++) {
                            if (isset($tokens[$x]['conditions'][$i]) === false) {
                                continue;
                            }
                            $tokens[$x]['conditions'][$i] = T_ANON_CLASS;
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
                                $type = $tokens[$x]['type'];
                                echo "\t\t* cleaned {$x} ({$type}) *" . PHP_EOL;
                            }
                        }
                    }
                    continue;
                } else {
                    if ($tokens[$i]['code'] === T_OPEN_SQUARE_BRACKET) {
                        // Unless there is a variable or a bracket before this token,
                        // it is the start of an array being defined using the short syntax.
                        for ($x = $i - 1; $x > 0; $x--) {
                            if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
                                break;
                            }
                        }
                        $allowed = array(T_CLOSE_CURLY_BRACKET => T_CLOSE_CURLY_BRACKET, T_CLOSE_SQUARE_BRACKET => T_CLOSE_SQUARE_BRACKET, T_CLOSE_PARENTHESIS => T_CLOSE_PARENTHESIS, T_VARIABLE => T_VARIABLE, T_STRING => T_STRING);
                        if (isset($allowed[$tokens[$x]['code']]) === false && isset($tokens[$i]['bracket_closer']) === true) {
                            $tokens[$i]['code'] = T_OPEN_SHORT_ARRAY;
                            $tokens[$i]['type'] = 'T_OPEN_SHORT_ARRAY';
                            $closer = $tokens[$i]['bracket_closer'];
                            $tokens[$closer]['code'] = T_CLOSE_SHORT_ARRAY;
                            $tokens[$closer]['type'] = 'T_CLOSE_SHORT_ARRAY';
                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
                                $line = $tokens[$i]['line'];
                                echo "\t* token {$i} on line {$line} changed from T_OPEN_SQUARE_BRACKET to T_OPEN_SHORT_ARRAY" . PHP_EOL;
                                $line = $tokens[$closer]['line'];
                                echo "\t* token {$closer} on line {$line} changed from T_CLOSE_SQUARE_BRACKET to T_CLOSE_SHORT_ARRAY" . PHP_EOL;
                            }
                        }
                        continue;
                    } else {
                        if ($tokens[$i]['code'] === T_STATIC) {
                            for ($x = $i - 1; $x > 0; $x--) {
                                if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
                                    break;
                                }
                            }
                            if ($tokens[$x]['code'] === T_INSTANCEOF) {
                                $tokens[$i]['code'] = T_STRING;
                                $tokens[$i]['type'] = 'T_STRING';
                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
                                    $line = $tokens[$i]['line'];
                                    echo "\t* token {$i} on line {$line} changed from T_STATIC to T_STRING" . PHP_EOL;
                                }
                            }
                            continue;
                        } else {
                            if ($tokens[$i]['code'] === T_ECHO && $tokens[$i]['content'] === '<?=') {
                                // HHVM tokenizes <?= as T_ECHO but it should be T_OPEN_TAG_WITH_ECHO.
                                $tokens[$i]['code'] = T_OPEN_TAG_WITH_ECHO;
                                $tokens[$i]['type'] = 'T_OPEN_TAG_WITH_ECHO';
                                if (PHP_CODESNIFFER_VERBOSITY > 1) {
                                    $line = $tokens[$i]['line'];
                                    echo "\t* token {$i} on line {$line} changed from T_ECHO to T_OPEN_TAG_WITH_ECHO" . PHP_EOL;
                                }
                            } else {
                                if ($tokens[$i]['code'] === T_TRUE || $tokens[$i]['code'] === T_FALSE || $tokens[$i]['code'] === T_NULL) {
                                    for ($x = $i + 1; $i < $numTokens; $x++) {
                                        if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
                                            // Non-whitespace content.
                                            break;
                                        }
                                    }
                                    $context = array(T_OBJECT_OPERATOR => true, T_NS_SEPARATOR => true, T_PAAMAYIM_NEKUDOTAYIM => true);
                                    if (isset($context[$tokens[$x]['code']]) === true) {
                                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
                                            $line = $tokens[$i]['line'];
                                            $type = $tokens[$i]['type'];
                                            echo "\t* token {$i} on line {$line} changed from {$type} to T_STRING" . PHP_EOL;
                                        }
                                        $tokens[$i]['code'] = T_STRING;
                                        $tokens[$i]['type'] = 'T_STRING';
                                    }
                                } else {
                                    if ($tokens[$i]['code'] === T_CONST) {
                                        // Context sensitive keywords support.
                                        for ($x = $i + 1; $i < $numTokens; $x++) {
                                            if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
                                                // Non-whitespace content.
                                                break;
                                            }
                                        }
                                        if ($tokens[$x]['code'] !== T_STRING) {
                                            if (PHP_CODESNIFFER_VERBOSITY > 1) {
                                                $line = $tokens[$x]['line'];
                                                $type = $tokens[$x]['type'];
                                                echo "\t* token {$x} on line {$line} changed from {$type} to T_STRING" . PHP_EOL;
                                            }
                                            $tokens[$x]['code'] = T_STRING;
                                            $tokens[$x]['type'] = 'T_STRING';
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            //end if
            if ($tokens[$i]['code'] !== T_CASE && $tokens[$i]['code'] !== T_DEFAULT || isset($tokens[$i]['scope_opener']) === false) {
                // Only interested in CASE and DEFAULT statements from here on in.
                continue;
            }
            $scopeOpener = $tokens[$i]['scope_opener'];
            $scopeCloser = $tokens[$i]['scope_closer'];
            // If the first char after the opener is a curly brace
            // and that brace has been ignored, it is actually
            // opening this case statement and the opener and closer are
            // probably set incorrectly.
            for ($x = $scopeOpener + 1; $x < $numTokens; $x++) {
                if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$tokens[$x]['code']]) === false) {
                    // Non-whitespace content.
                    break;
                }
            }
            if ($tokens[$x]['code'] === T_CASE || $tokens[$x]['code'] === T_DEFAULT) {
                // Special case for multiple CASE statements that share the same
                // closer. Because we are going backwards through the file, this next
                // CASE/DEFAULT statement is already fixed, so just use its closer
                // and don't worry about fixing anything.
                $newCloser = $tokens[$x]['scope_closer'];
                $tokens[$i]['scope_closer'] = $newCloser;
                if (PHP_CODESNIFFER_VERBOSITY > 1) {
                    $oldType = $tokens[$scopeCloser]['type'];
                    $newType = $tokens[$newCloser]['type'];
                    $line = $tokens[$i]['line'];
                    echo "\t* token {$i} (T_CASE) on line {$line} closer changed from {$scopeCloser} ({$oldType}) to {$newCloser} ({$newType})" . PHP_EOL;
                }
                continue;
            }
            if ($tokens[$x]['code'] !== T_OPEN_CURLY_BRACKET || isset($tokens[$x]['scope_condition']) === true) {
                // Not a CASE/DEFAULT with a curly brace opener.
                continue;
            }
            // The closer for this CASE/DEFAULT should be the closing curly brace and
            // not whatever it already is. The opener needs to be the opening curly
            // brace so everything matches up.
            $newCloser = $tokens[$x]['bracket_closer'];
            foreach (array($i, $x, $newCloser) as $index) {
                $tokens[$index]['scope_condition'] = $i;
                $tokens[$index]['scope_opener'] = $x;
                $tokens[$index]['scope_closer'] = $newCloser;
            }
            unset($tokens[$scopeOpener]['scope_condition']);
            unset($tokens[$scopeOpener]['scope_opener']);
            unset($tokens[$scopeOpener]['scope_closer']);
            unset($tokens[$scopeCloser]['scope_condition']);
            unset($tokens[$scopeCloser]['scope_opener']);
            unset($tokens[$scopeCloser]['scope_closer']);
            unset($tokens[$x]['bracket_opener']);
            unset($tokens[$x]['bracket_closer']);
            unset($tokens[$newCloser]['bracket_opener']);
            unset($tokens[$newCloser]['bracket_closer']);
            $tokens[$scopeCloser]['conditions'][] = $i;
            if (PHP_CODESNIFFER_VERBOSITY > 1) {
                $line = $tokens[$i]['line'];
                $tokenType = $tokens[$i]['type'];
                $oldType = $tokens[$scopeOpener]['type'];
                $newType = $tokens[$x]['type'];
                echo "\t* token {$i} ({$tokenType}) on line {$line} opener changed from {$scopeOpener} ({$oldType}) to {$x} ({$newType})" . PHP_EOL;
                $oldType = $tokens[$scopeCloser]['type'];
                $newType = $tokens[$newCloser]['type'];
                echo "\t* token {$i} ({$tokenType}) on line {$line} closer changed from {$scopeCloser} ({$oldType}) to {$newCloser} ({$newType})" . PHP_EOL;
            }
            // Now fix up all the tokens that think they are
            // inside the CASE/DEFAULT statement when they are really outside.
            for ($x = $newCloser; $x < $scopeCloser; $x++) {
                foreach ($tokens[$x]['conditions'] as $num => $oldCond) {
                    if ($oldCond === $tokens[$i]['code']) {
                        $oldConditions = $tokens[$x]['conditions'];
                        unset($tokens[$x]['conditions'][$num]);
                        if (PHP_CODESNIFFER_VERBOSITY > 1) {
                            $type = $tokens[$x]['type'];
                            $oldConds = '';
                            foreach ($oldConditions as $condition) {
                                $oldConds .= token_name($condition) . ',';
                            }
                            $oldConds = rtrim($oldConds, ',');
                            $newConds = '';
                            foreach ($tokens[$x]['conditions'] as $condition) {
                                $newConds .= token_name($condition) . ',';
                            }
                            $newConds = rtrim($newConds, ',');
                            echo "\t\t* cleaned {$x} ({$type}) *" . PHP_EOL;
                            echo "\t\t\t=> conditions changed from {$oldConds} to {$newConds}" . PHP_EOL;
                        }
                        break;
                    }
                    //end if
                }
                //end foreach
            }
            //end for
        }
        //end for
        if (PHP_CODESNIFFER_VERBOSITY > 1) {
            echo "\t*** END ADDITIONAL PHP PROCESSING ***" . PHP_EOL;
        }
    }