Completed
Pull Request — 8.x-1.x (#5)
by Frédéric G.
01:52
created

Dependencies::graphvizCreateFilepath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\qa;
4
5
use Doctrine\Common\Util\Debug;
6
use Drupal\Core\Extension\ModuleExtensionList;
7
use Drupal\Core\Extension\ThemeHandlerInterface;
8
use Grafizzi\Graph\Attribute;
9
use Grafizzi\Graph\Cluster;
10
use Grafizzi\Graph\Edge;
11
use Grafizzi\Graph\Graph;
12
use Grafizzi\Graph\Node;
13
use Pimple\Container;
14
use Psr\Log\LoggerInterface;
15
16
class Dependencies {
17
  const SHAPE_THEME = 'octagon';
18
  const SHAPE_ENGINE = 'doubleoctagon';
19
20
  /**
21
   * @var \Grafizzi\Graph\Attribute
22
   */
23
  protected $font;
24
25
  /**
26
   * @var \Psr\Log\LoggerInterface
27
   */
28
  protected $logger;
29
30
  /**
31
   * @var \Drupal\Core\Extension\ModuleExtensionList
32
   */
33
  protected $moduleExtensionList;
34
35
  /**
36
   * @var \Pimple\Container
37
   */
38
  protected $pimple;
39
40
  /**
41
   * @var \Drupal\Core\Extension\ThemeHandlerInterface
42
   */
43
  protected $themeHandler;
44
45
  public function __construct(
46
    ThemeHandlerInterface $themeHandler,
47
    ModuleExtensionList $moduleExtensionList,
48
    LoggerInterface $logger
49
  ) {
50
    $this->logger = $logger;
51
    $this->moduleExtensionList = $moduleExtensionList;
52
    $this->pimple = new Container(['logger' => $logger]);
53
    $this->themeHandler = $themeHandler;
54
55
    $this->font = $this->attr("fontsize", 10);
56
  }
57
58
59
  /**
60
   * Clone of function _graphviz_create_filepath() from graphviz_filter.module.
61
   *
62
   * @param string $path
63
   * @param string $filename
64
   *
65
   * @return string
66
   */
67
  public function graphvizCreateFilepath($path, $filename) {
68
    if (!empty($path)) {
69
      return rtrim($path, '/') .'/'. $filename;
70
    }
71
    return $filename;
72
  }
73
74
  /**
75
   * Facade for Grafizzi Attribute constructor.
76
   *
77
   * @param $name
78
   * @param $value
79
   *
80
   * @return \Grafizzi\Graph\Attribute
81
   */
82
  public function attr(string $name, string $value) : Attribute {
83
    return new Attribute($this->pimple, $name, $value);
84
  }
85
86
  public function edge(Node $from, Node $to, array $attrs) : Edge {
87
    return new Edge($this->pimple, $from, $to, $attrs);
88
  }
89
90
  /**
91
   * Facade for Grafizzi Node constructor.
92
   *
93
   * Strips the optional "namespace" (aka project or package) part of the name.
94
   *
95
   * @param string $name
96
   * @param array $attrs
97
   *
98
   * @return \Grafizzi\Graph\Node
99
   */
100
  public function node(string $name, array $attrs = []) : Node {
101
    // Strip the "namespace" part.
102
    $arName = explode(':', $name);
103
    $localName = array_pop($arName);
104
105
    $arLocal = explode(' ', $localName);
106
    $simpleName = current($arLocal);
107
    return new Node($this->pimple, $simpleName, $attrs);
108
  }
109
110
  /**
111
   * Facade for Grafizzi Cluster constructor.
112
   *
113
   * @param string $name
114
   *
115
   * @return \Grafizzi\Graph\Cluster
116
   */
117
  public function cluster(string $name): Cluster {
118
    return new Cluster($this->pimple, urlencode($name), [
119
      $this->attr('label', $name),
120
    ]);
121
  }
122
123
  protected function initGraph() : Graph {
124
    $g = new Graph($this->pimple, "deps", [
125
      $this->attr("rankdir", "RL"),
126
    ]);
127
    $g->setDirected(TRUE);
128
    return $g;
129
  }
130
131
  public function buildModules(Graph $g) : Graph {
132
    $modules = $this->moduleExtensionList->reset()->getList();
133
    krsort($modules);
134
135
    $packages = [];
136
137
    foreach ($modules as $module => $detail) {
138
      if (!$detail->status) {
139
        continue;
140
      }
141
      $package = $detail->info['package'] ?? '';
142
      if (!empty($package)) {
143
        if (!isset($packages[$package])) {
144
          $packageCluster = $this->cluster($package);
145
          $packages[$package] = $packageCluster;
146
          $g->addChild($packageCluster);
147
        }
148
        else {
149
          /** @var \Grafizzi\Graph\Cluster $packageCluster */
150
          $packageCluster = $packages[$package];
151
        }
152
153
        $packageCluster->addChild($from = $this->node("${package}:${module}", [$this->font]));
154
      }
155
      else {
156
        $g->addChild($from = $this->node($module, [$this->font]));
157
      }
158
159
      $dependencies = $detail->info['dependencies'] ?? [];
160
      foreach ($dependencies as $depend) {
161
        $to = $this->node($depend, [$this->font]);
162
        $g->addChild(
163
          $this->edge($from, $to, [
164
            $this->font,
165
            $this->attr('color', 'lightgray'),
166
          ]));
167
      }
168
    }
169
    return $g;
170
  }
171
172
  protected function buildTheming(Graph $g) : Graph {
173
    $engineShape = $this->attr('shape', static::SHAPE_ENGINE);
174
    $themeShape = $this->attr('shape', static::SHAPE_THEME);
175
    $engineLine = $this->attr('style', 'dotted');
176
    $baseLine = $this->attr('style', 'dashed');
177
178
    $themeList = $this->themeHandler->listInfo();
179
    krsort($themeList);
180
181
    $engines = [];
182
    $themes = [];
183
184
    foreach ($themeList as $theme => $detail) {
185
      if (empty($detail->status)) {
186
        continue;
187
      }
188
189
      // Build theme engine links.
190
      $themes[$theme] = $from = $this->node($theme, [$this->font, $themeShape]);
191
      $g->addChild($from);
192
      if (!empty($detail->owner)) {
193
        // D8 still theoretically supports multiple engines (e.g. nyan_cat).
194
        $engine = basename($detail->owner); // with extension
195
        $engineBase = basename($engine, '.engine');
196
        if (!isset($engines[$engineBase])) {
197
          $engines[$engineBase] = $engineNode = $this->node($engineBase, [
198
            $engineShape,
199
          ]);
200
          $g->addChild($engineCluster = $this->cluster($engineBase, [$this->font]));
0 ignored issues
show
Unused Code introduced by
The call to Dependencies::cluster() has too many arguments starting with array($this->font).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
201
          $engineCluster->addChild($engineNode);
202
        }
203
        $to = $engines[$engineBase];
204
        $g->addChild($this->edge($from, $to, [$this->font, $engineLine]));
205
      }
206
      else {
207
        $g->addChild($from);
208
      }
209
210
      // Build base theme links.
211
      $toName = $detail->base_theme ?? '';
212
      if (!empty($toName)) {
213
        $to = $themes[$toName];
214
        if (empty($to)) {
215
          $to = $this->node($toName, [$this->font]);
216
          $g->addChild($to);
217
        }
218
        $g->addChild($this->edge($from, $to, [$this->font, $baseLine]));
219
      }
220
    }
221
222
    return $g;
223
  }
224
225
  public function build() : Graph {
226
    // @see https://wiki.php.net/rfc/pipe-operator
227
    $g = $this->initGraph();
228
    $g = $this->buildModules($g);
229
    //$g = $this->buildTheming($g);
230
    return $g;
231
  }
232
233
}
234