ForbiddenBreakContinueOutsideLoopSniff.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. <?php
  2. /**
  3. * PHPCompatibility, an external standard for PHP_CodeSniffer.
  4. *
  5. * @package PHPCompatibility
  6. * @copyright 2012-2019 PHPCompatibility Contributors
  7. * @license https://opensource.org/licenses/LGPL-3.0 LGPL3
  8. * @link https://github.com/PHPCompatibility/PHPCompatibility
  9. */
  10. namespace PHPCompatibility\Sniffs\ControlStructures;
  11. use PHPCompatibility\Sniff;
  12. use PHP_CodeSniffer_File as File;
  13. /**
  14. * Detect using `break` and/or `continue` statements outside of a looping structure.
  15. *
  16. * PHP version 7.0
  17. *
  18. * @link https://www.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.other.break-continue
  19. * @link https://www.php.net/manual/en/control-structures.break.php
  20. * @link https://www.php.net/manual/en/control-structures.continue.php
  21. *
  22. * @since 7.0.7
  23. */
  24. class ForbiddenBreakContinueOutsideLoopSniff extends Sniff
  25. {
  26. /**
  27. * Token codes of control structure in which usage of break/continue is valid.
  28. *
  29. * @since 7.0.7
  30. *
  31. * @var array
  32. */
  33. protected $validLoopStructures = array(
  34. \T_FOR => true,
  35. \T_FOREACH => true,
  36. \T_WHILE => true,
  37. \T_DO => true,
  38. \T_SWITCH => true,
  39. );
  40. /**
  41. * Token codes which did not correctly get a condition assigned in older PHPCS versions.
  42. *
  43. * @since 7.0.7
  44. *
  45. * @var array
  46. */
  47. protected $backCompat = array(
  48. \T_CASE => true,
  49. \T_DEFAULT => true,
  50. );
  51. /**
  52. * Returns an array of tokens this test wants to listen for.
  53. *
  54. * @since 7.0.7
  55. *
  56. * @return array
  57. */
  58. public function register()
  59. {
  60. return array(
  61. \T_BREAK,
  62. \T_CONTINUE,
  63. );
  64. }
  65. /**
  66. * Processes this test, when one of its tokens is encountered.
  67. *
  68. * @since 7.0.7
  69. *
  70. * @param \PHP_CodeSniffer_File $phpcsFile The file being scanned.
  71. * @param int $stackPtr The position of the current token in the
  72. * stack passed in $tokens.
  73. *
  74. * @return void
  75. */
  76. public function process(File $phpcsFile, $stackPtr)
  77. {
  78. $tokens = $phpcsFile->getTokens();
  79. $token = $tokens[$stackPtr];
  80. // Check if the break/continue is within a valid loop structure.
  81. if (empty($token['conditions']) === false) {
  82. foreach ($token['conditions'] as $tokenCode) {
  83. if (isset($this->validLoopStructures[$tokenCode]) === true) {
  84. return;
  85. }
  86. }
  87. } else {
  88. // Deal with older PHPCS versions.
  89. if (isset($token['scope_condition']) === true && isset($this->backCompat[$tokens[$token['scope_condition']]['code']]) === true) {
  90. return;
  91. }
  92. }
  93. // If we're still here, no valid loop structure container has been found, so throw an error.
  94. $error = "Using '%s' outside of a loop or switch structure is invalid";
  95. $isError = false;
  96. $errorCode = 'Found';
  97. $data = array($token['content']);
  98. if ($this->supportsAbove('7.0')) {
  99. $error .= ' and will throw a fatal error since PHP 7.0';
  100. $isError = true;
  101. $errorCode = 'FatalError';
  102. }
  103. $this->addMessage($phpcsFile, $error, $stackPtr, $isError, $errorCode, $data);
  104. }
  105. }