PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/PHPCompatibility/Sniffs/ParameterValues/NewIconvMbstringCharsetDefaultSniff.php

http://github.com/wimg/PHPCompatibility
PHP | 232 lines | 131 code | 22 blank | 79 comment | 12 complexity | 8a95015f13c8f11d77485a150a844665 MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?php
  2. /**
  3. * PHPCompatibility, an external standard for PHP_CodeSniffer.
  4. *
  5. * @package PHPCompatibility
  6. * @copyright 2012-2020 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\Files\File;
  13. use PHP_CodeSniffer\Util\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 = [
  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\Files\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 = [
  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\Files\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. [
  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. // Note: the item names are treated case-sensitively in PHP, so match on exact case.
  169. $hasInputCharset = \preg_match('`([\'"])input-charset\1\s*=>`', $targetParam['clean']);
  170. $hasOutputCharset = \preg_match('`([\'"])output-charset\1\s*=>`', $targetParam['clean']);
  171. if ($hasInputCharset === 1 && $hasOutputCharset === 1) {
  172. // Both input as well as output charset are set.
  173. return;
  174. }
  175. if ($hasInputCharset !== 1) {
  176. $phpcsFile->addError(
  177. $error,
  178. $firstNonEmpty,
  179. 'InputPreferenceNotSet',
  180. [
  181. '$preferences[\'input-charset\']',
  182. '$preferences[\'input-charset\'] index',
  183. ]
  184. );
  185. }
  186. if ($hasOutputCharset !== 1) {
  187. $phpcsFile->addError(
  188. $error,
  189. $firstNonEmpty,
  190. 'OutputPreferenceNotSet',
  191. [
  192. '$preferences[\'output-charset\']',
  193. '$preferences[\'output-charset\'] index',
  194. ]
  195. );
  196. }
  197. return;
  198. }
  199. // The $preferences parameter was passed, but it was a variable/constant/output of a function call.
  200. $phpcsFile->addWarning(
  201. $error,
  202. $firstNonEmpty,
  203. 'Undetermined',
  204. [
  205. '$preferences[\'input/output-charset\']',
  206. '$preferences[\'input-charset\'] and $preferences[\'output-charset\'] indexes',
  207. ]
  208. );
  209. }
  210. }