RemovedMbstringModifiersSniff.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <?php
  2. /**
  3. * PHPCompatibility, an external standard for PHP_CodeSniffer.
  4. *
  5. * @package PHPCompatibility
  6. * @copyright 2012-2019 PHPCompatibility Contributors
  7. * @license https://opensource.org/licenses/LGPL-3.0 LGPL3
  8. * @link https://github.com/PHPCompatibility/PHPCompatibility
  9. */
  10. namespace PHPCompatibility\Sniffs\ParameterValues;
  11. use PHPCompatibility\AbstractFunctionCallParameterSniff;
  12. use PHP_CodeSniffer_File as File;
  13. use PHP_CodeSniffer_Tokens as Tokens;
  14. /**
  15. * Check for use of deprecated and removed regex modifiers for MbString regex functions.
  16. *
  17. * Initially just checks for the PHP 7.1 deprecated `e` modifier.
  18. *
  19. * PHP version 7.1
  20. *
  21. * @link https://wiki.php.net/rfc/deprecate_mb_ereg_replace_eval_option
  22. * @link https://www.php.net/manual/en/function.mb-regex-set-options.php
  23. *
  24. * @since 7.0.5
  25. * @since 7.0.8 This sniff now throws a warning instead of an error as the functionality is
  26. * only deprecated (for now).
  27. * @since 8.2.0 Now extends the `AbstractFunctionCallParameterSniff` instead of the base `Sniff` class.
  28. * @since 9.0.0 Renamed from `MbstringReplaceEModifierSniff` to `RemovedMbstringModifiersSniff`.
  29. */
  30. class RemovedMbstringModifiersSniff extends AbstractFunctionCallParameterSniff
  31. {
  32. /**
  33. * Functions to check for.
  34. *
  35. * Key is the function name, value the parameter position of the options parameter.
  36. *
  37. * @since 7.0.5
  38. * @since 8.2.0 Renamed from `$functions` to `$targetFunctions`.
  39. *
  40. * @var array
  41. */
  42. protected $targetFunctions = array(
  43. 'mb_ereg_replace' => 4,
  44. 'mb_eregi_replace' => 4,
  45. 'mb_regex_set_options' => 1,
  46. 'mbereg_replace' => 4, // Undocumented, but valid function alias.
  47. 'mberegi_replace' => 4, // Undocumented, but valid function alias.
  48. );
  49. /**
  50. * Do a version check to determine if this sniff needs to run at all.
  51. *
  52. * @since 8.2.0
  53. *
  54. * @return bool
  55. */
  56. protected function bowOutEarly()
  57. {
  58. // Version used here should be the highest version from the `$newModifiers` array,
  59. // i.e. the last PHP version in which a new modifier was introduced.
  60. return ($this->supportsAbove('7.1') === false);
  61. }
  62. /**
  63. * Process the parameters of a matched function.
  64. *
  65. * @since 7.0.5
  66. * @since 8.2.0 Renamed from `process()` to `processParameters()` and removed
  67. * logic superfluous now the sniff extends the abstract.
  68. *
  69. * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
  70. * @param int $stackPtr The position of the current token in the stack.
  71. * @param string $functionName The token content (function name) which was matched.
  72. * @param array $parameters Array with information about the parameters.
  73. *
  74. * @return int|void Integer stack pointer to skip forward or void to continue
  75. * normal file processing.
  76. */
  77. public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
  78. {
  79. $tokens = $phpcsFile->getTokens();
  80. $functionNameLc = strtolower($functionName);
  81. // Check whether the options parameter in the function call is passed.
  82. if (isset($parameters[$this->targetFunctions[$functionNameLc]]) === false) {
  83. return;
  84. }
  85. $optionsParam = $parameters[$this->targetFunctions[$functionNameLc]];
  86. $stringToken = $phpcsFile->findNext(Tokens::$stringTokens, $optionsParam['start'], $optionsParam['end'] + 1);
  87. if ($stringToken === false) {
  88. // No string token found in the options parameter, so skip it (e.g. variable passed in).
  89. return;
  90. }
  91. $options = '';
  92. /*
  93. * Get the content of any string tokens in the options parameter and remove the quotes and variables.
  94. */
  95. for ($i = $stringToken; $i <= $optionsParam['end']; $i++) {
  96. if (isset(Tokens::$stringTokens[$tokens[$i]['code']]) === false) {
  97. continue;
  98. }
  99. $content = $this->stripQuotes($tokens[$i]['content']);
  100. if ($tokens[$i]['code'] === \T_DOUBLE_QUOTED_STRING) {
  101. $content = $this->stripVariables($content);
  102. }
  103. $content = trim($content);
  104. if (empty($content) === false) {
  105. $options .= $content;
  106. }
  107. }
  108. if (strpos($options, 'e') !== false) {
  109. $error = 'The Mbstring regex "e" modifier is deprecated since PHP 7.1.';
  110. // The alternative mb_ereg_replace_callback() function is only available since 5.4.1.
  111. if ($this->supportsBelow('5.4.1') === false) {
  112. $error .= ' Use mb_ereg_replace_callback() instead (PHP 5.4.1+).';
  113. }
  114. $phpcsFile->addWarning($error, $stackPtr, 'Deprecated');
  115. }
  116. }
  117. }