PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/squizlabs/php_codesniffer/src/Standards/MySource/Sniffs/Channels/IncludeSystemSniff.php

https://bitbucket.org/ClintSosa/wp-bedrock
PHP | 314 lines | 242 code | 26 blank | 46 comment | 22 complexity | ad142cf23b2595b9b322a46af17c74f5 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, GPL-2.0
  1. <?php
  2. /**
  3. * Ensures that systems, asset types and libs are included before they are used.
  4. *
  5. * @author Greg Sherwood <gsherwood@squiz.net>
  6. * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
  7. * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
  8. */
  9. namespace PHP_CodeSniffer\Standards\MySource\Sniffs\Channels;
  10. use PHP_CodeSniffer\Sniffs\AbstractScopeSniff;
  11. use PHP_CodeSniffer\Files\File;
  12. use PHP_CodeSniffer\Util\Tokens;
  13. class IncludeSystemSniff extends AbstractScopeSniff
  14. {
  15. /**
  16. * A list of classes that don't need to be included.
  17. *
  18. * @var string[]
  19. */
  20. private $ignore = [
  21. 'self' => true,
  22. 'static' => true,
  23. 'parent' => true,
  24. 'channels' => true,
  25. 'basesystem' => true,
  26. 'dal' => true,
  27. 'init' => true,
  28. 'pdo' => true,
  29. 'util' => true,
  30. 'ziparchive' => true,
  31. 'phpunit_framework_assert' => true,
  32. 'abstractmysourceunittest' => true,
  33. 'abstractdatacleanunittest' => true,
  34. 'exception' => true,
  35. 'abstractwidgetwidgettype' => true,
  36. 'domdocument' => true,
  37. ];
  38. /**
  39. * Constructs a Squiz_Sniffs_Scope_MethodScopeSniff.
  40. */
  41. public function __construct()
  42. {
  43. parent::__construct([T_FUNCTION], [T_DOUBLE_COLON, T_EXTENDS], true);
  44. }//end __construct()
  45. /**
  46. * Processes the function tokens within the class.
  47. *
  48. * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
  49. * @param integer $stackPtr The position where the token was found.
  50. * @param integer $currScope The current scope opener token.
  51. *
  52. * @return void
  53. */
  54. protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope)
  55. {
  56. $tokens = $phpcsFile->getTokens();
  57. // Determine the name of the class that the static function
  58. // is being called on.
  59. $classNameToken = $phpcsFile->findPrevious(
  60. T_WHITESPACE,
  61. ($stackPtr - 1),
  62. null,
  63. true
  64. );
  65. // Don't process class names represented by variables as this can be
  66. // an inexact science.
  67. if ($tokens[$classNameToken]['code'] === T_VARIABLE) {
  68. return;
  69. }
  70. $className = $tokens[$classNameToken]['content'];
  71. if (isset($this->ignore[strtolower($className)]) === true) {
  72. return;
  73. }
  74. $includedClasses = [];
  75. $fileName = strtolower($phpcsFile->getFilename());
  76. $matches = [];
  77. if (preg_match('|/systems/(.*)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) {
  78. // This is an actions file, which means we don't
  79. // have to include the system in which it exists.
  80. $includedClasses[$matches[2]] = true;
  81. // Or a system it implements.
  82. $class = $phpcsFile->getCondition($stackPtr, T_CLASS);
  83. $implements = $phpcsFile->findNext(T_IMPLEMENTS, $class, ($class + 10));
  84. if ($implements !== false) {
  85. $implementsClass = $phpcsFile->findNext(T_STRING, $implements);
  86. $implementsClassName = strtolower($tokens[$implementsClass]['content']);
  87. if (substr($implementsClassName, -7) === 'actions') {
  88. $includedClasses[substr($implementsClassName, 0, -7)] = true;
  89. }
  90. }
  91. }
  92. // Go searching for includeSystem and includeAsset calls within this
  93. // function, or the inclusion of .inc files, which
  94. // would be library files.
  95. for ($i = ($currScope + 1); $i < $stackPtr; $i++) {
  96. $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
  97. if ($name !== false) {
  98. $includedClasses[$name] = true;
  99. // Special case for Widgets cause they are, well, special.
  100. } else if (strtolower($tokens[$i]['content']) === 'includewidget') {
  101. $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($i + 1));
  102. $typeName = trim($tokens[$typeName]['content'], " '");
  103. $includedClasses[strtolower($typeName).'widgettype'] = true;
  104. }
  105. }
  106. // Now go searching for includeSystem, includeAsset or require/include
  107. // calls outside our scope. If we are in a class, look outside the
  108. // class. If we are not, look outside the function.
  109. $condPtr = $currScope;
  110. if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
  111. foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
  112. if ($condType === T_CLASS) {
  113. break;
  114. }
  115. }
  116. }
  117. for ($i = 0; $i < $condPtr; $i++) {
  118. // Skip other scopes.
  119. if (isset($tokens[$i]['scope_closer']) === true) {
  120. $i = $tokens[$i]['scope_closer'];
  121. continue;
  122. }
  123. $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
  124. if ($name !== false) {
  125. $includedClasses[$name] = true;
  126. }
  127. }
  128. // If we are in a testing class, we might have also included
  129. // some systems and classes in our setUp() method.
  130. $setupFunction = null;
  131. if ($phpcsFile->hasCondition($stackPtr, T_CLASS) === true) {
  132. foreach ($tokens[$stackPtr]['conditions'] as $condPtr => $condType) {
  133. if ($condType === T_CLASS) {
  134. // Is this is a testing class?
  135. $name = $phpcsFile->findNext(T_STRING, $condPtr);
  136. $name = $tokens[$name]['content'];
  137. if (substr($name, -8) === 'UnitTest') {
  138. // Look for a method called setUp().
  139. $end = $tokens[$condPtr]['scope_closer'];
  140. $function = $phpcsFile->findNext(T_FUNCTION, ($condPtr + 1), $end);
  141. while ($function !== false) {
  142. $name = $phpcsFile->findNext(T_STRING, $function);
  143. if ($tokens[$name]['content'] === 'setUp') {
  144. $setupFunction = $function;
  145. break;
  146. }
  147. $function = $phpcsFile->findNext(T_FUNCTION, ($function + 1), $end);
  148. }
  149. }
  150. }
  151. }//end foreach
  152. }//end if
  153. if ($setupFunction !== null) {
  154. $start = ($tokens[$setupFunction]['scope_opener'] + 1);
  155. $end = $tokens[$setupFunction]['scope_closer'];
  156. for ($i = $start; $i < $end; $i++) {
  157. $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
  158. if ($name !== false) {
  159. $includedClasses[$name] = true;
  160. }
  161. }
  162. }//end if
  163. if (isset($includedClasses[strtolower($className)]) === false) {
  164. $error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
  165. $data = [$className];
  166. $phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data);
  167. }
  168. }//end processTokenWithinScope()
  169. /**
  170. * Processes a token within the scope that this test is listening to.
  171. *
  172. * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
  173. * @param int $stackPtr The position in the stack where
  174. * this token was found.
  175. *
  176. * @return void
  177. */
  178. protected function processTokenOutsideScope(File $phpcsFile, $stackPtr)
  179. {
  180. $tokens = $phpcsFile->getTokens();
  181. if ($tokens[$stackPtr]['code'] === T_EXTENDS) {
  182. // Find the class name.
  183. $classNameToken = $phpcsFile->findNext(T_STRING, ($stackPtr + 1));
  184. $className = $tokens[$classNameToken]['content'];
  185. } else {
  186. // Determine the name of the class that the static function
  187. // is being called on. But don't process class names represented by
  188. // variables as this can be an inexact science.
  189. $classNameToken = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
  190. if ($tokens[$classNameToken]['code'] === T_VARIABLE) {
  191. return;
  192. }
  193. $className = $tokens[$classNameToken]['content'];
  194. }
  195. // Some systems are always available.
  196. if (isset($this->ignore[strtolower($className)]) === true) {
  197. return;
  198. }
  199. $includedClasses = [];
  200. $fileName = strtolower($phpcsFile->getFilename());
  201. $matches = [];
  202. if (preg_match('|/systems/([^/]+)/([^/]+)?actions.inc$|', $fileName, $matches) !== 0) {
  203. // This is an actions file, which means we don't
  204. // have to include the system in which it exists
  205. // We know the system from the path.
  206. $includedClasses[$matches[1]] = true;
  207. }
  208. // Go searching for includeSystem, includeAsset or require/include
  209. // calls outside our scope.
  210. for ($i = 0; $i < $stackPtr; $i++) {
  211. // Skip classes and functions as will we never get
  212. // into their scopes when including this file, although
  213. // we have a chance of getting into IF's, WHILE's etc.
  214. if (($tokens[$i]['code'] === T_CLASS
  215. || $tokens[$i]['code'] === T_INTERFACE
  216. || $tokens[$i]['code'] === T_FUNCTION)
  217. && isset($tokens[$i]['scope_closer']) === true
  218. ) {
  219. $i = $tokens[$i]['scope_closer'];
  220. continue;
  221. }
  222. $name = $this->getIncludedClassFromToken($phpcsFile, $tokens, $i);
  223. if ($name !== false) {
  224. $includedClasses[$name] = true;
  225. // Special case for Widgets cause they are, well, special.
  226. } else if (strtolower($tokens[$i]['content']) === 'includewidget') {
  227. $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($i + 1));
  228. $typeName = trim($tokens[$typeName]['content'], " '");
  229. $includedClasses[strtolower($typeName).'widgettype'] = true;
  230. }
  231. }//end for
  232. if (isset($includedClasses[strtolower($className)]) === false) {
  233. if ($tokens[$stackPtr]['code'] === T_EXTENDS) {
  234. $error = 'Class extends non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
  235. $data = [$className];
  236. $phpcsFile->addError($error, $stackPtr, 'NotIncludedExtends', $data);
  237. } else {
  238. $error = 'Static method called on non-included class or system "%s"; include system with Channels::includeSystem() or include class with require_once';
  239. $data = [$className];
  240. $phpcsFile->addError($error, $stackPtr, 'NotIncludedCall', $data);
  241. }
  242. }
  243. }//end processTokenOutsideScope()
  244. /**
  245. * Determines the included class name from given token.
  246. *
  247. * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
  248. * @param array $tokens The array of file tokens.
  249. * @param int $stackPtr The position in the tokens array of the
  250. * potentially included class.
  251. *
  252. * @return string
  253. */
  254. protected function getIncludedClassFromToken(File $phpcsFile, array $tokens, $stackPtr)
  255. {
  256. if (strtolower($tokens[$stackPtr]['content']) === 'includesystem') {
  257. $systemName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
  258. $systemName = trim($tokens[$systemName]['content'], " '");
  259. return strtolower($systemName);
  260. } else if (strtolower($tokens[$stackPtr]['content']) === 'includeasset') {
  261. $typeName = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
  262. $typeName = trim($tokens[$typeName]['content'], " '");
  263. return strtolower($typeName).'assettype';
  264. } else if (isset(Tokens::$includeTokens[$tokens[$stackPtr]['code']]) === true) {
  265. $filePath = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, ($stackPtr + 1));
  266. $filePath = $tokens[$filePath]['content'];
  267. $filePath = trim($filePath, " '");
  268. $filePath = basename($filePath, '.inc');
  269. return strtolower($filePath);
  270. }
  271. return false;
  272. }//end getIncludedClassFromToken()
  273. }//end class