1<?php 2/** 3 * Checks the cyclomatic complexity (McCabe) for functions. 4 * 5 * PHP version 5 6 * 7 * @category PHP 8 * @package PHP_CodeSniffer 9 * @author Greg Sherwood <gsherwood@squiz.net> 10 * @author Marc McIntyre <mmcintyre@squiz.net> 11 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 12 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 13 * @link http://pear.php.net/package/PHP_CodeSniffer 14 */ 15 16/** 17 * Checks the cyclomatic complexity (McCabe) for functions. 18 * 19 * The cyclomatic complexity (also called McCabe code metrics) 20 * indicates the complexity within a function by counting 21 * the different paths the function includes. 22 * 23 * @category PHP 24 * @package PHP_CodeSniffer 25 * @author Johann-Peter Hartmann <hartmann@mayflower.de> 26 * @author Greg Sherwood <gsherwood@squiz.net> 27 * @copyright 2007-2014 Mayflower GmbH 28 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 29 * @version Release: @package_version@ 30 * @link http://pear.php.net/package/PHP_CodeSniffer 31 */ 32class Generic_Sniffs_Metrics_CyclomaticComplexitySniff implements PHP_CodeSniffer_Sniff 33{ 34 35 /** 36 * A complexity higher than this value will throw a warning. 37 * 38 * @var int 39 */ 40 public $complexity = 10; 41 42 /** 43 * A complexity higher than this value will throw an error. 44 * 45 * @var int 46 */ 47 public $absoluteComplexity = 20; 48 49 50 /** 51 * Returns an array of tokens this test wants to listen for. 52 * 53 * @return array 54 */ 55 public function register() 56 { 57 return array(T_FUNCTION); 58 59 }//end register() 60 61 62 /** 63 * Processes this test, when one of its tokens is encountered. 64 * 65 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 66 * @param int $stackPtr The position of the current token 67 * in the stack passed in $tokens. 68 * 69 * @return void 70 */ 71 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 72 { 73 $this->currentFile = $phpcsFile; 74 75 $tokens = $phpcsFile->getTokens(); 76 77 // Ignore abstract methods. 78 if (isset($tokens[$stackPtr]['scope_opener']) === false) { 79 return; 80 } 81 82 // Detect start and end of this function definition. 83 $start = $tokens[$stackPtr]['scope_opener']; 84 $end = $tokens[$stackPtr]['scope_closer']; 85 86 // Predicate nodes for PHP. 87 $find = array( 88 T_CASE => true, 89 T_DEFAULT => true, 90 T_CATCH => true, 91 T_IF => true, 92 T_FOR => true, 93 T_FOREACH => true, 94 T_WHILE => true, 95 T_DO => true, 96 T_ELSEIF => true, 97 ); 98 99 $complexity = 1; 100 101 // Iterate from start to end and count predicate nodes. 102 for ($i = ($start + 1); $i < $end; $i++) { 103 if (isset($find[$tokens[$i]['code']]) === true) { 104 $complexity++; 105 } 106 } 107 108 if ($complexity > $this->absoluteComplexity) { 109 $error = 'Function\'s cyclomatic complexity (%s) exceeds allowed maximum of %s'; 110 $data = array( 111 $complexity, 112 $this->absoluteComplexity, 113 ); 114 $phpcsFile->addError($error, $stackPtr, 'MaxExceeded', $data); 115 } else if ($complexity > $this->complexity) { 116 $warning = 'Function\'s cyclomatic complexity (%s) exceeds %s; consider refactoring the function'; 117 $data = array( 118 $complexity, 119 $this->complexity, 120 ); 121 $phpcsFile->addWarning($warning, $stackPtr, 'TooHigh', $data); 122 } 123 124 }//end process() 125 126 127}//end class 128