NativeFunctionCasingFixer.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. <?php
  2. /*
  3. * This file is part of PHP CS Fixer.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  7. *
  8. * This source file is subject to the MIT license that is bundled
  9. * with this source code in the file LICENSE.
  10. */
  11. namespace PhpCsFixer\Fixer\Casing;
  12. use PhpCsFixer\AbstractFixer;
  13. use PhpCsFixer\FixerDefinition\CodeSample;
  14. use PhpCsFixer\FixerDefinition\FixerDefinition;
  15. use PhpCsFixer\Tokenizer\Token;
  16. use PhpCsFixer\Tokenizer\Tokens;
  17. /**
  18. * @author SpacePossum
  19. */
  20. final class NativeFunctionCasingFixer extends AbstractFixer
  21. {
  22. /**
  23. * {@inheritdoc}
  24. */
  25. public function getDefinition()
  26. {
  27. return new FixerDefinition(
  28. 'Function defined by PHP should be called using the correct casing.',
  29. [new CodeSample("<?php\nSTRLEN(\$str);")]
  30. );
  31. }
  32. /**
  33. * {@inheritdoc}
  34. */
  35. public function isCandidate(Tokens $tokens)
  36. {
  37. return $tokens->isTokenKindFound(T_STRING);
  38. }
  39. /**
  40. * {@inheritdoc}
  41. */
  42. protected function applyFix(\SplFileInfo $file, Tokens $tokens)
  43. {
  44. static $nativeFunctionNames = null;
  45. if (null === $nativeFunctionNames) {
  46. $nativeFunctionNames = $this->getNativeFunctionNames();
  47. }
  48. for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
  49. // test if we are at a function all
  50. if (!$tokens[$index]->isGivenKind(T_STRING)) {
  51. continue;
  52. }
  53. $next = $tokens->getNextMeaningfulToken($index);
  54. if (!$tokens[$next]->equals('(')) {
  55. $index = $next;
  56. continue;
  57. }
  58. $functionNamePrefix = $tokens->getPrevMeaningfulToken($index);
  59. if ($tokens[$functionNamePrefix]->isGivenKind([T_DOUBLE_COLON, T_NEW, T_OBJECT_OPERATOR, T_FUNCTION])) {
  60. continue;
  61. }
  62. if ($tokens[$functionNamePrefix]->isGivenKind(T_NS_SEPARATOR)) {
  63. // skip if the call is to a constructor or to a function in a namespace other than the default
  64. $prev = $tokens->getPrevMeaningfulToken($functionNamePrefix);
  65. if ($tokens[$prev]->isGivenKind([T_STRING, T_NEW])) {
  66. continue;
  67. }
  68. }
  69. // test if the function call is to a native PHP function
  70. $lower = strtolower($tokens[$index]->getContent());
  71. if (!array_key_exists($lower, $nativeFunctionNames)) {
  72. continue;
  73. }
  74. $tokens[$index] = new Token([T_STRING, $nativeFunctionNames[$lower]]);
  75. $index = $next;
  76. }
  77. }
  78. /**
  79. * @return array<string, string>
  80. */
  81. private function getNativeFunctionNames()
  82. {
  83. $allFunctions = get_defined_functions();
  84. $functions = [];
  85. foreach ($allFunctions['internal'] as $function) {
  86. $functions[strtolower($function)] = $function;
  87. }
  88. return $functions;
  89. }
  90. }