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 15/** 16 * Ensures that systems and asset types are used if they are included. 17 * 18 * @category PHP 19 * @package PHP_CodeSniffer_MySource 20 * @author Greg Sherwood <gsherwood@squiz.net> 21 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 22 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 23 * @version Release: @package_version@ 24 * @link http://pear.php.net/package/PHP_CodeSniffer 25 */ 26class MySource_Sniffs_Channels_UnusedSystemSniff implements PHP_CodeSniffer_Sniff 27{ 28 29 30 /** 31 * Returns an array of tokens this test wants to listen for. 32 * 33 * @return array 34 */ 35 public function register() 36 { 37 return array(T_DOUBLE_COLON); 38 39 }//end register() 40 41 42 /** 43 * Processes this sniff, when one of its tokens is encountered. 44 * 45 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 46 * @param int $stackPtr The position of the current token in 47 * the stack passed in $tokens. 48 * 49 * @return void 50 */ 51 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 52 { 53 $tokens = $phpcsFile->getTokens(); 54 55 // Check if this is a call to includeSystem, includeAsset or includeWidget. 56 $methodName = strtolower($tokens[($stackPtr + 1)]['content']); 57 if ($methodName === 'includesystem' 58 || $methodName === 'includeasset' 59 || $methodName === 'includewidget' 60 ) { 61 $systemName = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 3), null, true); 62 if ($systemName === false || $tokens[$systemName]['code'] !== T_CONSTANT_ENCAPSED_STRING) { 63 // Must be using a variable instead of a specific system name. 64 // We can't accurately check that. 65 return; 66 } 67 68 $systemName = trim($tokens[$systemName]['content'], " '"); 69 } else { 70 return; 71 } 72 73 if ($methodName === 'includeasset') { 74 $systemName .= 'assettype'; 75 } else if ($methodName === 'includewidget') { 76 $systemName .= 'widgettype'; 77 } 78 79 $systemName = strtolower($systemName); 80 81 // Now check if this system is used anywhere in this scope. 82 $level = $tokens[$stackPtr]['level']; 83 for ($i = ($stackPtr + 1); $i < $phpcsFile->numTokens; $i++) { 84 if ($tokens[$i]['level'] < $level) { 85 // We have gone out of scope. 86 // If the original include was inside an IF statement that 87 // is checking if the system exists, check the outer scope 88 // as well. 89 if ($tokens[$stackPtr]['level'] === $level) { 90 // We are still in the base level, so this is the first 91 // time we have got here. 92 $conditions = array_keys($tokens[$stackPtr]['conditions']); 93 if (empty($conditions) === false) { 94 $cond = array_pop($conditions); 95 if ($tokens[$cond]['code'] === T_IF) { 96 $i = $tokens[$cond]['scope_closer']; 97 $level--; 98 continue; 99 } 100 } 101 } 102 103 break; 104 }//end if 105 106 if ($tokens[$i]['code'] !== T_DOUBLE_COLON 107 && $tokens[$i]['code'] !== T_EXTENDS 108 && $tokens[$i]['code'] !== T_IMPLEMENTS 109 ) { 110 continue; 111 } 112 113 switch ($tokens[$i]['code']) { 114 case T_DOUBLE_COLON: 115 $usedName = strtolower($tokens[($i - 1)]['content']); 116 if ($usedName === $systemName) { 117 // The included system was used, so it is fine. 118 return; 119 } 120 break; 121 case T_EXTENDS: 122 $classNameToken = $phpcsFile->findNext(T_STRING, ($i + 1)); 123 $className = strtolower($tokens[$classNameToken]['content']); 124 if ($className === $systemName) { 125 // The included system was used, so it is fine. 126 return; 127 } 128 break; 129 case T_IMPLEMENTS: 130 $endImplements = $phpcsFile->findNext(array(T_EXTENDS, T_OPEN_CURLY_BRACKET), ($i + 1)); 131 for ($x = ($i + 1); $x < $endImplements; $x++) { 132 if ($tokens[$x]['code'] === T_STRING) { 133 $className = strtolower($tokens[$x]['content']); 134 if ($className === $systemName) { 135 // The included system was used, so it is fine. 136 return; 137 } 138 } 139 } 140 break; 141 }//end switch 142 }//end for 143 144 // If we get to here, the system was not use. 145 $error = 'Included system "%s" is never used'; 146 $data = array($systemName); 147 $phpcsFile->addError($error, $stackPtr, 'Found', $data); 148 149 }//end process() 150 151 152}//end class 153