NoAliasFunctionsFixer.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Fixer\Alias;
  13. use PhpCsFixer\AbstractFixer;
  14. use PhpCsFixer\Fixer\ConfigurableFixerInterface;
  15. use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
  16. use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
  17. use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
  18. use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
  19. use PhpCsFixer\FixerDefinition\CodeSample;
  20. use PhpCsFixer\FixerDefinition\FixerDefinition;
  21. use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
  22. use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
  23. use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
  24. use PhpCsFixer\Tokenizer\Token;
  25. use PhpCsFixer\Tokenizer\Tokens;
  26. /**
  27. * @author Vladimir Reznichenko <kalessil@gmail.com>
  28. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  29. */
  30. final class NoAliasFunctionsFixer extends AbstractFixer implements ConfigurableFixerInterface
  31. {
  32. private const SETS = [
  33. '@internal' => [
  34. 'diskfreespace' => 'disk_free_space',
  35. 'dns_check_record' => 'checkdnsrr',
  36. 'dns_get_mx' => 'getmxrr',
  37. 'session_commit' => 'session_write_close',
  38. 'stream_register_wrapper' => 'stream_wrapper_register',
  39. 'set_file_buffer' => 'stream_set_write_buffer',
  40. 'socket_set_blocking' => 'stream_set_blocking',
  41. 'socket_get_status' => 'stream_get_meta_data',
  42. 'socket_set_timeout' => 'stream_set_timeout',
  43. 'socket_getopt' => 'socket_get_option',
  44. 'socket_setopt' => 'socket_set_option',
  45. 'chop' => 'rtrim',
  46. 'close' => 'closedir',
  47. 'doubleval' => 'floatval',
  48. 'fputs' => 'fwrite',
  49. 'get_required_files' => 'get_included_files',
  50. 'ini_alter' => 'ini_set',
  51. 'is_double' => 'is_float',
  52. 'is_integer' => 'is_int',
  53. 'is_long' => 'is_int',
  54. 'is_real' => 'is_float',
  55. 'is_writeable' => 'is_writable',
  56. 'join' => 'implode',
  57. 'key_exists' => 'array_key_exists',
  58. 'magic_quotes_runtime' => 'set_magic_quotes_runtime',
  59. 'pos' => 'current',
  60. 'show_source' => 'highlight_file',
  61. 'sizeof' => 'count',
  62. 'strchr' => 'strstr',
  63. 'user_error' => 'trigger_error',
  64. ],
  65. '@IMAP' => [
  66. 'imap_create' => 'imap_createmailbox',
  67. 'imap_fetchtext' => 'imap_body',
  68. 'imap_header' => 'imap_headerinfo',
  69. 'imap_listmailbox' => 'imap_list',
  70. 'imap_listsubscribed' => 'imap_lsub',
  71. 'imap_rename' => 'imap_renamemailbox',
  72. 'imap_scan' => 'imap_listscan',
  73. 'imap_scanmailbox' => 'imap_listscan',
  74. ],
  75. '@ldap' => [
  76. 'ldap_close' => 'ldap_unbind',
  77. 'ldap_modify' => 'ldap_mod_replace',
  78. ],
  79. '@mysqli' => [
  80. 'mysqli_execute' => 'mysqli_stmt_execute',
  81. 'mysqli_set_opt' => 'mysqli_options',
  82. 'mysqli_escape_string' => 'mysqli_real_escape_string',
  83. ],
  84. '@pg' => [
  85. 'pg_exec' => 'pg_query',
  86. ],
  87. '@oci' => [
  88. 'oci_free_cursor' => 'oci_free_statement',
  89. ],
  90. '@odbc' => [
  91. 'odbc_do' => 'odbc_exec',
  92. 'odbc_field_precision' => 'odbc_field_len',
  93. ],
  94. '@mbreg' => [
  95. 'mbereg' => 'mb_ereg',
  96. 'mbereg_match' => 'mb_ereg_match',
  97. 'mbereg_replace' => 'mb_ereg_replace',
  98. 'mbereg_search' => 'mb_ereg_search',
  99. 'mbereg_search_getpos' => 'mb_ereg_search_getpos',
  100. 'mbereg_search_getregs' => 'mb_ereg_search_getregs',
  101. 'mbereg_search_init' => 'mb_ereg_search_init',
  102. 'mbereg_search_pos' => 'mb_ereg_search_pos',
  103. 'mbereg_search_regs' => 'mb_ereg_search_regs',
  104. 'mbereg_search_setpos' => 'mb_ereg_search_setpos',
  105. 'mberegi' => 'mb_eregi',
  106. 'mberegi_replace' => 'mb_eregi_replace',
  107. 'mbregex_encoding' => 'mb_regex_encoding',
  108. 'mbsplit' => 'mb_split',
  109. ],
  110. '@openssl' => [
  111. 'openssl_get_publickey' => 'openssl_pkey_get_public',
  112. 'openssl_get_privatekey' => 'openssl_pkey_get_private',
  113. ],
  114. '@sodium' => [
  115. 'sodium_crypto_scalarmult_base' => 'sodium_crypto_box_publickey_from_secretkey',
  116. ],
  117. '@exif' => [
  118. 'read_exif_data' => 'exif_read_data',
  119. ],
  120. '@ftp' => [
  121. 'ftp_quit' => 'ftp_close',
  122. ],
  123. '@posix' => [
  124. 'posix_errno' => 'posix_get_last_error',
  125. ],
  126. '@pcntl' => [
  127. 'pcntl_errno' => 'pcntl_get_last_error',
  128. ],
  129. '@time' => [
  130. 'mktime' => ['time', 0],
  131. 'gmmktime' => ['time', 0],
  132. ],
  133. ];
  134. /**
  135. * @var array<string, array{string, int}|string> stores alias (key) - master (value) functions mapping
  136. */
  137. private array $aliases = [];
  138. public function configure(array $configuration): void
  139. {
  140. parent::configure($configuration);
  141. $this->aliases = [];
  142. foreach ($this->configuration['sets'] as $set) {
  143. if ('@all' === $set) {
  144. $this->aliases = array_merge(...array_values(self::SETS));
  145. break;
  146. }
  147. $this->aliases = array_merge($this->aliases, self::SETS[$set]);
  148. }
  149. }
  150. public function getDefinition(): FixerDefinitionInterface
  151. {
  152. return new FixerDefinition(
  153. 'Master functions shall be used instead of aliases.',
  154. [
  155. new CodeSample(
  156. '<?php
  157. $a = chop($b);
  158. close($b);
  159. $a = doubleval($b);
  160. $a = fputs($b, $c);
  161. $a = get_required_files();
  162. ini_alter($b, $c);
  163. $a = is_double($b);
  164. $a = is_integer($b);
  165. $a = is_long($b);
  166. $a = is_real($b);
  167. $a = is_writeable($b);
  168. $a = join($glue, $pieces);
  169. $a = key_exists($key, $array);
  170. magic_quotes_runtime($new_setting);
  171. $a = pos($array);
  172. $a = show_source($filename, true);
  173. $a = sizeof($b);
  174. $a = strchr($haystack, $needle);
  175. $a = imap_header($imap_stream, 1);
  176. user_error($message);
  177. mbereg_search_getregs();
  178. '
  179. ),
  180. new CodeSample(
  181. '<?php
  182. $a = is_double($b);
  183. mbereg_search_getregs();
  184. ',
  185. ['sets' => ['@mbreg']]
  186. ),
  187. ],
  188. null,
  189. 'Risky when any of the alias functions are overridden.'
  190. );
  191. }
  192. /**
  193. * {@inheritdoc}
  194. *
  195. * Must run before ImplodeCallFixer, PhpUnitDedicateAssertFixer.
  196. */
  197. public function getPriority(): int
  198. {
  199. return 40;
  200. }
  201. public function isCandidate(Tokens $tokens): bool
  202. {
  203. return $tokens->isTokenKindFound(T_STRING);
  204. }
  205. public function isRisky(): bool
  206. {
  207. return true;
  208. }
  209. protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
  210. {
  211. $functionsAnalyzer = new FunctionsAnalyzer();
  212. $argumentsAnalyzer = new ArgumentsAnalyzer();
  213. /** @var Token $token */
  214. foreach ($tokens->findGivenKind(T_STRING) as $index => $token) {
  215. // check mapping hit
  216. $tokenContent = strtolower($token->getContent());
  217. if (!isset($this->aliases[$tokenContent])) {
  218. continue;
  219. }
  220. // skip expressions without parameters list
  221. $openParenthesis = $tokens->getNextMeaningfulToken($index);
  222. if (!$tokens[$openParenthesis]->equals('(')) {
  223. continue;
  224. }
  225. if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
  226. continue;
  227. }
  228. if (\is_array($this->aliases[$tokenContent])) {
  229. [$alias, $numberOfArguments] = $this->aliases[$tokenContent];
  230. $count = $argumentsAnalyzer->countArguments($tokens, $openParenthesis, $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis));
  231. if ($numberOfArguments !== $count) {
  232. continue;
  233. }
  234. } else {
  235. $alias = $this->aliases[$tokenContent];
  236. }
  237. $tokens[$index] = new Token([T_STRING, $alias]);
  238. }
  239. }
  240. protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
  241. {
  242. $sets = [
  243. '@all' => 'all listed sets',
  244. '@internal' => 'native functions',
  245. '@exif' => 'EXIF functions',
  246. '@ftp' => 'FTP functions',
  247. '@IMAP' => 'IMAP functions',
  248. '@ldap' => 'LDAP functions',
  249. '@mbreg' => 'from `ext-mbstring`',
  250. '@mysqli' => 'mysqli functions',
  251. '@oci' => 'oci functions',
  252. '@odbc' => 'odbc functions',
  253. '@openssl' => 'openssl functions',
  254. '@pcntl' => 'PCNTL functions',
  255. '@pg' => 'pg functions',
  256. '@posix' => 'POSIX functions',
  257. '@snmp' => 'SNMP functions', // @TODO Remove on next major 4.0 as this set is now empty
  258. '@sodium' => 'libsodium functions',
  259. '@time' => 'time functions',
  260. ];
  261. $list = "List of sets to fix. Defined sets are:\n\n";
  262. foreach ($sets as $set => $description) {
  263. $list .= sprintf("* `%s` (%s);\n", $set, $description);
  264. }
  265. $list = rtrim($list, ";\n").'.';
  266. return new FixerConfigurationResolver([
  267. (new FixerOptionBuilder('sets', $list))
  268. ->setAllowedTypes(['string[]'])
  269. ->setAllowedValues([new AllowedValueSubset(array_keys($sets))])
  270. ->setDefault(['@internal', '@IMAP', '@pg'])
  271. ->getOption(),
  272. ]);
  273. }
  274. }