Utils.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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;
  13. use PhpCsFixer\Fixer\FixerInterface;
  14. use PhpCsFixer\Tokenizer\Token;
  15. /**
  16. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  17. * @author Graham Campbell <hello@gjcampbell.co.uk>
  18. * @author Odín del Río <odin.drp@gmail.com>
  19. *
  20. * @internal
  21. */
  22. final class Utils
  23. {
  24. /**
  25. * @var array<string,true>
  26. */
  27. private static $deprecations = [];
  28. private function __construct()
  29. {
  30. // cannot create instance of util. class
  31. }
  32. /**
  33. * Converts a camel cased string to a snake cased string.
  34. */
  35. public static function camelCaseToUnderscore(string $string): string
  36. {
  37. return mb_strtolower(Preg::replace('/(?<!^)((?=[\p{Lu}][^\p{Lu}])|(?<![\p{Lu}])(?=[\p{Lu}]))/', '_', $string));
  38. }
  39. /**
  40. * Calculate the trailing whitespace.
  41. *
  42. * What we're doing here is grabbing everything after the final newline.
  43. */
  44. public static function calculateTrailingWhitespaceIndent(Token $token): string
  45. {
  46. if (!$token->isWhitespace()) {
  47. throw new \InvalidArgumentException(sprintf('The given token must be whitespace, got "%s".', $token->getName()));
  48. }
  49. $str = strrchr(
  50. str_replace(["\r\n", "\r"], "\n", $token->getContent()),
  51. "\n"
  52. );
  53. if (false === $str) {
  54. return '';
  55. }
  56. return ltrim($str, "\n");
  57. }
  58. /**
  59. * Perform stable sorting using provided comparison function.
  60. *
  61. * Stability is ensured by using Schwartzian transform.
  62. *
  63. * @param mixed[] $elements
  64. * @param callable $getComparedValue a callable that takes a single element and returns the value to compare
  65. * @param callable $compareValues a callable that compares two values
  66. *
  67. * @return mixed[]
  68. */
  69. public static function stableSort(array $elements, callable $getComparedValue, callable $compareValues): array
  70. {
  71. array_walk($elements, static function (&$element, int $index) use ($getComparedValue): void {
  72. $element = [$element, $index, $getComparedValue($element)];
  73. });
  74. usort($elements, static function ($a, $b) use ($compareValues): int {
  75. $comparison = $compareValues($a[2], $b[2]);
  76. if (0 !== $comparison) {
  77. return $comparison;
  78. }
  79. return $a[1] <=> $b[1];
  80. });
  81. return array_map(static function (array $item) {
  82. return $item[0];
  83. }, $elements);
  84. }
  85. /**
  86. * Sort fixers by their priorities.
  87. *
  88. * @param FixerInterface[] $fixers
  89. *
  90. * @return FixerInterface[]
  91. */
  92. public static function sortFixers(array $fixers): array
  93. {
  94. // Schwartzian transform is used to improve the efficiency and avoid
  95. // `usort(): Array was modified by the user comparison function` warning for mocked objects.
  96. return self::stableSort(
  97. $fixers,
  98. static function (FixerInterface $fixer): int {
  99. return $fixer->getPriority();
  100. },
  101. static function (int $a, int $b): int {
  102. return $b <=> $a;
  103. }
  104. );
  105. }
  106. /**
  107. * Join names in natural language wrapped in backticks, e.g. `a`, `b` and `c`.
  108. *
  109. * @param string[] $names
  110. *
  111. * @throws \InvalidArgumentException
  112. */
  113. public static function naturalLanguageJoinWithBackticks(array $names): string
  114. {
  115. if (empty($names)) {
  116. throw new \InvalidArgumentException('Array of names cannot be empty.');
  117. }
  118. $names = array_map(static function (string $name): string {
  119. return sprintf('`%s`', $name);
  120. }, $names);
  121. $last = array_pop($names);
  122. if (\count($names) > 0) {
  123. return implode(', ', $names).' and '.$last;
  124. }
  125. return $last;
  126. }
  127. public static function triggerDeprecation(\Exception $futureException): void
  128. {
  129. if (getenv('PHP_CS_FIXER_FUTURE_MODE')) {
  130. throw new \RuntimeException(
  131. 'Your are using something deprecated, see previous exception. Aborting execution because `PHP_CS_FIXER_FUTURE_MODE` environment variable is set.',
  132. 0,
  133. $futureException
  134. );
  135. }
  136. $message = $futureException->getMessage();
  137. self::$deprecations[$message] = true;
  138. @trigger_error($message, E_USER_DEPRECATED);
  139. }
  140. public static function getTriggeredDeprecations(): array
  141. {
  142. $triggeredDeprecations = array_keys(self::$deprecations);
  143. sort($triggeredDeprecations);
  144. return $triggeredDeprecations;
  145. }
  146. }