> */ protected $targetFunctions = [ 'fputcsv' => [ 'position' => 5, 'name' => 'escape', ], 'fgetcsv' => [ 'position' => 5, 'name' => 'escape', ], 'str_getcsv' => [ 'position' => 4, 'name' => 'escape', ], ]; /** * Determine if this sniff needs to run at all. * * @since 10.0.0 * * @return bool */ protected function bowOutEarly() { return false; } /** * Process the parameters of a matched function. * * @since 10.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. * @param array> $parameters Array with information about the parameters. * * @return void */ public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters) { if (ScannedCode::shouldRunOnOrBelow('7.3') === true) { $this->checkParameterValueIsEmptyString($phpcsFile, $stackPtr, $functionName, $parameters); } if (ScannedCode::shouldRunOnOrAbove('8.4') === true) { $this->checkIfParameterIsPassed($phpcsFile, $stackPtr, $functionName, $parameters); } } /** * Process the function if no parameters were found. * * @since 10.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. * * @return void */ public function processNoParameters(File $phpcsFile, $stackPtr, $functionName) { if (ScannedCode::shouldRunOnOrAbove('8.4') === true) { $this->checkIfParameterIsPassed($phpcsFile, $stackPtr, $functionName, []); } } /** * PHP < 7.4: Process a passed `$escape` parameter to check if the value is an empty string. * * @since 10.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. * @param array> $parameters Array with information about the parameters. * * @return void */ private function checkParameterValueIsEmptyString(File $phpcsFile, $stackPtr, $functionName, $parameters) { $functionLC = \strtolower($functionName); $paramInfo = $this->targetFunctions[$functionLC]; $targetParam = PassedParameters::getParameterFromStack($parameters, $paramInfo['position'], $paramInfo['name']); if ($targetParam === false) { return; } if ($targetParam['clean'] !== '""' && $targetParam['clean'] !== "''") { // Not a hard-coded empty string. Bow out, either valid non-empty string or undetermined. return; } $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], ($targetParam['end'] + 1), true); // Special case the changed behaviour for str_getcsv(). if ($functionLC === 'str_getcsv') { if (ScannedCode::shouldRunOnOrAbove('7.4') === false) { // Only report the error when the code needs to run on both PHP <= 7.3 as well as PHP >= 7.4. return; } $msg = 'Passing an empty string as the $%s parameter to %s() would fall through to the default value ("\\") in PHP 7.3 and earlier, while in PHP 7.4+ it will disable the proprietary CSV escaping mechanism.'; $code = 'ChangedBehaviour'; $data = [ $paramInfo['name'], $functionLC, ]; $phpcsFile->addError($msg, $firstNonEmpty, $code, $data); return; } $msg = 'Passing an empty string as the $%s parameter to %s() to disable the proprietary CSV escaping mechanism was not supported in PHP 7.3 or earlier.'; $code = 'EmptyStringNotAllowed'; $data = [ $paramInfo['name'], $functionLC, ]; $phpcsFile->addError($msg, $firstNonEmpty, $code, $data); } /** * PHP 8.4+: Verify the `$escape` parameter is passed. * * @since 10.0.0 * * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. * @param int $stackPtr The position of the current token in the stack. * @param string $functionName The token content (function name) which was matched. * @param array> $parameters Array with information about the parameters. * * @return void */ private function checkIfParameterIsPassed(File $phpcsFile, $stackPtr, $functionName, $parameters) { $functionLC = \strtolower($functionName); $paramInfo = $this->targetFunctions[$functionLC]; $targetParam = PassedParameters::getParameterFromStack($parameters, $paramInfo['position'], $paramInfo['name']); if ($targetParam !== false && $targetParam['clean'] !== '') { return; } $msg = 'The $%s parameter must be passed when calling %s() as its default value will change in a future PHP version.'; $code = 'DeprecatedParamNotPassed'; $data = [ $paramInfo['name'], $functionLC, ]; $phpcsFile->addWarning($msg, $stackPtr, $code, $data); } }