class CodeUnitFindingVisitor
@internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@psalm-type CodeUnitFunctionType = array{ name: string, namespacedName: string, namespace: string, signature: string, startLine: int, endLine: int, ccn: int } @psalm-type CodeUnitMethodType = array{ methodName: string, signature: string, visibility: string, startLine: int, endLine: int, ccn: int } @psalm-type CodeUnitClassType = array{ name: string, namespacedName: string, namespace: string, startLine: int, endLine: int, methods: array<string, CodeUnitMethodType> } @psalm-type CodeUnitTraitType = array{ name: string, namespacedName: string, namespace: string, startLine: int, endLine: int, methods: array<string, CodeUnitMethodType> }
Hierarchy
- class \PhpParser\NodeVisitorAbstract implements \PhpParser\NodeVisitor
- class \SebastianBergmann\CodeCoverage\StaticAnalysis\CodeUnitFindingVisitor extends \PhpParser\NodeVisitorAbstract
Expanded class hierarchy of CodeUnitFindingVisitor
File
-
vendor/
phpunit/ php-code-coverage/ src/ StaticAnalysis/ CodeUnitFindingVisitor.php, line 70
Namespace
SebastianBergmann\CodeCoverage\StaticAnalysisView source
final class CodeUnitFindingVisitor extends NodeVisitorAbstract {
/**
* @psalm-var array<string, CodeUnitClassType>
*/
private array $classes = [];
/**
* @psalm-var array<string, CodeUnitTraitType>
*/
private array $traits = [];
/**
* @psalm-var array<string, CodeUnitFunctionType>
*/
private array $functions = [];
public function enterNode(Node $node) : void {
if ($node instanceof Class_) {
if ($node->isAnonymous()) {
return;
}
$this->processClass($node);
}
if ($node instanceof Trait_) {
$this->processTrait($node);
}
if (!$node instanceof ClassMethod && !$node instanceof Function_) {
return;
}
if ($node instanceof ClassMethod) {
$parentNode = $node->getAttribute('parent');
if ($parentNode instanceof Class_ && $parentNode->isAnonymous()) {
return;
}
$this->processMethod($node);
return;
}
$this->processFunction($node);
}
/**
* @psalm-return array<string, CodeUnitClassType>
*/
public function classes() : array {
return $this->classes;
}
/**
* @psalm-return array<string, CodeUnitTraitType>
*/
public function traits() : array {
return $this->traits;
}
/**
* @psalm-return array<string, CodeUnitFunctionType>
*/
public function functions() : array {
return $this->functions;
}
private function cyclomaticComplexity(ClassMethod|Function_ $node) : int {
$nodes = $node->getStmts();
if ($nodes === null) {
return 0;
}
$traverser = new NodeTraverser();
$cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor();
$traverser->addVisitor($cyclomaticComplexityCalculatingVisitor);
/* @noinspection UnusedFunctionResultInspection */
$traverser->traverse($nodes);
return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity();
}
private function signature(ClassMethod|Function_ $node) : string {
$signature = ($node->returnsByRef() ? '&' : '') . $node->name
->toString() . '(';
$parameters = [];
foreach ($node->getParams() as $parameter) {
assert(isset($parameter->var->name));
$parameterAsString = '';
if ($parameter->type !== null) {
$parameterAsString = $this->type($parameter->type) . ' ';
}
$parameterAsString .= '$' . $parameter->var->name;
/* @todo Handle default values */
$parameters[] = $parameterAsString;
}
$signature .= implode(', ', $parameters) . ')';
$returnType = $node->getReturnType();
if ($returnType !== null) {
$signature .= ': ' . $this->type($returnType);
}
return $signature;
}
private function type(ComplexType|Identifier|Name $type) : string {
if ($type instanceof NullableType) {
return '?' . $type->type;
}
if ($type instanceof UnionType) {
return $this->unionTypeAsString($type);
}
if ($type instanceof IntersectionType) {
return $this->intersectionTypeAsString($type);
}
return $type->toString();
}
private function visibility(ClassMethod $node) : string {
if ($node->isPrivate()) {
return 'private';
}
if ($node->isProtected()) {
return 'protected';
}
return 'public';
}
private function processClass(Class_ $node) : void {
$name = $node->name
->toString();
$namespacedName = $node->namespacedName
->toString();
$this->classes[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'methods' => [],
];
}
private function processTrait(Trait_ $node) : void {
$name = $node->name
->toString();
$namespacedName = $node->namespacedName
->toString();
$this->traits[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'methods' => [],
];
}
private function processMethod(ClassMethod $node) : void {
$parentNode = $node->getAttribute('parent');
if ($parentNode instanceof Interface_) {
return;
}
assert($parentNode instanceof Class_ || $parentNode instanceof Trait_ || $parentNode instanceof Enum_);
assert(isset($parentNode->name));
assert(isset($parentNode->namespacedName));
assert($parentNode->namespacedName instanceof Name);
$parentName = $parentNode->name
->toString();
$parentNamespacedName = $parentNode->namespacedName
->toString();
if ($parentNode instanceof Class_) {
$storage =& $this->classes;
}
else {
$storage =& $this->traits;
}
if (!isset($storage[$parentNamespacedName])) {
$storage[$parentNamespacedName] = [
'name' => $parentName,
'namespacedName' => $parentNamespacedName,
'namespace' => $this->namespace($parentNamespacedName, $parentName),
'startLine' => $parentNode->getStartLine(),
'endLine' => $parentNode->getEndLine(),
'methods' => [],
];
}
$storage[$parentNamespacedName]['methods'][$node->name
->toString()] = [
'methodName' => $node->name
->toString(),
'signature' => $this->signature($node),
'visibility' => $this->visibility($node),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'ccn' => $this->cyclomaticComplexity($node),
];
}
private function processFunction(Function_ $node) : void {
assert(isset($node->name));
assert(isset($node->namespacedName));
assert($node->namespacedName instanceof Name);
$name = $node->name
->toString();
$namespacedName = $node->namespacedName
->toString();
$this->functions[$namespacedName] = [
'name' => $name,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'signature' => $this->signature($node),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
'ccn' => $this->cyclomaticComplexity($node),
];
}
private function namespace(string $namespacedName, string $name) : string {
return trim(rtrim($namespacedName, $name), '\\');
}
private function unionTypeAsString(UnionType $node) : string {
$types = [];
foreach ($node->types as $type) {
if ($type instanceof IntersectionType) {
$types[] = '(' . $this->intersectionTypeAsString($type) . ')';
continue;
}
$types[] = $this->typeAsString($type);
}
return implode('|', $types);
}
private function intersectionTypeAsString(IntersectionType $node) : string {
$types = [];
foreach ($node->types as $type) {
$types[] = $this->typeAsString($type);
}
return implode('&', $types);
}
private function typeAsString(Identifier|Name $node) : string {
if ($node instanceof Name) {
return $node->toCodeString();
}
return $node->toString();
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|
CodeUnitFindingVisitor::$classes | private | property | @psalm-var array<string, CodeUnitClassType> | ||
CodeUnitFindingVisitor::$functions | private | property | @psalm-var array<string, CodeUnitFunctionType> | ||
CodeUnitFindingVisitor::$traits | private | property | @psalm-var array<string, CodeUnitTraitType> | ||
CodeUnitFindingVisitor::classes | public | function | @psalm-return array<string, CodeUnitClassType> | ||
CodeUnitFindingVisitor::cyclomaticComplexity | private | function | |||
CodeUnitFindingVisitor::enterNode | public | function | Called when entering a node. | Overrides NodeVisitorAbstract::enterNode | |
CodeUnitFindingVisitor::functions | public | function | @psalm-return array<string, CodeUnitFunctionType> | ||
CodeUnitFindingVisitor::intersectionTypeAsString | private | function | |||
CodeUnitFindingVisitor::namespace | private | function | |||
CodeUnitFindingVisitor::processClass | private | function | |||
CodeUnitFindingVisitor::processFunction | private | function | |||
CodeUnitFindingVisitor::processMethod | private | function | |||
CodeUnitFindingVisitor::processTrait | private | function | |||
CodeUnitFindingVisitor::signature | private | function | |||
CodeUnitFindingVisitor::traits | public | function | @psalm-return array<string, CodeUnitTraitType> | ||
CodeUnitFindingVisitor::type | private | function | |||
CodeUnitFindingVisitor::typeAsString | private | function | |||
CodeUnitFindingVisitor::unionTypeAsString | private | function | |||
CodeUnitFindingVisitor::visibility | private | function | |||
NodeVisitor::DONT_TRAVERSE_CHILDREN | public | constant | If NodeVisitor::enterNode() returns DONT_TRAVERSE_CHILDREN, child nodes of the current node will not be traversed for any visitors. |
||
NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN | public | constant | If NodeVisitor::enterNode() returns DONT_TRAVERSE_CURRENT_AND_CHILDREN, child nodes of the current node will not be traversed for any visitors. |
||
NodeVisitor::REMOVE_NODE | public | constant | If NodeVisitor::leaveNode() returns REMOVE_NODE for a node that occurs in an array, it will be removed from the array. |
||
NodeVisitor::REPLACE_WITH_NULL | public | constant | If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns REPLACE_WITH_NULL, the node will be replaced with null. This is not a legal return value if the node is part of an array, rather than another node. |
||
NodeVisitor::STOP_TRAVERSAL | public | constant | If NodeVisitor::enterNode() or NodeVisitor::leaveNode() returns STOP_TRAVERSAL, traversal is aborted. |
||
NodeVisitorAbstract::afterTraverse | public | function | Called once after traversal. | Overrides NodeVisitor::afterTraverse | 1 |
NodeVisitorAbstract::beforeTraverse | public | function | Called once before traversal. | Overrides NodeVisitor::beforeTraverse | 5 |
NodeVisitorAbstract::leaveNode | public | function | Called when leaving a node. | Overrides NodeVisitor::leaveNode | 2 |