Skip to content

Commit

Permalink
refactor: Name AssociationsBuilder to EdgesBuilder
Browse files Browse the repository at this point in the history
- Rename class since that namespace goal is to create a Digraph
  • Loading branch information
MontealegreLuis committed May 15, 2022
1 parent 3045c38 commit f03b3bf
Show file tree
Hide file tree
Showing 15 changed files with 209 additions and 156 deletions.
2 changes: 1 addition & 1 deletion src/Code/ClassDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function __construct(
* classes via the constructor
*
* @return Parameter[]
* @see \PhUml\Graphviz\Builders\AssociationsBuilder::fromProperties() for more details
* @see \PhUml\Graphviz\Builders\EdgesBuilder::fromProperties() for more details
*/
public function constructorParameters(): array
{
Expand Down
4 changes: 2 additions & 2 deletions src/Code/Variables/HasType.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public function hasTypeDeclaration(): bool;
/**
* It is used by the `EdgesBuilder` class to mark an association as resolved
*
* @see \PhUml\Graphviz\Builders\EdgesBuilder::needAssociation() for more details
* @see \PhUml\Graphviz\Builders\EdgesBuilder::markAssociationResolvedFor() for more details
* @see \PhUml\Graphviz\Builders\DirectedEdgesBuilder::needAssociation() for more details
* @see \PhUml\Graphviz\Builders\DirectedEdgesBuilder::markAssociationResolvedFor() for more details
*/
public function type(): TypeDeclaration;
}
27 changes: 0 additions & 27 deletions src/Graphviz/Builders/AssociationsBuilder.php

This file was deleted.

6 changes: 3 additions & 3 deletions src/Graphviz/Builders/ClassGraphBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ final class ClassGraphBuilder
/** @var HasDotRepresentation[] */
private array $dotElements;

private readonly AssociationsBuilder $associationsBuilder;
private readonly EdgesBuilder $associationsBuilder;

public function __construct(AssociationsBuilder $associationsBuilder = null)
public function __construct(EdgesBuilder $associationsBuilder)
{
$this->dotElements = [];
$this->associationsBuilder = $associationsBuilder ?? new NoAssociationsBuilder();
$this->associationsBuilder = $associationsBuilder;
}

/**
Expand Down
88 changes: 88 additions & 0 deletions src/Graphviz/Builders/DirectedEdgesBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php declare(strict_types=1);
/**
* This source file is subject to the license that is bundled with this package in the file LICENSE.
*/

namespace PhUml\Graphviz\Builders;

use PhUml\Code\ClassDefinition;
use PhUml\Code\Codebase;
use PhUml\Code\Name;
use PhUml\Code\Variables\HasType;
use PhUml\Graphviz\Edge;

/**
* It creates directed edges by inspecting a class
*
* 1. It creates edges by inspecting the properties of a class
* 2. It creates edges by inspecting the parameters of the constructor of a class
*/
final class DirectedEdgesBuilder implements EdgesBuilder
{
/** @var bool[] */
private array $edges = [];

/**
* It creates an edge if the property
*
* - Has type information, and it's not a PHP's built-in type
* - The association hasn't already been resolved
*
* @return Edge[]
*/
public function fromProperties(ClassDefinition $class, Codebase $codebase): array
{
return $this->buildEdgesFor($class, $class->properties(), $codebase);
}

/**
* It creates an edge if the constructor parameter
*
* - Has type information, and it's not a PHP's built-in type
* - The association hasn't already been resolved
*
* @return Edge[]
*/
public function fromConstructor(ClassDefinition $class, Codebase $codebase): array
{
return $this->buildEdgesFor($class, $class->constructorParameters(), $codebase);
}

/**
* @param HasType[] $variables
* @return Edge[]
*/
private function buildEdgesFor(ClassDefinition $class, array $variables, Codebase $codebase): array
{
$edges = [];
foreach ($variables as $variable) {
$key = EdgeKey::from($class->name(), $variable->type());
if ($this->isAssociationResolved($key)) {
continue;
}

$this->markAssociationResolvedFor($key);
$edges[] = $this->addAssociations($class, $variable, $codebase);
}
return array_merge(...$edges);
}

/** @return Edge[] */
private function addAssociations(ClassDefinition $class, HasType $property, Codebase $codebase): array
{
return array_map(
static fn (Name $reference): Edge => Edge::association($codebase->get($reference), $class),
$property->references()
);
}

private function isAssociationResolved(EdgeKey $key): bool
{
return array_key_exists((string) $key, $this->edges) && $this->edges[(string) $key] === true;
}

private function markAssociationResolvedFor(EdgeKey $key): void
{
$this->edges[(string) $key] = true;
}
}
35 changes: 35 additions & 0 deletions src/Graphviz/Builders/EdgeKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php declare(strict_types=1);
/**
* This source file is subject to the license that is bundled with this package in the file LICENSE.
*/

namespace PhUml\Graphviz\Builders;

use PhUml\Code\Name;
use PhUml\Code\Variables\TypeDeclaration;
use Stringable;

final class EdgeKey implements Stringable
{
private string $key;

public static function from(Name $name, TypeDeclaration $type): EdgeKey
{
return new EdgeKey($name . $type);
}

private function __construct(string $key)
{
$this->key = $key;
}

public function equals(EdgeKey $anotherKey): bool
{
return $this->key === $anotherKey->key;
}

public function __toString(): string
{
return $this->key;
}
}
88 changes: 9 additions & 79 deletions src/Graphviz/Builders/EdgesBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,91 +7,21 @@

use PhUml\Code\ClassDefinition;
use PhUml\Code\Codebase;
use PhUml\Code\Name;
use PhUml\Code\Variables\HasType;
use PhUml\Graphviz\Edge;

/**
* It creates edges by inspecting a class
* It discovers associations between classes/interfaces by inspecting
*
* 1. It creates edges by inspecting the properties of a class
* 2. It creates edges by inspecting the parameters of the constructor of a class
* 1. The properties of a class
* 2. The parameters injected through the constructor of a class
*
* It creates edges between the definitions when appropriate
*/
final class EdgesBuilder implements AssociationsBuilder
interface EdgesBuilder
{
/** @var bool[] */
private array $associations = [];

/**
* It creates an edge if the property
*
* - Has type information, and it's not a PHP's built-in type
* - The association hasn't already been resolved
*
* @return Edge[]
*/
public function fromProperties(ClassDefinition $class, Codebase $codebase): array
{
return $this->buildEdgesFor($class, $class->properties(), $codebase);
}

/**
* It creates an edge if the constructor parameter
*
* - Has type information and it's not a PHP's built-in type
* - The association hasn't already been resolved
*
* @return Edge[]
*/
public function fromConstructor(ClassDefinition $class, Codebase $codebase): array
{
return $this->buildEdgesFor($class, $class->constructorParameters(), $codebase);
}

/**
* @param HasType[] $variables
* @return Edge[]
*/
private function buildEdgesFor(ClassDefinition $class, array $variables, Codebase $codebase): array
{
$edges = [];
foreach ($variables as $parameter) {
if (! $this->needAssociation($class, $parameter)) {
continue;
}
$edges[] = $this->addAssociations($class, $parameter, $codebase);
}
return array_merge(...$edges);
}
/** @return Edge[]*/
public function fromProperties(ClassDefinition $class, Codebase $codebase): array;

/** @return Edge[] */
private function addAssociations(ClassDefinition $class, HasType $property, Codebase $codebase): array
{
$this->markAssociationResolvedFor($class, $property);

return array_map(
static fn (Name $reference): Edge => Edge::association($codebase->get($reference), $class),
$property->references()
);
}

private function needAssociation(ClassDefinition $class, HasType $property): bool
{
return ! $this->isAssociationResolved($class, $property);
}

private function isAssociationResolved(ClassDefinition $class, HasType $property): bool
{
return array_key_exists($this->associationKey($class, $property), $this->associations);
}

private function markAssociationResolvedFor(ClassDefinition $class, HasType $property): void
{
$this->associations[$this->associationKey($class, $property)] = true;
}

private function associationKey(ClassDefinition $class, HasType $property): string
{
return $class->name() . $property->type();
}
public function fromConstructor(ClassDefinition $class, Codebase $codebase): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* This class is used when the user ran the `phuml:diagram` command without the `associations` option.
* Which means that no associations should be discovered
*/
final class NoAssociationsBuilder implements AssociationsBuilder
final class NoEdgesBuilder implements EdgesBuilder
{
public function fromProperties(ClassDefinition $class, Codebase $codebase): array
{
Expand Down
6 changes: 3 additions & 3 deletions src/Parser/Code/Builders/ClassDefinitionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use PhpParser\Node\Stmt\Class_;
use PhUml\Code\ClassDefinition;
use PhUml\Code\Name as ClassDefinitionName;
use PhUml\Code\Name;
use PhUml\Parser\Code\Builders\Names\InterfaceNamesBuilder;
use PhUml\Parser\Code\Builders\Names\TraitNamesBuilder;

Expand Down Expand Up @@ -35,10 +35,10 @@ public function build(Class_ $class): ClassDefinition
$useStatements = $this->useStatementsBuilder->build($class);

return new ClassDefinition(
new ClassDefinitionName((string) $class->namespacedName),
new Name((string) $class->namespacedName),
$this->membersBuilder->methods($class->getMethods(), $useStatements),
$this->membersBuilder->constants($class->stmts),
$class->extends !== null ? new ClassDefinitionName((string) $class->extends) : null,
$class->extends !== null ? new Name((string) $class->extends) : null,
$this->membersBuilder->properties($class->stmts, $class->getMethod('__construct'), $useStatements),
$this->buildInterfaces($class->implements),
$this->buildTraits($class->stmts),
Expand Down
5 changes: 3 additions & 2 deletions src/Parser/Code/Builders/InterfaceDefinitionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use PhpParser\Node\Stmt\Interface_;
use PhUml\Code\InterfaceDefinition;
use PhUml\Code\Name as InterfaceDefinitionName;
use PhUml\Code\Name;
use PhUml\Parser\Code\Builders\Names\InterfaceNamesBuilder;

/**
Expand All @@ -29,8 +29,9 @@ public function __construct(
public function build(Interface_ $interface): InterfaceDefinition
{
$useStatements = $this->useStatementsBuilder->build($interface);

return new InterfaceDefinition(
new InterfaceDefinitionName((string) $interface->namespacedName),
new Name((string) $interface->namespacedName),
$this->membersBuilder->methods($interface->getMethods(), $useStatements),
$this->membersBuilder->constants($interface->stmts),
$this->buildInterfaces($interface->extends)
Expand Down
1 change: 1 addition & 0 deletions src/Parser/Code/Builders/TraitDefinitionBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function __construct(
public function build(Trait_ $trait): TraitDefinition
{
$useStatements = $this->useStatementsBuilder->build($trait);

return new TraitDefinition(
new Name((string) $trait->namespacedName),
$this->membersBuilder->methods($trait->getMethods(), $useStatements),
Expand Down
10 changes: 5 additions & 5 deletions src/Processors/GraphvizConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@

namespace PhUml\Processors;

use PhUml\Graphviz\Builders\AssociationsBuilder;
use PhUml\Graphviz\Builders\DirectedEdgesBuilder;
use PhUml\Graphviz\Builders\EdgesBuilder;
use PhUml\Graphviz\Builders\NoAssociationsBuilder;
use PhUml\Graphviz\Builders\NoEdgesBuilder;
use PhUml\Graphviz\Styles\DigraphStyle;
use PhUml\Graphviz\Styles\ThemeName;
use Webmozart\Assert\Assert;

final class GraphvizConfiguration
{
private readonly AssociationsBuilder $associationsBuilder;
private readonly EdgesBuilder $associationsBuilder;

private readonly DigraphStyle $digraphStyle;

/** @param mixed[] $options */
public function __construct(array $options)
{
Assert::boolean($options['associations'], 'Generate digraph associations option must be a boolean value');
$this->associationsBuilder = $options['associations'] ? new EdgesBuilder() : new NoAssociationsBuilder();
$this->associationsBuilder = $options['associations'] ? new DirectedEdgesBuilder() : new NoEdgesBuilder();
Assert::string($options['theme'], 'Theme option must be a string value');
$theme = new ThemeName($options['theme']);
Assert::boolean($options['hide-empty-blocks'], 'Hide digraph empty blocks option must be a boolean value');
Expand All @@ -32,7 +32,7 @@ public function __construct(array $options)
: DigraphStyle::default($theme);
}

public function associationsBuilder(): AssociationsBuilder
public function edgesBuilder(): EdgesBuilder
{
return $this->associationsBuilder;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Processors/GraphvizProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ final class GraphvizProcessor implements Processor
public static function fromConfiguration(GraphvizConfiguration $configuration): GraphvizProcessor
{
$style = $configuration->digraphStyle();
$associationsBuilder = $configuration->associationsBuilder();
$associationsBuilder = $configuration->edgesBuilder();

return new GraphvizProcessor(
new ClassGraphBuilder($associationsBuilder),
Expand Down
Loading

0 comments on commit f03b3bf

Please sign in to comment.