NewIconvMbstringCharsetDefaultSniff.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. * Detect calls to Iconv and Mbstring functions with the optional `$charset`/`$encoding` parameter not set.
  16. *
  17. * The default value for the iconv `$charset` and the MbString $encoding` parameters was changed
  18. * in PHP 5.6 to the value of `default_charset`, which defaults to `UTF-8`.
  19. *
  20. * Previously, the iconv functions would default to the value of `iconv.internal_encoding`;
  21. * The Mbstring functions would default to the return value of `mb_internal_encoding()`.
  22. * In both case, this would normally come down to `ISO-8859-1`.
  23. *
  24. * PHP version 5.6
  25. *
  26. * @link https://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.default-encoding
  27. * @link https://www.php.net/manual/en/migration56.deprecated.php#migration56.deprecated.iconv-mbstring-encoding
  28. * @link https://wiki.php.net/rfc/default_encoding
  29. *
  30. * @since 9.3.0
  31. */
  32. class NewIconvMbstringCharsetDefaultSniff extends AbstractFunctionCallParameterSniff
  33. {
  34. /**
  35. * Functions to check for.
  36. *
  37. * Only those functions where the charset/encoding parameter is optional need to be listed.
  38. *
  39. * Key is the function name, value the 1-based parameter position of
  40. * the $charset/$encoding parameter.
  41. *
  42. * @since 9.3.0
  43. *
  44. * @var array
  45. */
  46. protected $targetFunctions = array(
  47. 'iconv_mime_decode_headers' => 3,
  48. 'iconv_mime_decode' => 3,
  49. 'iconv_mime_encode' => 3, // Special case.
  50. 'iconv_strlen' => 2,
  51. 'iconv_strpos' => 4,
  52. 'iconv_strrpos' => 3,
  53. 'iconv_substr' => 4,
  54. 'mb_check_encoding' => 2,
  55. 'mb_chr' => 2,
  56. 'mb_convert_case' => 3,
  57. 'mb_convert_encoding' => 3,
  58. 'mb_convert_kana' => 3,
  59. 'mb_decode_numericentity' => 3,
  60. 'mb_encode_numericentity' => 3,
  61. 'mb_ord' => 2,
  62. 'mb_scrub' => 2,
  63. 'mb_strcut' => 4,
  64. 'mb_stripos' => 4,
  65. 'mb_stristr' => 4,
  66. 'mb_strlen' => 2,
  67. 'mb_strpos' => 4,
  68. 'mb_strrchr' => 4,
  69. 'mb_strrichr' => 4,
  70. 'mb_strripos' => 4,
  71. 'mb_strrpos' => 4,
  72. 'mb_strstr' => 4,
  73. 'mb_strtolower' => 2,
  74. 'mb_strtoupper' => 2,
  75. 'mb_strwidth' => 2,
  76. 'mb_substr_count' => 3,
  77. 'mb_substr' => 4,
  78. );
  79. /**
  80. * Do a version check to determine if this sniff needs to run at all.
  81. *
  82. * Note: This sniff should only trigger errors when both PHP 5.5 or lower,
  83. * as well as PHP 5.6 or higher need to be supported within the application.
  84. *
  85. * @since 9.3.0
  86. *
  87. * @return bool
  88. */
  89. protected function bowOutEarly()
  90. {
  91. return ($this->supportsBelow('5.5') === false || $this->supportsAbove('5.6') === false);
  92. }
  93. /**
  94. * Process the parameters of a matched function.
  95. *
  96. * @since 9.3.0
  97. *
  98. * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
  99. * @param int $stackPtr The position of the current token in the stack.
  100. * @param string $functionName The token content (function name) which was matched.
  101. * @param array $parameters Array with information about the parameters.
  102. *
  103. * @return int|void Integer stack pointer to skip forward or void to continue
  104. * normal file processing.
  105. */
  106. public function processParameters(File $phpcsFile, $stackPtr, $functionName, $parameters)
  107. {
  108. $functionLC = strtolower($functionName);
  109. if ($functionLC === 'iconv_mime_encode') {
  110. // Special case the iconv_mime_encode() function.
  111. return $this->processIconvMimeEncode($phpcsFile, $stackPtr, $functionName, $parameters);
  112. }
  113. if (isset($parameters[$this->targetFunctions[$functionLC]]) === true) {
  114. return;
  115. }
  116. $paramName = '$encoding';
  117. if (strpos($functionLC, 'iconv_') === 0) {
  118. $paramName = '$charset';
  119. } elseif ($functionLC === 'mb_convert_encoding') {
  120. $paramName = '$from_encoding';
  121. }
  122. $error = 'The default value of the %1$s parameter for %2$s() was changed from ISO-8859-1 to UTF-8 in PHP 5.6. For cross-version compatibility, the %1$s parameter should be explicitly set.';
  123. $data = array(
  124. $paramName,
  125. $functionName,
  126. );
  127. $phpcsFile->addError($error, $stackPtr, 'NotSet', $data);
  128. }
  129. /**
  130. * Process the parameters of a matched call to the iconv_mime_encode() function.
  131. *
  132. * @since 9.3.0
  133. *
  134. * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
  135. * @param int $stackPtr The position of the current token in the stack.
  136. * @param string $functionName The token content (function name) which was matched.
  137. * @param array $parameters Array with information about the parameters.
  138. *
  139. * @return int|void Integer stack pointer to skip forward or void to continue
  140. * normal file processing.
  141. */
  142. public function processIconvMimeEncode(File $phpcsFile, $stackPtr, $functionName, $parameters)
  143. {
  144. $error = 'The default value of the %s parameter index for iconv_mime_encode() was changed from ISO-8859-1 to UTF-8 in PHP 5.6. For cross-version compatibility, the %s should be explicitly set.';
  145. $functionLC = strtolower($functionName);
  146. if (isset($parameters[$this->targetFunctions[$functionLC]]) === false) {
  147. $phpcsFile->addError(
  148. $error,
  149. $stackPtr,
  150. 'PreferencesNotSet',
  151. array(
  152. '$preferences[\'input/output-charset\']',
  153. '$preferences[\'input-charset\'] and $preferences[\'output-charset\'] indexes',
  154. )
  155. );
  156. return;
  157. }
  158. $tokens = $phpcsFile->getTokens();
  159. $targetParam = $parameters[$this->targetFunctions[$functionLC]];
  160. $firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $targetParam['start'], ($targetParam['end'] + 1), true);
  161. if ($firstNonEmpty === false) {
  162. // Parse error or live coding.
  163. return;
  164. }
  165. if ($tokens[$firstNonEmpty]['code'] === \T_ARRAY
  166. || $tokens[$firstNonEmpty]['code'] === \T_OPEN_SHORT_ARRAY
  167. ) {
  168. $hasInputCharset = preg_match('`([\'"])input-charset\1\s*=>`', $targetParam['raw']);
  169. $hasOutputCharset = preg_match('`([\'"])output-charset\1\s*=>`', $targetParam['raw']);
  170. if ($hasInputCharset === 1 && $hasOutputCharset === 1) {
  171. // Both input as well as output charset are set.
  172. return;
  173. }
  174. if ($hasInputCharset !== 1) {
  175. $phpcsFile->addError(
  176. $error,
  177. $firstNonEmpty,
  178. 'InputPreferenceNotSet',
  179. array(
  180. '$preferences[\'input-charset\']',
  181. '$preferences[\'input-charset\'] index',
  182. )
  183. );
  184. }
  185. if ($hasOutputCharset !== 1) {
  186. $phpcsFile->addError(
  187. $error,
  188. $firstNonEmpty,
  189. 'OutputPreferenceNotSet',
  190. array(
  191. '$preferences[\'output-charset\']',
  192. '$preferences[\'output-charset\'] index',
  193. )
  194. );
  195. }
  196. return;
  197. }
  198. // The $preferences parameter was passed, but it was a variable/constant/output of a function call.
  199. $phpcsFile->addWarning(
  200. $error,
  201. $firstNonEmpty,
  202. 'Undetermined',
  203. array(
  204. '$preferences[\'input/output-charset\']',
  205. '$preferences[\'input-charset\'] and $preferences[\'output-charset\'] indexes',
  206. )
  207. );
  208. }
  209. }