Completed
Push — master ( 071c16...b9f390 )
by Mike
03:33
created

Router::generateUrlForDescriptor()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
nc 5
nop 3
dl 0
loc 24
ccs 0
cts 18
cp 0
crap 30
rs 9.2248
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * This file is part of phpDocumentor.
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @author    Mike van Riel <[email protected]>
11
 * @copyright 2010-2018 Mike van Riel / Naenius (http://www.naenius.com)
12
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
13
 * @link      http://phpdoc.org
14
 */
15
16
namespace phpDocumentor\Transformer\Router;
17
18
use ArrayObject;
19
use InvalidArgumentException;
20
use phpDocumentor\Descriptor\ClassDescriptor;
21
use phpDocumentor\Descriptor\ConstantDescriptor;
22
use phpDocumentor\Descriptor\DescriptorAbstract;
23
use phpDocumentor\Descriptor\FileDescriptor;
24
use phpDocumentor\Descriptor\FunctionDescriptor;
25
use phpDocumentor\Descriptor\InterfaceDescriptor;
26
use phpDocumentor\Descriptor\MethodDescriptor;
27
use phpDocumentor\Descriptor\NamespaceDescriptor;
28
use phpDocumentor\Descriptor\PackageDescriptor;
29
use phpDocumentor\Descriptor\ProjectDescriptorBuilder;
30
use phpDocumentor\Descriptor\PropertyDescriptor;
31
use phpDocumentor\Descriptor\TraitDescriptor;
32
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen;
33
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
34
use phpDocumentor\Transformer\Router\UrlGenerator\QualifiedNameToUrlConverter;
35
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
36
37
/**
38
 * The default for phpDocumentor.
39
 */
40
class Router extends ArrayObject
41
{
42
    private $projectDescriptorBuilder;
43
    private $fqsenUrlGenerator;
44
    private $converter;
45
    private $urlGenerator;
46
47
    public function __construct(
48
        ProjectDescriptorBuilder $projectDescriptorBuilder,
49
        UrlGenerator\FqsenDescriptor $fqsenUrlGenerator,
50
        QualifiedNameToUrlConverter $converter,
51
        UrlGeneratorInterface $urlGenerator
52
    ) {
53
        $this->projectDescriptorBuilder = $projectDescriptorBuilder;
54
        $this->fqsenUrlGenerator = $fqsenUrlGenerator;
55
        $this->converter = $converter;
56
        $this->urlGenerator = $urlGenerator;
57
58
        parent::__construct();
59
        $this->configure();
60
    }
61
62
    /**
63
     * Configuration function to add routing rules to a router.
64
     */
65
    public function configure()
66
    {
67
        // Here we cheat! If a string element is passed to this rule then we try to transform it into a Descriptor
68
        // if the node is translated we do not let it match and instead fall through to one of the other rules.
69
        $stringRule = function (&$node) {
70
            $elements = $this->projectDescriptorBuilder->getProjectDescriptor()->getIndexes()->get('elements');
71
            if (is_string($node) && isset($elements[$node])) {
72
                $node = $elements[$node];
73
            }
74
75
            return false;
76
        };
77
78
        // @codingStandardsIgnoreStart
79
        $this[] = new Rule(
80
            $stringRule, function () {
81
            return false;
82
        }
83
        );
84
        $this[] = new Rule(
85
            function ($node) {
86
                return $node instanceof FileDescriptor;
87
            },
88
            function (FileDescriptor $node): string {
89
                return $this->generateUrlForDescriptor('file', $node->getPath());
90
            }
91
        );
92
        $this[] = new Rule(
93
            function ($node) {
94
                return $node instanceof PackageDescriptor;
95
            },
96
            function (PackageDescriptor $node): string {
97
                return $this->generateUrlForDescriptor('package', (string) $node->getFullyQualifiedStructuralElementName());
98
            }
99
        );
100
        $this[] = new Rule(
101
            function ($node) {
102
                return $node instanceof NamespaceDescriptor;
103
            },
104
            function (NamespaceDescriptor $node): string {
105
                return $this->generateUrlForDescriptor('namespace', (string) $node->getFullyQualifiedStructuralElementName());
106
            }
107
        );
108
        $this[] = new Rule(
109
            function ($node): bool {
110
                return $node instanceof ClassDescriptor || $node instanceof InterfaceDescriptor || $node instanceof TraitDescriptor;
111
            },
112
            function (DescriptorAbstract $node): string {
113
                return $this->generateUrlForDescriptor('class', (string) $node->getFullyQualifiedStructuralElementName());
114
            }
115
        );
116
        $this[] = new Rule(
117
            function ($node) {
118
                return $node instanceof ConstantDescriptor
119
                    && ($node->getParent() instanceof FileDescriptor || !$node->getParent());
120
            },
121
            function (ConstantDescriptor $node): string {
122
                return $this->generateUrlForDescriptor(
123
                    'namespace',
124
                    (string) $node->getNamespace(),
125
                    'constant_' . $node->getName()
126
                );
127
            }
128
        );
129
        $this[] = new Rule(
130
            function ($node) {
131
                return $node instanceof ConstantDescriptor
132
                    && !($node->getParent() instanceof FileDescriptor || !$node->getParent());
133
            },
134
            function (ConstantDescriptor $node): string {
135
                return $this->generateUrlForDescriptor(
136
                    'class',
137
                    (string) $node->getParent()->getFullyQualifiedStructuralElementName(),
138
                    'constant_' . $node->getName()
139
                );
140
            }
141
        );
142
        $this[] = new Rule(
143
            function ($node) {
144
                return $node instanceof MethodDescriptor;
145
            },
146
            function (MethodDescriptor $node): string {
147
                return $this->generateUrlForDescriptor(
148
                    'class',
149
                    (string) $node->getParent()->getFullyQualifiedStructuralElementName(),
150
                    'method_' . $node->getName()
151
                );
152
            }
153
        );
154
        $this[] = new Rule(
155
            function ($node) {
156
                return $node instanceof FunctionDescriptor;
157
            },
158
            function (FunctionDescriptor $node): string {
159
                return $this->generateUrlForDescriptor(
160
                    'namespace',
161
                    (string) $node->getNamespace(),
162
                    'function_' . $node->getName()
163
                );
164
            }
165
        );
166
        $this[] = new Rule(
167
            function ($node) {
168
                return $node instanceof PropertyDescriptor;
169
            },
170
            function (PropertyDescriptor $node): string {
171
                return $this->generateUrlForDescriptor(
172
                    'class',
173
                    (string) $node->getParent()->getFullyQualifiedStructuralElementName(),
174
                    'property_' . $node->getName()
175
                );
176
            }
177
        );
178
        $this[] = new Rule(
179
            function ($node) {
180
                return $node instanceof Fqsen;
181
            }, $this->fqsenUrlGenerator
182
        );
183
184
        // if this is a link to an external page; return that URL
185
        $this[] = new Rule(
186
            function ($node) {
187
                return $node instanceof Url;
188
            },
189
            function ($node) {
190
                return (string) $node;
191
            }
192
        );
193
194
        // do not generate a file for every unknown type
195
        $this[] = new Rule(
196
            function () {
197
                return true;
198
            },
199
            function () {
200
                return false;
201
            }
202
        );
203
        // @codingStandardsIgnoreEnd
204
    }
205
206
    public function generate($node): ?string
207
    {
208
        $rule = $this->match($node);
209
        if (!$rule) {
210
            return null;
211
        }
212
213
        return $rule->generate($node) ?: '';
214
    }
215
216
    /**
217
     * Tries to match the provided node with one of the rules in this router.
218
     *
219
     * @param string|DescriptorAbstract $node
220
     *
221
     * @return Rule|null
222
     */
223
    private function match($node)
224
    {
225
        /** @var Rule $rule */
226
        foreach ($this as $rule) {
227
            if ($rule->match($node)) {
228
                return $rule;
229
            }
230
        }
231
232
        return null;
233
    }
234
235
    private function generateUrlForDescriptor(string $type, string $fqsen, string $fragment = ''): string
236
    {
237
        switch ($type) {
238
            case 'namespace':
239
                $name = $this->converter->fromNamespace($fqsen);
240
                break;
241
            case 'class':
242
                $name = $this->converter->fromClass($fqsen);
243
                break;
244
            case 'package':
245
                $name = $this->converter->fromPackage($fqsen);
246
                break;
247
            case 'file':
248
                $name = $this->converter->fromFile($fqsen);
249
                break;
250
            default:
251
                throw new InvalidArgumentException('Unknown url type');
252
        }
253
254
        return $this->urlGenerator->generate(
255
            $type,
256
            ['name' => $name, '_fragment' => $fragment]
257
        );
258
    }
259
}
260