Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 127 additions & 82 deletions src/Analyzer/FileVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Arkitect\Analyzer;

use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\Node\NullableType;
use PhpParser\NodeVisitorAbstract;
Expand All @@ -28,34 +27,97 @@ public function setFilePath(?string $filePath): void

public function enterNode(Node $node): void
{
if ($node instanceof Node\Stmt\Class_) {
if (!$node->isAnonymous() && null !== $node->namespacedName) {
$this->classDescriptionBuilder->setClassName($node->namespacedName->toCodeString());
}
$this->handleClassNode($node);

foreach ($node->implements as $interface) {
$this->classDescriptionBuilder
->addInterface($interface->toString(), $interface->getLine());
}
$this->handleEnumNode($node);

if (!$node->isAnonymous() && null !== $node->extends) {
$this->classDescriptionBuilder
->addExtends($node->extends->toString(), $node->getLine());
}
$this->handleStaticClassConstantNode($node);

if ($node->isFinal()) {
$this->classDescriptionBuilder->setFinal(true);
}
$this->handleStaticClassCallsNode($node);

if ($node->isReadonly()) {
$this->classDescriptionBuilder->setReadonly(true);
}
$this->handleInstanceOf($node);

if ($node->isAbstract()) {
$this->classDescriptionBuilder->setAbstract(true);
}
$this->handleNewExpression($node);

$this->handleTypedProperty($node);

$this->handleDocComment($node);

$this->handleParamDependency($node);

$this->handleInterfaceNode($node);

$this->handleTraitNode($node);

$this->handleReturnTypeDependency($node);

$this->handleAttributeNode($node);
}

public function getClassDescriptions(): array
{
return $this->classDescriptions;
}

public function clearParsedClassDescriptions(): void
{
$this->classDescriptions = [];
$this->classDescriptionBuilder->setFilePath(null);
$this->classDescriptionBuilder->clear();
}

public function leaveNode(Node $node): void
{
if ($node instanceof Node\Stmt\Class_ && !$node->isAnonymous()) {
$this->classDescriptions[] = $this->classDescriptionBuilder->build();
$this->classDescriptionBuilder->clear();
}

if ($node instanceof Node\Stmt\Enum_) {
$this->classDescriptions[] = $this->classDescriptionBuilder->build();
$this->classDescriptionBuilder->clear();
}

if ($node instanceof Node\Stmt\Interface_) {
$this->classDescriptions[] = $this->classDescriptionBuilder->build();
$this->classDescriptionBuilder->clear();
}

if ($node instanceof Node\Stmt\Trait_) {
$this->classDescriptions[] = $this->classDescriptionBuilder->build();
$this->classDescriptionBuilder->clear();
}
}

private function handleClassNode(Node $node): void
{
if (!($node instanceof Node\Stmt\Class_)) {
return;
}

if (!$node->isAnonymous() && null !== $node->namespacedName) {
$this->classDescriptionBuilder->setClassName($node->namespacedName->toCodeString());
}

foreach ($node->implements as $interface) {
$this->classDescriptionBuilder
->addInterface($interface->toString(), $interface->getLine());
}

if (!$node->isAnonymous() && null !== $node->extends) {
$this->classDescriptionBuilder
->addExtends($node->extends->toString(), $node->getLine());
}

$this->classDescriptionBuilder->setFinal($node->isFinal());

$this->classDescriptionBuilder->setReadonly($node->isReadonly());

$this->classDescriptionBuilder->setAbstract($node->isAbstract());
}

private function handleEnumNode(Node $node): void
{
if ($node instanceof Node\Stmt\Enum_ && null !== $node->namespacedName) {
$this->classDescriptionBuilder->setClassName($node->namespacedName->toCodeString());
$this->classDescriptionBuilder->setEnum(true);
Expand All @@ -65,7 +127,10 @@ public function enterNode(Node $node): void
->addInterface($interface->toString(), $interface->getLine());
}
}
}

