Passed
Push — develop ( 7d2249...4ccadf )
by Baptiste
02:09
created

Visualize::__invoke()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 1
dl 0
loc 23
ccs 16
cts 16
cp 1
crap 1
rs 9.0856
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
4
namespace Innmind\Compose;
5
6
use Innmind\Compose\{
7
    Arguments as Args,
8
    Definition\Argument as Arg,
9
    Definition\Name,
10
    Definition\Service,
11
    Definition\Service\Constructor,
12
    Definition\Service\Argument,
13
    Definition\Service\Argument\HoldReference,
14
    Visualization\Graph\Dependency,
15
    Visualization\Graph\Arguments,
1 ignored issue
show
Bug introduced by
This use statement conflicts with another class in this namespace, Innmind\Compose\Arguments. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
16
    Visualization\Node\Element
17
};
18
use Innmind\Graphviz\{
19
    Graph,
20
    Node\Node
21
};
22
use Innmind\Immutable\{
23
    MapInterface,
24
    Set,
25
    Str,
26
    Map
27
};
28
29
final class Visualize
30
{
31 1
    public function __invoke(Services $services): Graph
32
    {
33
        $graph = $this
34 1
            ->loadClusters($services->dependencies())
35 1
            ->reduce(
36 1
                Graph\Graph::directed(),
37 1
                static function(Graph $graph, Graph $cluster): Graph {
38 1
                    return $graph->cluster($cluster);
39 1
                }
40
            )
41 1
            ->cluster(new Arguments($services->arguments()));
42
43 1
        $nodes = $this->buildNodes($services);
44 1
        $this->linkNodes($nodes);
45
46 1
        $graph = $nodes->reduce(
47 1
            $graph,
48 1
            static function(Graph $graph, string $name, array $pair): Graph {
49 1
                return $graph->add($pair[0]);
50 1
            }
51
        );
52
53 1
        return $this->linkDefaults($graph, $services->arguments(), $nodes);
54
    }
55
56 1
    private function loadClusters(Dependencies $dependencies): Set
57
    {
58 1
        return $dependencies->exposed()->reduce(
59 1
            Set::of(Graph::class),
60 1
            static function(Set $clusters, Name $dependency, MapInterface $services): Set {
61 1
                return $clusters->add(new Dependency($dependency, $services));
62 1
            }
63
        );
64
    }
65
66 1
    private function buildNodes(Services $services): Map
67
    {
68
        return $services
69 1
            ->all()
70 1
            ->filter(static function(Service $service): bool {
71 1
                return !$service->decorates();
72 1
            })
73 1
            ->reduce(
74 1
                new Map('string', 'array'),
75 1
                static function(Map $nodes, Service $service): Map {
76 1
                    $node = Element::service($service);
77
78 1
                    return $nodes->put(
79 1
                        (string) $node->name(),
80 1
                        [$node, $service]
81
                    );
82 1
                }
83
            );
84
    }
85
86 1
    private function linkNodes(Map $nodes): void
87
    {
88
        $nodes
89 1
            ->values()
90 1
            ->filter(static function(array $pair): bool {
91
                //do not try to link nodes for services that do not depend on
92
                //other services
93 1
                return $pair[1]
94 1
                    ->arguments()
95 1
                    ->filter(static function(Argument $argument): bool {
96 1
                        return $argument instanceof HoldReference;
97 1
                    })
98 1
                    ->size() > 0;
99 1
            })
100 1
            ->foreach(static function(array $pair) use ($nodes): void {
101 1
                $pair[1]
102 1
                    ->arguments()
103 1
                    ->filter(static function(Argument $argument): bool {
104 1
                        return $argument instanceof HoldReference;
105 1
                    })
106 1
                    ->foreach(static function(HoldReference $argument) use ($pair, $nodes): void {
107 1
                        $name = (string) Element::build($argument->reference())->name();
108
109
                        //an argument or a dependency
110 1
                        if (!$nodes->contains($name)) {
111 1
                            $pair[0]->linkedTo(Node::named($name));
112
113 1
                            return;
114
                        }
115
116 1
                        $pair[0]->linkedTo(
117 1
                            $nodes->get($name)[0]
118
                        );
119 1
                    });
120 1
            });
121 1
    }
122
123 1
    private function linkDefaults(Graph $graph, Args $arguments, Map $nodes): Graph
124
    {
125
        return $arguments
126 1
            ->all()
127 1
            ->filter(static function(Arg $argument): bool {
128 1
                return $argument->hasDefault();
129 1
            })
130 1
            ->reduce(
131 1
                $graph,
132 1
                static function(Graph $graph, Arg $argument) use ($nodes): Graph {
133 1
                    $node = Element::build($argument->name());
134 1
                    $default = Element::build($argument->default());
135 1
                    $default = $nodes->get((string) $default->name())[0];
136 1
                    $edge = $node->linkedTo($default);
137
                    $edge
138 1
                        ->dotted()
139 1
                        ->displayAs('defaults to');
140
141 1
                    return $graph->add($node);
142 1
                }
143
            );
144
    }
145
}
146