1<?php 2/** 3 * This file is part of the CodeAnalysis add-on for PHP_CodeSniffer. 4 * 5 * PHP version 5 6 * 7 * @category PHP 8 * @package PHP_CodeSniffer 9 * @author Greg Sherwood <gsherwood@squiz.net> 10 * @author Manuel Pichler <mapi@manuel-pichler.de> 11 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 12 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 13 * @link http://pear.php.net/package/PHP_CodeSniffer 14 */ 15 16/** 17 * Checks the for unused function parameters. 18 * 19 * This sniff checks that all function parameters are used in the function body. 20 * One exception is made for empty function bodies or function bodies that only 21 * contain comments. This could be useful for the classes that implement an 22 * interface that defines multiple methods but the implementation only needs some 23 * of them. 24 * 25 * @category PHP 26 * @package PHP_CodeSniffer 27 * @author Manuel Pichler <mapi@manuel-pichler.de> 28 * @author Greg Sherwood <gsherwood@squiz.net> 29 * @copyright 2007-2014 Manuel Pichler. All rights reserved. 30 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 31 * @version Release: @package_version@ 32 * @link http://pear.php.net/package/PHP_CodeSniffer 33 */ 34class Generic_Sniffs_CodeAnalysis_UnusedFunctionParameterSniff implements PHP_CodeSniffer_Sniff 35{ 36 37 38 /** 39 * Returns an array of tokens this test wants to listen for. 40 * 41 * @return array 42 */ 43 public function register() 44 { 45 return array( 46 T_FUNCTION, 47 T_CLOSURE, 48 ); 49 50 }//end register() 51 52 53 /** 54 * Processes this test, when one of its tokens is encountered. 55 * 56 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 57 * @param int $stackPtr The position of the current token 58 * in the stack passed in $tokens. 59 * 60 * @return void 61 */ 62 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 63 { 64 $tokens = $phpcsFile->getTokens(); 65 $token = $tokens[$stackPtr]; 66 67 // Skip broken function declarations. 68 if (isset($token['scope_opener']) === false || isset($token['parenthesis_opener']) === false) { 69 return; 70 } 71 72 $params = array(); 73 foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) { 74 $params[$param['name']] = $stackPtr; 75 } 76 77 $next = ++$token['scope_opener']; 78 $end = --$token['scope_closer']; 79 80 $foundContent = false; 81 $validTokens = array( 82 T_HEREDOC => T_HEREDOC, 83 T_NOWDOC => T_NOWDOC, 84 T_END_HEREDOC => T_END_HEREDOC, 85 T_END_NOWDOC => T_END_NOWDOC, 86 T_DOUBLE_QUOTED_STRING => T_DOUBLE_QUOTED_STRING, 87 ); 88 $validTokens += PHP_CodeSniffer_Tokens::$emptyTokens; 89 90 for (; $next <= $end; ++$next) { 91 $token = $tokens[$next]; 92 $code = $token['code']; 93 94 // Ignorable tokens. 95 if (isset(PHP_CodeSniffer_Tokens::$emptyTokens[$code]) === true) { 96 continue; 97 } 98 99 if ($foundContent === false) { 100 // A throw statement as the first content indicates an interface method. 101 if ($code === T_THROW) { 102 return; 103 } 104 105 // A return statement as the first content indicates an interface method. 106 if ($code === T_RETURN) { 107 $tmp = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($next + 1), null, true); 108 if ($tmp === false) { 109 return; 110 } 111 112 // There is a return. 113 if ($tokens[$tmp]['code'] === T_SEMICOLON) { 114 return; 115 } 116 117 $tmp = $phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($tmp + 1), null, true); 118 if ($tmp !== false && $tokens[$tmp]['code'] === T_SEMICOLON) { 119 // There is a return <token>. 120 return; 121 } 122 }//end if 123 }//end if 124 125 $foundContent = true; 126 127 if ($code === T_VARIABLE && isset($params[$token['content']]) === true) { 128 unset($params[$token['content']]); 129 } else if ($code === T_DOLLAR) { 130 $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true); 131 if ($tokens[$nextToken]['code'] === T_OPEN_CURLY_BRACKET) { 132 $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true); 133 if ($tokens[$nextToken]['code'] === T_STRING) { 134 $varContent = '$'.$tokens[$nextToken]['content']; 135 if (isset($params[$varContent]) === true) { 136 unset($params[$varContent]); 137 } 138 } 139 } 140 } else if ($code === T_DOUBLE_QUOTED_STRING 141 || $code === T_START_HEREDOC 142 || $code === T_START_NOWDOC 143 ) { 144 // Tokenize strings that can contain variables. 145 // Make sure the string is re-joined if it occurs over multiple lines. 146 $content = $token['content']; 147 for ($i = ($next + 1); $i <= $end; $i++) { 148 if (isset($validTokens[$tokens[$i]['code']]) === true) { 149 $content .= $tokens[$i]['content']; 150 $next++; 151 } else { 152 break; 153 } 154 } 155 156 $stringTokens = token_get_all(sprintf('<?php %s;?>', $content)); 157 foreach ($stringTokens as $stringPtr => $stringToken) { 158 if (is_array($stringToken) === false) { 159 continue; 160 } 161 162 $varContent = ''; 163 if ($stringToken[0] === T_DOLLAR_OPEN_CURLY_BRACES) { 164 $varContent = '$'.$stringTokens[($stringPtr + 1)][1]; 165 } else if ($stringToken[0] === T_VARIABLE) { 166 $varContent = $stringToken[1]; 167 } 168 169 if ($varContent !== '' && isset($params[$varContent]) === true) { 170 unset($params[$varContent]); 171 } 172 } 173 }//end if 174 }//end for 175 176 if ($foundContent === true && count($params) > 0) { 177 foreach ($params as $paramName => $position) { 178 $error = 'The method parameter %s is never used'; 179 $data = array($paramName); 180 $phpcsFile->addWarning($error, $position, 'Found', $data); 181 } 182 } 183 184 }//end process() 185 186 187}//end class 188