123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- <?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\ParameterValues;
- use PHPCompatibility\AbstractFunctionCallParameterSniff;
- use PHP_CodeSniffer_File as File;
- use PHP_CodeSniffer_Tokens as Tokens;
- /**
- * Passing the `$glue` and `$pieces` parameters to `implode()` in reverse order has
- * been deprecated in PHP 7.4.
- *
- * PHP version 7.4
- *
- * @link https://www.php.net/manual/en/migration74.deprecated.php#migration74.deprecated.core.implode-reverse-parameters
- * @link https://wiki.php.net/rfc/deprecations_php_7_4#implode_parameter_order_mix
- * @link https://php.net/manual/en/function.implode.php
- *
- * @since 9.3.0
- */
- class RemovedImplodeFlexibleParamOrderSniff extends AbstractFunctionCallParameterSniff
- {
- /**
- * Functions to check for.
- *
- * @since 9.3.0
- *
- * @var array
- */
- protected $targetFunctions = array(
- 'implode' => true,
- 'join' => true,
- );
- /**
- * List of PHP native constants which should be recognized as text strings.
- *
- * @since 9.3.0
- *
- * @var array
- */
- private $constantStrings = array(
- 'DIRECTORY_SEPARATOR' => true,
- 'PHP_EOL' => true,
- );
- /**
- * List of PHP native functions which should be recognized as returning an array.
- *
- * Note: The array_*() functions will always be taken into account.
- *
- * @since 9.3.0
- *
- * @var array
- */
- private $arrayFunctions = array(
- 'compact' => true,
- 'explode' => true,
- 'range' => true,
- );
- /**
- * List of PHP native array functions which should *not* be recognized as returning an array.
- *
- * @since 9.3.0
- *
- * @var array
- */
- private $arrayFunctionExceptions = array(
- 'array_key_exists' => true,
- 'array_key_first' => true,
- 'array_key_last' => true,
- 'array_multisort' => true,
- 'array_pop' => true,
- 'array_product' => true,
- 'array_push' => true,
- 'array_search' => true,
- 'array_shift' => true,
- 'array_sum' => true,
- 'array_unshift' => true,
- 'array_walk_recursive' => true,
- 'array_walk' => true,
- );
- /**
- * Do a version check to determine if this sniff needs to run at all.
- *
- * @since 9.3.0
- *
- * @return bool
- */
- protected function bowOutEarly()
- {
- return ($this->supportsAbove('7.4') === false);
- }
- /**
- * Process the parameters of a matched function.
- *
- * @since 9.3.0
- *
- * @param \PHP_CodeSniffer_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 int|void Integer stack pointer to skip forward or void to continue
- * normal file processing.
- */
- public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
- {
- if (isset($parameters[2]) === false) {
- // Only one parameter, this must be $pieces. Bow out.
- return;
- }
- $tokens = $phpcsFile->getTokens();
- /*
- * Examine the first parameter.
- * If there is any indication that this is an array declaration, we have an error.
- */
- $targetParam = $parameters[1];
- $start = $targetParam['start'];
- $end = ($targetParam['end'] + 1);
- $isOnlyText = true;
- $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true);
- if ($firstNonEmpty === false) {
- // Parse error. Shouldn't be possible.
- return;
- }
- if ($tokens[$firstNonEmpty]['code'] === \T_OPEN_PARENTHESIS) {
- $start = ($firstNonEmpty + 1);
- $end = $tokens[$firstNonEmpty]['parenthesis_closer'];
- }
- $hasTernary = $phpcsFile->findNext(\T_INLINE_THEN, $start, $end);
- if ($hasTernary !== false
- && isset($tokens[$start]['nested_parenthesis'], $tokens[$hasTernary]['nested_parenthesis'])
- && count($tokens[$start]['nested_parenthesis']) === count($tokens[$hasTernary]['nested_parenthesis'])
- ) {
- $start = ($hasTernary + 1);
- }
- for ($i = $start; $i < $end; $i++) {
- $tokenCode = $tokens[$i]['code'];
- if (isset(Tokens::$emptyTokens[$tokenCode])) {
- continue;
- }
- if ($tokenCode === \T_STRING && isset($this->constantStrings[$tokens[$i]['content']])) {
- continue;
- }
- if ($hasTernary !== false && $tokenCode === \T_INLINE_ELSE) {
- continue;
- }
- if (isset(Tokens::$stringTokens[$tokenCode]) === false) {
- $isOnlyText = false;
- }
- if ($tokenCode === \T_ARRAY || $tokenCode === \T_OPEN_SHORT_ARRAY || $tokenCode === \T_ARRAY_CAST) {
- $this->throwNotice($phpcsFile, $stackPtr, $functionName);
- return;
- }
- if ($tokenCode === \T_STRING) {
- /*
- * Check for specific functions which return an array (i.e. $pieces).
- */
- $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($i + 1), $end, true);
- if ($nextNonEmpty === false || $tokens[$nextNonEmpty]['code'] !== \T_OPEN_PARENTHESIS) {
- continue;
- }
- $nameLc = strtolower($tokens[$i]['content']);
- if (isset($this->arrayFunctions[$nameLc]) === false
- && (strpos($nameLc, 'array_') !== 0
- || isset($this->arrayFunctionExceptions[$nameLc]) === true)
- ) {
- continue;
- }
- // Now make sure it's the PHP native function being called.
- $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($i - 1), $start, true);
- if ($tokens[$prevNonEmpty]['code'] === \T_DOUBLE_COLON
- || $tokens[$prevNonEmpty]['code'] === \T_OBJECT_OPERATOR
- ) {
- // Method call, not a call to the PHP native function.
- continue;
- }
- if ($tokens[$prevNonEmpty]['code'] === \T_NS_SEPARATOR
- && $tokens[$prevNonEmpty - 1]['code'] === \T_STRING
- ) {
- // Namespaced function.
- continue;
- }
- // Ok, so we know that there is an array function in the first param.
- // 99.9% chance that this is $pieces, not $glue.
- $this->throwNotice($phpcsFile, $stackPtr, $functionName);
- return;
- }
- }
- if ($isOnlyText === true) {
- // First parameter only contained text string tokens, i.e. glue.
- return;
- }
- /*
- * Examine the second parameter.
- */
- $targetParam = $parameters[2];
- $start = $targetParam['start'];
- $end = ($targetParam['end'] + 1);
- $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $start, $end, true);
- if ($firstNonEmpty === false) {
- // Parse error. Shouldn't be possible.
- return;
- }
- if ($tokens[$firstNonEmpty]['code'] === \T_OPEN_PARENTHESIS) {
- $start = ($firstNonEmpty + 1);
- $end = $tokens[$firstNonEmpty]['parenthesis_closer'];
- }
- $hasTernary = $phpcsFile->findNext(\T_INLINE_THEN, $start, $end);
- if ($hasTernary !== false
- && isset($tokens[$start]['nested_parenthesis'], $tokens[$hasTernary]['nested_parenthesis'])
- && count($tokens[$start]['nested_parenthesis']) === count($tokens[$hasTernary]['nested_parenthesis'])
- ) {
- $start = ($hasTernary + 1);
- }
- for ($i = $start; $i < $end; $i++) {
- $tokenCode = $tokens[$i]['code'];
- if (isset(Tokens::$emptyTokens[$tokenCode])) {
- continue;
- }
- if ($tokenCode === \T_ARRAY || $tokenCode === \T_OPEN_SHORT_ARRAY || $tokenCode === \T_ARRAY_CAST) {
- // Found an array, $pieces is second.
- return;
- }
- if ($tokenCode === \T_STRING && isset($this->constantStrings[$tokens[$i]['content']])) {
- // One of the special cased, PHP native string constants found.
- $this->throwNotice($phpcsFile, $stackPtr, $functionName);
- return;
- }
- if ($tokenCode === \T_STRING || $tokenCode === \T_VARIABLE) {
- // Function call, constant or variable encountered.
- // No matter what this is combined with, we won't be able to reliably determine the value.
- return;
- }
- if ($tokenCode === \T_CONSTANT_ENCAPSED_STRING
- || $tokenCode === \T_DOUBLE_QUOTED_STRING
- || $tokenCode === \T_HEREDOC
- || $tokenCode === \T_NOWDOC
- ) {
- $this->throwNotice($phpcsFile, $stackPtr, $functionName);
- return;
- }
- }
- }
- /**
- * Throw the error/warning.
- *
- * @since 9.3.0
- *
- * @param \PHP_CodeSniffer_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
- */
- protected function throwNotice(File $phpcsFile, $stackPtr, $functionName)
- {
- $message = 'Passing the $glue and $pieces parameters in reverse order to %s has been deprecated since PHP 7.4';
- $isError = false;
- $errorCode = 'Deprecated';
- $data = array($functionName);
- /*
- Support for the deprecated behaviour is expected to be removed in PHP 8.0.
- Once this has been implemented, this section should be uncommented.
- if ($this->supportsAbove('8.0') === true) {
- $message .= ' and is removed since PHP 8.0';
- $isError = true;
- $errorCode = 'Removed';
- }
- */
- $message .= '; $glue should be the first parameter and $pieces the second';
- $this->addMessage($phpcsFile, $message, $stackPtr, $isError, $errorCode, $data);
- }
- }
|