LiteralNamespacesSniff.php 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Sniffs\LiteralNamespaces;
  7. use PHP_CodeSniffer\Sniffs\Sniff;
  8. use PHP_CodeSniffer\Files\File;
  9. /**
  10. * Custom phpcs sniff to detect usages of literal class and interface names.
  11. */
  12. class LiteralNamespacesSniff implements Sniff
  13. {
  14. /**
  15. * @var string
  16. */
  17. private $literalNamespacePattern = '/^[\\\]{0,2}[A-Z][A-Za-z]+([\\\]{1,2}[A-Z][A-Za-z]+){2,}(?!\\\+)$/';
  18. /**
  19. * @var array
  20. */
  21. private $classNames = [];
  22. /**
  23. * @inheritdoc
  24. */
  25. public function register()
  26. {
  27. return [
  28. T_CONSTANT_ENCAPSED_STRING,
  29. T_DOUBLE_QUOTED_STRING,
  30. ];
  31. }
  32. /**
  33. * @inheritdoc
  34. */
  35. public function process(File $sourceFile, $stackPtr)
  36. {
  37. $tokens = $sourceFile->getTokens();
  38. if ($sourceFile->findPrevious(T_STRING_CONCAT, $stackPtr, $stackPtr - 3) ||
  39. $sourceFile->findNext(T_STRING_CONCAT, $stackPtr, $stackPtr + 3)
  40. ) {
  41. return;
  42. }
  43. $content = trim($tokens[$stackPtr]['content'], "\"'");
  44. // replace double slashes from class name for avoiding problems with class autoload
  45. if (strpos($content, '\\') !== false) {
  46. $content = preg_replace('|\\\{2,}|', '\\', $content);
  47. }
  48. if (preg_match($this->literalNamespacePattern, $content) === 1 && $this->classExists($content)) {
  49. $sourceFile->addError(
  50. "Use ::class notation instead.",
  51. $stackPtr,
  52. 'LiteralClassUsage'
  53. );
  54. }
  55. }
  56. /**
  57. * @param string $className
  58. * @return bool
  59. */
  60. private function classExists($className)
  61. {
  62. if (!isset($this->classNames[$className])) {
  63. $this->classNames[$className] = class_exists($className) || interface_exists($className);
  64. }
  65. return $this->classNames[$className];
  66. }
  67. }