private function handleStaticClassConstantNode(Node $node): void
{
/**
* adding static classes as dependencies
* $constantValue = StaticClass::constant;.
Expand All @@ -83,7 +148,10 @@ public function enterNode(Node $node): void
$this->classDescriptionBuilder
->addDependency(new ClassDependency($node->class->toString(), $node->getLine()));
}
}

private function handleStaticClassCallsNode(Node $node): void
{
/**
* adding static function classes as dependencies
* $static = StaticClass::foo();.
Expand All @@ -101,7 +169,10 @@ public function enterNode(Node $node): void
$this->classDescriptionBuilder
->addDependency(new ClassDependency($node->class->toString(), $node->getLine()));
}
}

private function handleInstanceOf(Node $node): void
{
if (
$node instanceof Node\Expr\Instanceof_
&& method_exists($node->class, 'toString')
Expand All @@ -112,7 +183,10 @@ public function enterNode(Node $node): void
$this->classDescriptionBuilder
->addDependency(new ClassDependency($node->class->toString(), $node->getLine()));
}
}

private function handleNewExpression(Node $node): void
{
if (
$node instanceof Node\Expr\New_
&& !($node->class instanceof Node\Expr\Variable)
Expand All @@ -130,56 +204,53 @@ public function enterNode(Node $node): void
$this->classDescriptionBuilder
->addDependency(new ClassDependency($node->class->toString(), $node->getLine()));
}
}

/**
* matches parameters dependency in property definitions like
* public ?NotBlank $foo;.
*
* @see FileVisitorTest::test_it_parse_typed_property
*/
private function handleTypedProperty(Node $node): void
{
if ($node instanceof Node\Stmt\Property) {
if (null === $node->type) {
return;
}

$type = $node->type;
if ($type instanceof NullableType) {
/** @var NullableType * */
$nullableType = $type;
$type = $nullableType->type;
$type = $type->type;
}

if (!method_exists($type, 'toString')) {
return;
}

if ($this->isBuiltInType($type->toString())) {
if (!method_exists($type, 'toString') || $this->isBuiltInType($type->toString())) {
return;
}

try {
$this->classDescriptionBuilder->addDependency(new ClassDependency($type->toString(), $node->getLine()));
$this->classDescriptionBuilder
->addDependency(new ClassDependency($type->toString(), $node->getLine()));
} catch (\Exception $e) {
// Silently ignore
}
}
}

if (null !== $node->getDocComment()) {
/** @var Doc $docComment */
$docComment = $node->getDocComment();
private function handleDocComment(Node $node): void
{
$docComment = $node->getDocComment();

$this->classDescriptionBuilder->addDocBlock($docComment->getText());
if (null === $docComment) {
return;
}

/**
* matches parameters dependency in functions and method definitions like
* public function __construct(Symfony\Component\HttpFoundation\Request $request).
*
* @see FileVisitorTest::test_should_returns_all_dependencies
*/
$this->classDescriptionBuilder->addDocBlock($docComment->getText());
}

private function handleParamDependency(Node $node): void
{
if ($node instanceof Node\Param) {
$this->addParamDependency($node);
}
}

private function handleInterfaceNode(Node $node): void
{
if ($node instanceof Node\Stmt\Interface_) {
if (null === $node->namespacedName) {
return;
Expand All @@ -193,7 +264,10 @@ public function enterNode(Node $node): void
->addExtends($interface->toString(), $interface->getLine());
}
}
}

private function handleTraitNode(Node $node): void
{
if ($node instanceof Node\Stmt\Trait_) {
if (null === $node->namespacedName) {
return;
Expand All @@ -202,15 +276,21 @@ public function enterNode(Node $node): void
$this->classDescriptionBuilder->setClassName($node->namespacedName->toCodeString());
$this->classDescriptionBuilder->setTrait(true);
}
}

private function handleReturnTypeDependency(Node $node): void
{
if ($node instanceof Node\Stmt\ClassMethod) {
$returnType = $node->returnType;
if ($returnType instanceof Node\Name\FullyQualified) {
$this->classDescriptionBuilder
->addDependency(new ClassDependency($returnType->toString(), $returnType->getLine()));
}
}
}

private function handleAttributeNode(Node $node): void
{
if ($node instanceof Node\Attribute) {
$nodeName = $node->name;

Expand All @@ -221,41 +301,6 @@ public function enterNode(Node $node): void
}
}

public function getClassDescriptions(): array
{
return $this->classDescriptions;
}

public function clearParsedClassDescriptions(): void
{
$this->classDescriptions = [];
$this->classDescriptionBuilder->setFilePath(null);
$this->classDescriptionBuilder->clear();
}

public function leaveNode(Node $node): void
{
if ($node instanceof Node\Stmt\Class_ && !$node->isAnonymous()) {
$this->classDescriptions[] = $this->classDescriptionBuilder->build();
$this->classDescriptionBuilder->clear();
}

if ($node instanceof Node\Stmt\Enum_) {
$this->classDescriptions[] = $this->classDescriptionBuilder->build();
$this->classDescriptionBuilder->clear();
}

if ($node instanceof Node\Stmt\Interface_) {
$this->classDescriptions[] = $this->classDescriptionBuilder->build();
$this->classDescriptionBuilder->clear();
}

if ($node instanceof Node\Stmt\Trait_) {
$this->classDescriptions[] = $this->classDescriptionBuilder->build();
$this->classDescriptionBuilder->clear();
}
}

private function isSelfOrStaticOrParent(string $dependencyClass): bool
{
return 'self' === $dependencyClass || 'static' === $dependencyClass || 'parent' === $dependencyClass;
Expand Down