PageRenderTime 46ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/CodeSniffer/Standards/MySource/Sniffs/Channels/IncludeSystemSniff.php

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