/** * Visits methods, functions or closures and calculated their complexity. * * @param \PDepend\Source\AST\AbstractASTCallable $callable * @return void * @since 0.9.8 */ public function calculateComplexity(AbstractASTCallable $callable) { $data = array(self::M_CYCLOMATIC_COMPLEXITY_1 => 1, self::M_CYCLOMATIC_COMPLEXITY_2 => 1); foreach ($callable->getChildren() as $child) { $data = $child->accept($this, $data); } $this->metrics[$callable->getId()] = $data; }
/** * @see http://www.scribd.com/doc/99533/Halstead-s-Operators-and-Operands-in-C-C-JAVA-by-Indranil-Nandy * * @param \PDepend\Source\AST\AbstractASTCallable $callable * @return void */ public function calculateHalsteadBasis(AbstractASTCallable $callable) { $operators = array(); $operands = array(); $skipUntil = null; $tokens = $callable->getTokens(); foreach ($tokens as $i => $token) { /* * Some operations should be ignored, e.g. function declarations. * When we encounter a new function, we'll skip all tokens until we * find the closing token. */ if ($skipUntil !== null) { if ($token->type === $skipUntil) { $skipUntil = null; } continue; } switch ($token->type) { // A pair of parenthesis is considered a single operator. case Tokens::T_PARENTHESIS_CLOSE: case Tokens::T_CURLY_BRACE_CLOSE: case Tokens::T_SQUARED_BRACKET_CLOSE: case Tokens::T_ANGLE_BRACKET_CLOSE: break; // A label is considered an operator if it is used as the target // of a GOTO statement. // A label is considered an operator if it is used as the target // of a GOTO statement. case Tokens::T_GOTO: $operators[] = $token->image; // Ignore next token as operand but count as operator instead. $skipUntil = $tokens[$i + 1]->type; $operators[] = $tokens[$i + 1]->image; break; /* * The following control structures case ...: for (...) if (...) * switch (...) while(...) and try-catch (...) are treated in a * special way. The colon and the parentheses are considered to * be a part of the constructs. The case and the colon or the * “for (...)”, “if (...)”, “switch (...)”, “while(...)”, * “try-catch( )” are counted together as one operator. */ // case Tokens::T_SWITCH: // not followed by () // case Tokens::T_TRY: // not followed by () // case Tokens::T_DO: // always comes with while, which accounts for () already /* * The following control structures case ...: for (...) if (...) * switch (...) while(...) and try-catch (...) are treated in a * special way. The colon and the parentheses are considered to * be a part of the constructs. The case and the colon or the * “for (...)”, “if (...)”, “switch (...)”, “while(...)”, * “try-catch( )” are counted together as one operator. */ // case Tokens::T_SWITCH: // not followed by () // case Tokens::T_TRY: // not followed by () // case Tokens::T_DO: // always comes with while, which accounts for () already case Tokens::T_IF: case Tokens::T_FOR: case Tokens::T_FOREACH: case Tokens::T_WHILE: case Tokens::T_CATCH: $operators[] = $token->image; /* * These are always followed by parenthesis, which would add * another operator (only opening parenthesis counts) * so we'll have to skip that one. */ $skipUntil = Tokens::T_PARENTHESIS_OPEN; break; /* * The ternary operator ‘?’ followed by ‘:’ is considered a * single operator as it is equivalent to “if-else” construct. */ /* * The ternary operator ‘?’ followed by ‘:’ is considered a * single operator as it is equivalent to “if-else” construct. */ case Tokens::T_COLON: /* * Colon is used after keyword, where it counts as part of * that operator, or in ternary operator, where it also * counts as 1. */ break; // The comments are considered neither an operator nor an operand. // The comments are considered neither an operator nor an operand. case Tokens::T_DOC_COMMENT: case Tokens::T_COMMENT: break; /* * `new` is considered same as the function call, mainly because * it's equivalent to the function call. */ /* * `new` is considered same as the function call, mainly because * it's equivalent to the function call. */ case Tokens::T_NEW: break; /* * Like T_IF & co, array(..) needs 3 tokens ("array", "(" and * ")") for what's essentially just 1 operator. */ /* * Like T_IF & co, array(..) needs 3 tokens ("array", "(" and * ")") for what's essentially just 1 operator. */ case Tokens::T_ARRAY: break; /* * Class::method or $object->method both only count as 1 * identifier, even though they consist of 3 tokens. */ /* * Class::method or $object->method both only count as 1 * identifier, even though they consist of 3 tokens. */ case Tokens::T_OBJECT_OPERATOR: case Tokens::T_DOUBLE_COLON: // Glue ->/:: and before & after parts together. $image = array_pop($operands) . $token->image . $tokens[$i + 1]->image; $operands[] = $image; // Skip next part (would be seen as operand) $skipUntil = $tokens[$i + 1]->type; break; // Ignore HEREDOC delimiters. // Ignore HEREDOC delimiters. case Tokens::T_START_HEREDOC: case Tokens::T_END_HEREDOC: break; // Ignore PHP open & close tags and non-PHP content. // Ignore PHP open & close tags and non-PHP content. case Tokens::T_OPEN_TAG: case Tokens::T_CLOSE_TAG: case Tokens::T_NO_PHP: break; /* * The function name is considered a single operator when it * appears as calling a function, but when it appears in * declarations or in function definitions it is not counted as * operator. * Default parameter assignments are not counted. */ /* * The function name is considered a single operator when it * appears as calling a function, but when it appears in * declarations or in function definitions it is not counted as * operator. * Default parameter assignments are not counted. */ case Tokens::T_FUNCTION: // Because `)` could appear in default argument assignment // (`$var = array()`), we need to skip until `{`, but that // one should be included in operators. $skipUntil = Tokens::T_CURLY_BRACE_OPEN; $operators[] = '{'; break; /* * When variables or constants appear in declaration they are * not considered as operands, they are considered operands only * when they appear with operators in expressions. */ /* * When variables or constants appear in declaration they are * not considered as operands, they are considered operands only * when they appear with operators in expressions. */ case Tokens::T_VAR: case Tokens::T_CONST: $skipUntil = Tokens::T_SEMICOLON; break; case Tokens::T_STRING: // `define` is T_STRING, just like any other identifier. if ($token->image === 'define') { // Undo all of "define", "(", name, ",", value, ")" $skipUntil = Tokens::T_PARENTHESIS_CLOSE; } else { $operands[] = $token->image; } break; // Operands // Operands case Tokens::T_CONSTANT_ENCAPSED_STRING: case Tokens::T_VARIABLE: case Tokens::T_LNUMBER: case Tokens::T_DNUMBER: case Tokens::T_NUM_STRING: case Tokens::T_NULL: case Tokens::T_TRUE: case Tokens::T_FALSE: case Tokens::T_CLASS_FQN: case Tokens::T_LINE: case Tokens::T_METHOD_C: case Tokens::T_NS_C: case Tokens::T_DIR: case TOKENS::T_ENCAPSED_AND_WHITESPACE: // content of HEREDOC $operands[] = $token->image; break; // Everything else is an operator. // Everything else is an operator. default: $operators[] = $token->image; break; } } $this->metrics[$callable->getId()] = array('n1' => count($operators), 'n2' => count($operands), 'N1' => count(array_unique($operators)), 'N2' => count(array_unique($operands))); }
/** * Visits the given callable instance. * * @param \PDepend\Source\AST\AbstractASTCallable $callable * @return void */ private function visitCallable(AbstractASTCallable $callable) { $this->metrics[$callable->getId()] = array(self::M_CRAP_INDEX => $this->calculateCrapIndex($callable), self::M_COVERAGE => $this->calculateCoverage($callable)); }
/** * This method will calculate the NPath complexity for the given callable * instance. * * @param \PDepend\Source\AST\AbstractASTCallable $callable * @return void * @since 0.9.12 */ protected function calculateComplexity(AbstractASTCallable $callable) { $npath = '1'; foreach ($callable->getChildren() as $child) { $stmt = $child->accept($this, $npath); $npath = MathUtil::mul($npath, $stmt); } $this->metrics[$callable->getId()] = $npath; }