/**
  * Processes this test, when one of its tokens is encountered.
  *
  * @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 int|void
  */
 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
 {
     // Merge any custom functions with the defaults, if we haven't already.
     if (!self::$addedCustomFunctions) {
         WordPress_Sniff::$escapingFunctions = array_merge(WordPress_Sniff::$escapingFunctions, array_flip($this->customEscapingFunctions));
         WordPress_Sniff::$autoEscapedFunctions = array_merge(WordPress_Sniff::$autoEscapedFunctions, array_flip($this->customAutoEscapedFunctions));
         WordPress_Sniff::$printingFunctions = array_merge(WordPress_Sniff::$printingFunctions, array_flip($this->customPrintingFunctions));
         if (!empty($this->customSanitizingFunctions)) {
             WordPress_Sniff::$escapingFunctions = array_merge(WordPress_Sniff::$escapingFunctions, array_flip($this->customSanitizingFunctions));
             $phpcsFile->addWarning('The customSanitizingFunctions property is deprecated in favor of customEscapingFunctions.', 0, 'DeprecatedCustomSanitizingFunctions');
         }
         self::$addedCustomFunctions = true;
     }
     $this->init($phpcsFile);
     $tokens = $phpcsFile->getTokens();
     $function = $tokens[$stackPtr]['content'];
     // Find the opening parenthesis (if present; T_ECHO might not have it).
     $open_paren = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, $stackPtr + 1, null, true);
     // If function, not T_ECHO nor T_PRINT
     if ($tokens[$stackPtr]['code'] == T_STRING) {
         // Skip if it is a function but is not of the printing functions.
         if (!isset(self::$printingFunctions[$tokens[$stackPtr]['content']])) {
             return;
         }
         if (isset($tokens[$open_paren]['parenthesis_closer'])) {
             $end_of_statement = $tokens[$open_paren]['parenthesis_closer'];
         }
         // These functions only need to have the first argument escaped.
         if (in_array($function, array('trigger_error', 'user_error'))) {
             $end_of_statement = $phpcsFile->findEndOfStatement($open_paren + 1);
         }
     }
     // Checking for the ignore comment, ex: //xss ok
     if ($this->has_whitelist_comment('xss', $stackPtr)) {
         return;
     }
     if (isset($end_of_statement, self::$unsafePrintingFunctions[$function])) {
         $error = $phpcsFile->addError("Expected next thing to be an escaping function (like %s), not '%s'", $stackPtr, 'UnsafePrintingFunction', array(self::$unsafePrintingFunctions[$function], $function));
         // If the error was reported, don't bother checking the function's arguments.
         if ($error) {
             return $end_of_statement;
         }
     }
     $ternary = false;
     // This is already determined if this is a function and not T_ECHO.
     if (!isset($end_of_statement)) {
         $end_of_statement = $phpcsFile->findNext(array(T_SEMICOLON, T_CLOSE_TAG), $stackPtr);
         $last_token = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, $end_of_statement - 1, null, true);
         // Check for the ternary operator. We only need to do this here if this
         // echo is lacking parenthesis. Otherwise it will be handled below.
         if (T_OPEN_PARENTHESIS !== $tokens[$open_paren]['code'] || T_CLOSE_PARENTHESIS !== $tokens[$last_token]['code']) {
             $ternary = $phpcsFile->findNext(T_INLINE_THEN, $stackPtr, $end_of_statement);
             // If there is a ternary skip over the part before the ?. However, if
             // the ternary is within parentheses, it will be handled in the loop.
             if ($ternary && empty($tokens[$ternary]['nested_parenthesis'])) {
                 $stackPtr = $ternary;
             }
         }
     }
     // Ignore the function itself.
     $stackPtr++;
     $in_cast = false;
     // looping through echo'd components
     $watch = true;
     for ($i = $stackPtr; $i < $end_of_statement; $i++) {
         // Ignore whitespaces and comments.
         if (in_array($tokens[$i]['code'], array(T_WHITESPACE, T_COMMENT))) {
             continue;
         }
         if (T_OPEN_PARENTHESIS === $tokens[$i]['code']) {
             if ($in_cast) {
                 // Skip to the end of a function call if it has been casted to a safe value.
                 $i = $tokens[$i]['parenthesis_closer'];
                 $in_cast = false;
             } else {
                 // Skip over the condition part of a ternary (i.e., to after the ?).
                 $ternary = $phpcsFile->findNext(T_INLINE_THEN, $i, $tokens[$i]['parenthesis_closer']);
                 if ($ternary) {
                     $next_paren = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $i, $tokens[$i]['parenthesis_closer']);
                     // We only do it if the ternary isn't within a subset of parentheses.
                     if (!$next_paren || $ternary > $tokens[$next_paren]['parenthesis_closer']) {
                         $i = $ternary;
                     }
                 }
             }
             continue;
         }
         // Handle arrays for those functions that accept them.
         if ($tokens[$i]['code'] === T_ARRAY) {
             $i++;
             // Skip the opening parenthesis.
             continue;
         }
         if (in_array($tokens[$i]['code'], array(T_DOUBLE_ARROW, T_CLOSE_PARENTHESIS))) {
             continue;
         }
         // Handle magic constants for debug functions.
         if (in_array($tokens[$i]['code'], array(T_METHOD_C, T_FUNC_C, T_FILE, T_CLASS_C))) {
             continue;
         }
         // Wake up on concatenation characters, another part to check
         if (in_array($tokens[$i]['code'], array(T_STRING_CONCAT))) {
             $watch = true;
             continue;
         }
         // Wake up after a ternary else (:).
         if ($ternary && in_array($tokens[$i]['code'], array(T_INLINE_ELSE))) {
             $watch = true;
             continue;
         }
         // Wake up for commas.
         if ($tokens[$i]['code'] === T_COMMA) {
             $in_cast = false;
             $watch = true;
             continue;
         }
         if ($watch === false) {
             continue;
         }
         // Allow T_CONSTANT_ENCAPSED_STRING eg: echo 'Some String';
         // Also T_LNUMBER, e.g.: echo 45; exit -1;
         if (in_array($tokens[$i]['code'], array(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER, T_MINUS))) {
             continue;
         }
         $watch = false;
         // Allow int/double/bool casted variables
         if (in_array($tokens[$i]['code'], array(T_INT_CAST, T_DOUBLE_CAST, T_BOOL_CAST))) {
             $in_cast = true;
             continue;
         }
         // Now check that next token is a function call.
         if (T_STRING === $this->tokens[$i]['code']) {
             $functionName = $this->tokens[$i]['content'];
             $is_formatting_function = isset(self::$formattingFunctions[$functionName]);
             // Skip pointer to after the function.
             if ($_pos = $this->phpcsFile->findNext(array(T_OPEN_PARENTHESIS), $i, null, null, null, true)) {
                 // If this is a formatting function we just skip over the opening
                 // parenthesis. Otherwise we skip all the way to the closing.
                 if ($is_formatting_function) {
                     $i = $_pos + 1;
                     $watch = true;
                 } else {
                     $i = $this->tokens[$_pos]['parenthesis_closer'];
                 }
             }
             // If this is a safe function, we don't flag it.
             if ($is_formatting_function || isset(self::$autoEscapedFunctions[$functionName]) || isset(self::$escapingFunctions[$functionName])) {
                 continue;
             }
         }
         $this->phpcsFile->addError("Expected next thing to be an escaping function (see Codex for 'Data Validation'), not '%s'", $i, 'OutputNotEscaped', $this->tokens[$i]['content']);
     }
     return $end_of_statement;
 }
 /**
  * Processes this test, when one of its tokens is encountered.
  *
  * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
  * @param int                  $stackPtr  The position of the current token
  *                                        in the stack passed in $tokens.
  *
  * @todo Allow T_CONSTANT_ENCAPSED_STRING?
  *
  * @return void
  */
 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
 {
     // Merge any custom functions with the defaults, if we haven't already.
     if (!self::$addedCustomFunctions) {
         self::$sanitizingFunctions = array_merge(self::$sanitizingFunctions, $this->customSanitizingFunctions);
         self::$autoEscapedFunctions = array_merge(self::$autoEscapedFunctions, $this->customAutoEscapedFunctions);
         self::$addedCustomFunctions = true;
     }
     $tokens = $phpcsFile->getTokens();
     // If function, not T_ECHO nor T_PRINT
     if ($tokens[$stackPtr]['code'] == T_STRING) {
         // Skip if it is a function but is not of the printing functions ( self::needSanitizingFunctions )
         if (!in_array($tokens[$stackPtr]['content'], $this->needSanitizingFunctions)) {
             return;
         }
         $stackPtr++;
         // Ignore the starting bracket
     }
     // Ensure that the next token is a whitespace.
     $stackPtr++;
     if ($tokens[$stackPtr]['code'] === T_WHITESPACE) {
         $stackPtr++;
     }
     // Checking for the ignore comment, ex: //xss ok
     $isAtEndOfStatement = false;
     $commentOkRegex = '/xss\\W*(ok|pass|clear|whitelist)/i';
     $tokensCount = count($tokens);
     for ($i = $stackPtr; $i < $tokensCount; $i++) {
         if ($tokens[$i]['code'] === T_SEMICOLON) {
             $isAtEndOfStatement = true;
         }
         if ($isAtEndOfStatement === true && in_array($tokens[$i]['code'], array(T_SEMICOLON, T_WHITESPACE, T_COMMENT)) === false) {
             break;
         }
         preg_match($commentOkRegex, $tokens[$i]['content'], $matches);
         if ($tokens[$i]['code'] === T_COMMENT && empty($matches) === false) {
             return;
         }
     }
     // looping through echo'd components
     $watch = true;
     for ($i = $stackPtr; $i < count($tokens); $i++) {
         // End processing if found the end of statement
         if ($tokens[$i]['code'] == T_SEMICOLON) {
             return;
         }
         // Ignore whitespaces
         if ($tokens[$i]['code'] == T_WHITESPACE) {
             continue;
         }
         // Wake up on concatenation characters, another part to check
         if (in_array($tokens[$i]['code'], array(T_STRING_CONCAT))) {
             $watch = true;
             continue;
         }
         if ($watch === false) {
             continue;
         }
         $watch = false;
         // Allow T_CONSTANT_ENCAPSED_STRING eg: echo 'Some String';
         if (in_array($tokens[$i]['code'], array(T_CONSTANT_ENCAPSED_STRING))) {
             continue;
         }
         // Allow int/double/bool casted variables
         if (in_array($tokens[$i]['code'], array(T_INT_CAST, T_DOUBLE_CAST, T_BOOL_CAST))) {
             continue;
         }
         // Now check that next token is a function call.
         if (in_array($tokens[$i]['code'], array(T_STRING)) === false) {
             $phpcsFile->addError("Expected next thing to be a escaping function, not '%s'", $i, null, $tokens[$i]['content']);
             continue;
         } else {
             $functionName = $tokens[$i]['content'];
             if (in_array($functionName, self::$autoEscapedFunctions) === false && in_array($functionName, self::$sanitizingFunctions) === false) {
                 $phpcsFile->addError("Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw '%s'", $i, null, $tokens[$i]['content']);
             }
             // Skip pointer to after the function
             if ($_pos = $phpcsFile->findNext(array(T_OPEN_PARENTHESIS), $i, null, null, null, true)) {
                 $i = $tokens[$_pos]['parenthesis_closer'];
             }
             continue;
         }
     }
 }