public function countFile($filename, $countTests)
{
if ($countTests) {
$this->preProcessFile($filename);
}
$buffer = file_get_contents($filename);
$this->collector->incrementLines(substr_count($buffer, "\n"));
$tokens = token_get_all($buffer);
$numTokens = count($tokens);
unset($buffer);
$this->collector->addFile($filename);
$blocks = [];
$currentBlock = false;
$namespace = false;
$className = null;
$functionName = null;
$testClass = false;
$this->collector->currentClassReset();
$isInMethod = false;
for ($i = 0; $i < $numTokens; $i++) {
if (is_string($tokens[$i])) {
$token = trim($tokens[$i]);
if ($token == ';') {
if ($className !== null && !$testClass) {
$this->collector->currentClassIncrementLines();
if ($functionName !== null) {
$this->collector->currentMethodIncrementLines();
}
} elseif ($functionName !== null) {
$this->collector->incrementFunctionLines();
}
$this->collector->incrementLogicalLines();
} elseif ($token == '?' && !$testClass) {
if ($className !== null) {
$this->collector->currentClassIncrementComplexity();
$this->collector->currentMethodIncrementComplexity();
}
$this->collector->incrementComplexity();
} elseif ($token == '{') {
if ($currentBlock == T_CLASS) {
$block = $className;
} elseif ($currentBlock == T_FUNCTION) {
$block = $functionName;
} else {
$block = false;
}
array_push($blocks, $block);
$currentBlock = false;
} elseif ($token == '}') {
$block = array_pop($blocks);
if ($block !== false && $block !== null) {
if ($block == $functionName) {
$functionName = null;
if ($isInMethod) {
$this->collector->currentMethodStop();
$isInMethod = false;
}
} elseif ($block == $className) {
$className = null;
$testClass = false;
$this->collector->currentClassReset();
}
}
}
continue;
}
list($token, $value) = $tokens[$i];
switch ($token) {
case T_NAMESPACE:
$namespace = $this->getNamespaceName($tokens, $i);
$this->collector->addNamespace($namespace);
break;
case T_CLASS:
case T_INTERFACE:
case T_TRAIT:
if (!$this->isClassDeclaration($tokens, $i)) {
continue;
}
$this->collector->currentClassReset();
$this->collector->currentClassIncrementComplexity();
$className = $this->getClassName($namespace, $tokens, $i);
$currentBlock = T_CLASS;
if ($token == T_TRAIT) {
$this->collector->incrementTraits();
} elseif ($token == T_INTERFACE) {
$this->collector->incrementInterfaces();
} else {
if ($countTests && $this->isTestClass($className)) {
$testClass = true;
$this->collector->incrementTestClasses();
} else {
if (isset($tokens[$i - 2]) && is_array($tokens[$i - 2]) && $tokens[$i - 2][0] == T_ABSTRACT) {
$this->collector->incrementAbstractClasses();
} else {
$this->collector->incrementConcreteClasses();
}
}
}
break;
case T_FUNCTION:
$prev = $this->getPreviousNonWhitespaceTokenPos($tokens, $i);
if ($tokens[$prev][0] === T_USE) {
continue;
}
$currentBlock = T_FUNCTION;
$next = $this->getNextNonWhitespaceTokenPos($tokens, $i);
if (!is_array($tokens[$next]) && $tokens[$next] == '&') {
$next = $this->getNextNonWhitespaceTokenPos($tokens, $next);
}
if (is_array($tokens[$next]) && $tokens[$next][0] == T_STRING) {
$functionName = $tokens[$next][1];
} else {
$currentBlock = 'anonymous function';
$functionName = 'anonymous function';
$this->collector->incrementAnonymousFunctions();
}
if ($currentBlock == T_FUNCTION) {
if ($className === null && $functionName != 'anonymous function') {
$this->collector->incrementNamedFunctions();
} else {
$static = false;
$visibility = T_PUBLIC;
for ($j = $i; $j > 0; $j--) {
if (is_string($tokens[$j])) {
if ($tokens[$j] == '{' || $tokens[$j] == '}' || $tokens[$j] == ';') {
break;
}
continue;
}
if (isset($tokens[$j][0])) {
switch ($tokens[$j][0]) {
case T_PRIVATE:
$visibility = T_PRIVATE;
break;
case T_PROTECTED:
$visibility = T_PROTECTED;
break;
case T_STATIC:
$static = true;
break;
}
}
}
if ($testClass && $this->isTestMethod($functionName, $visibility, $static, $tokens, $i)) {
$this->collector->incrementTestMethods();
} elseif (!$testClass) {
$isInMethod = true;
$this->collector->currentMethodStart();
if (!$static) {
$this->collector->incrementNonStaticMethods();
} else {
$this->collector->incrementStaticMethods();
}
if ($visibility == T_PUBLIC) {
$this->collector->incrementPublicMethods();
} else {
$this->collector->incrementNonPublicMethods();
}
}
}
}
break;
case T_CURLY_OPEN:
$currentBlock = T_CURLY_OPEN;
array_push($blocks, $currentBlock);
break;
case T_DOLLAR_OPEN_CURLY_BRACES:
$currentBlock = T_DOLLAR_OPEN_CURLY_BRACES;
array_push($blocks, $currentBlock);
break;
case T_IF:
case T_ELSEIF:
case T_FOR:
case T_FOREACH:
case T_WHILE:
case T_CASE:
case T_CATCH:
case T_BOOLEAN_AND:
case T_LOGICAL_AND:
case T_BOOLEAN_OR:
case T_LOGICAL_OR:
if (!$testClass) {
if ($isInMethod) {
$this->collector->currentClassIncrementComplexity();
$this->collector->currentMethodIncrementComplexity();
}
$this->collector->incrementComplexity();
}
break;
case T_COMMENT:
case T_DOC_COMMENT:
// We want to count all intermediate lines before the token ends
// But sometimes a new token starts after a newline, we don't want to count that.
// That happend with /* */ and /** */, but not with // since it'll end at the end
$this->collector->incrementCommentLines(substr_count(rtrim($value, "\n"), "\n") + 1);
break;
case T_CONST:
$this->collector->incrementClassConstants();
break;
case T_STRING:
if ($value == 'define') {
$this->collector->incrementGlobalConstants();
$j = $i + 1;
while (isset($tokens[$j]) && $tokens[$j] != ';') {
if (is_array($tokens[$j]) && $tokens[$j][0] == T_CONSTANT_ENCAPSED_STRING) {
$this->collector->addConstant(str_replace('\'', '', $tokens[$j][1]));
break;
}
$j++;
}
} else {
$this->collector->addPossibleConstantAccesses($value);
}
break;
case T_DOUBLE_COLON:
case T_OBJECT_OPERATOR:
$n = $this->getNextNonWhitespaceTokenPos($tokens, $i);
$nn = $this->getNextNonWhitespaceTokenPos($tokens, $n);
if ($n && $nn && isset($tokens[$n][0]) && ($tokens[$n][0] == T_STRING || $tokens[$n][0] == T_VARIABLE) && $tokens[$nn] == '(') {
if ($token == T_DOUBLE_COLON) {
$this->collector->incrementStaticMethodCalls();
} else {
$this->collector->incrementNonStaticMethodCalls();
}
} else {
if ($token == T_DOUBLE_COLON && $tokens[$n][0] == T_VARIABLE) {
$this->collector->incrementStaticAttributeAccesses();
} elseif ($token == T_OBJECT_OPERATOR) {
$this->collector->incrementNonStaticAttributeAccesses();
}
}
break;
case T_GLOBAL:
$this->collector->incrementGlobalVariableAccesses();
break;
case T_VARIABLE:
if ($value == '$GLOBALS') {
$this->collector->incrementGlobalVariableAccesses();
} elseif (isset($this->superGlobals[$value])) {
$this->collector->incrementSuperGlobalVariableAccesses();
}
break;
}
}
}