Exemplo n.º 1
0
 /**
  * @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)));
 }