Completed
Push — master ( a015de...da4521 )
by Mike
04:17
created

Router::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 10
dl 0
loc 26
ccs 14
cts 14
cp 1
crap 1
rs 9.504
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\Descriptor;
23
use phpDocumentor\Descriptor\DescriptorAbstract;
24
use phpDocumentor\Descriptor\FileDescriptor;
25
use phpDocumentor\Descriptor\FunctionDescriptor;
26
use phpDocumentor\Descriptor\InterfaceDescriptor;
27
use phpDocumentor\Descriptor\MethodDescriptor;
28
use phpDocumentor\Descriptor\NamespaceDescriptor;
29
use phpDocumentor\Descriptor\PackageDescriptor;
30
use phpDocumentor\Descriptor\ProjectDescriptorBuilder;
31
use phpDocumentor\Descriptor\PropertyDescriptor;
32
use phpDocumentor\Descriptor\TraitDescriptor;
33
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen;
34
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
35
use phpDocumentor\Transformer\Transformation;
36
use phpDocumentor\Transformer\Writer\Pathfinder;
37
use UnexpectedValueException;
38
39
/**
40
 * The default for phpDocumentor.
41
 */
42
class Router extends ArrayObject
43
{
44
    private $projectDescriptorBuilder;
45
    private $namespaceUrlGenerator;
46
    private $fileUrlGenerator;
47
    private $packageUrlGenerator;
48
    private $classUrlGenerator;
49
    private $methodUrlGenerator;
50
    private $constantUrlGenerator;
51
    private $functionUrlGenerator;
52
    private $propertyUrlGenerator;
53
    private $fqsenUrlGenerator;
54
55 13
    public function __construct(
56
        ProjectDescriptorBuilder $projectDescriptorBuilder,
57
        UrlGenerator\NamespaceDescriptor $namespaceUrlGenerator,
58
        UrlGenerator\FileDescriptor $fileUrlGenerator,
59
        UrlGenerator\PackageDescriptor $packageUrlGenerator,
60
        UrlGenerator\ClassDescriptor $classUrlGenerator,
61
        UrlGenerator\MethodDescriptor $methodUrlGenerator,
62
        UrlGenerator\ConstantDescriptor $constantUrlGenerator,
63
        UrlGenerator\FunctionDescriptor $functionUrlGenerator,
64
        UrlGenerator\PropertyDescriptor $propertyUrlGenerator,
65
        UrlGenerator\FqsenDescriptor $fqsenUrlGenerator
66
    ) {
67 13
        $this->projectDescriptorBuilder = $projectDescriptorBuilder;
68 13
        $this->namespaceUrlGenerator = $namespaceUrlGenerator;
69 13
        $this->fileUrlGenerator = $fileUrlGenerator;
70 13
        $this->packageUrlGenerator = $packageUrlGenerator;
71 13
        $this->classUrlGenerator = $classUrlGenerator;
72 13
        $this->methodUrlGenerator = $methodUrlGenerator;
73 13
        $this->constantUrlGenerator = $constantUrlGenerator;
74 13
        $this->functionUrlGenerator = $functionUrlGenerator;
75 13
        $this->propertyUrlGenerator = $propertyUrlGenerator;
76 13
        $this->fqsenUrlGenerator = $fqsenUrlGenerator;
77
78 13
        parent::__construct();
79 13
        $this->configure();
80 13
    }
81
82
    /**
83
     * Configuration function to add routing rules to a router.
84
     */
85 13
    public function configure()
86
    {
87 13
        $projectDescriptorBuilder = $this->projectDescriptorBuilder;
88
89
        // Here we cheat! If a string element is passed to this rule then we try to transform it into a Descriptor
90
        // if the node is translated we do not let it match and instead fall through to one of the other rules.
91
        $stringRule = function (&$node) use ($projectDescriptorBuilder) {
92 13
            $elements = $projectDescriptorBuilder->getProjectDescriptor()->getIndexes()->get('elements');
93 13
            if (is_string($node) && isset($elements[$node])) {
94 1
                $node = $elements[$node];
95
            }
96
97 13
            return false;
98 13
        };
99
100
        // @codingStandardsIgnoreStart
101
        $this[] = new Rule($stringRule, function () {
102
            return false;
103 13
        });
104
        $this[] = new Rule(function ($node) {
105 13
            return $node instanceof FileDescriptor;
106 13
        }, $this->fileUrlGenerator);
107
        $this[] = new Rule(function ($node) {
108 12
            return $node instanceof PackageDescriptor;
109 13
        }, $this->packageUrlGenerator);
110
        $this[] = new Rule(function ($node) {
111 11
            return $node instanceof TraitDescriptor;
112 13
        }, $this->classUrlGenerator);
113
        $this[] = new Rule(function ($node) {
114 10
            return $node instanceof NamespaceDescriptor;
115 13
        }, $this->namespaceUrlGenerator);
116
        $this[] = new Rule(function ($node) {
117 9
            return $node instanceof InterfaceDescriptor;
118 13
        }, $this->classUrlGenerator);
119
        $this[] = new Rule(function ($node) {
120 8
            return $node instanceof ClassDescriptor;
121 13
        }, $this->classUrlGenerator);
122
        $this[] = new Rule(function ($node) {
123 7
            return $node instanceof ConstantDescriptor;
124 13
        }, $this->constantUrlGenerator);
125
        $this[] = new Rule(function ($node) {
126 6
            return $node instanceof MethodDescriptor;
127 13
        }, $this->methodUrlGenerator);
128
        $this[] = new Rule(function ($node) {
129 4
            return $node instanceof FunctionDescriptor;
130 13
        }, $this->functionUrlGenerator);
131
        $this[] = new Rule(function ($node) {
132 3
            return $node instanceof PropertyDescriptor;
133 13
        }, $this->propertyUrlGenerator);
134
        $this[] = new Rule(function ($node) {
135 2
            return $node instanceof Fqsen;
136 13
        }, $this->fqsenUrlGenerator);
137
138
        // if this is a link to an external page; return that URL
139 13
        $this[] = new Rule(
140
            function ($node) {
141 1
                return $node instanceof Url;
142 13
            },
143
            function ($node) {
144 1
                return (string) $node;
145 13
            }
146
        );
147
148
        // do not generate a file for every unknown type
149
        $this[] = new Rule(function () {
150
            return true;
151
        }, function () {
152
            return false;
153 13
        });
154
        // @codingStandardsIgnoreEnd
155 13
    }
156
157
    /**
158
     * Tries to match the provided node with one of the rules in this router.
159
     *
160
     * @param string|DescriptorAbstract $node
161
     *
162
     * @return Rule|null
163
     */
164 14
    public function match($node)
165
    {
166
        /** @var Rule $rule */
167 14
        foreach ($this as $rule) {
168 14
            if ($rule->match($node)) {
169 14
                return $rule;
170
            }
171
        }
172
173
        return null;
174
    }
175
176
    /**
177
     * Uses the currently selected node and transformation to assemble the destination path for the file.
178
     *
179
     * Writers accept the use of a Query to be able to generate output for multiple objects using the same
180
     * template.
181
     *
182
     * The given node is the result of such a query, or if no query given the selected element, and the transformation
183
     * contains the destination file.
184
     *
185
     * Since it is important to be able to generate a unique name per element can the user provide a template variable
186
     * in the name of the file.
187
     * Such a template variable always resides between double braces and tries to take the node value of a given
188
     * query string.
189
     *
190
     * Example:
191
     *
192
     *   An artifact stating `classes/{{name}}.html` will try to find the
193
     *   node 'name' as a child of the given $node and use that value instead.
194
     *
195
     * @throws InvalidArgumentException if no artifact is provided and no routing rule matches.
196
     * @throws UnexpectedValueException if the provided node does not contain anything.
197
     *
198
     * @return null|string returns the destination location or false if generation should be aborted.
199
     */
200
    public function destination(Descriptor $descriptor, Transformation $transformation): ?string
201
    {
202
        $path = $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . $transformation->getArtifact();
203
        if (!$transformation->getArtifact()) {
204
            $rule = $this->match($descriptor);
0 ignored issues
show
Documentation introduced by
$descriptor is of type object<phpDocumentor\Descriptor\Descriptor>, but the function expects a string|object<phpDocumen...tor\DescriptorAbstract>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
205
            if (!$rule) {
206
                throw new InvalidArgumentException(
207
                    'No matching routing rule could be found for the given node, please provide an artifact location, '
208
                    . 'encountered: ' . get_class($descriptor)
209
                );
210
            }
211
212
            $rule = new ForFileProxy($rule);
213
            $url = $rule->generate($descriptor);
0 ignored issues
show
Documentation introduced by
$descriptor is of type object<phpDocumentor\Descriptor\Descriptor>, but the function expects a string|object<phpDocumen...tor\DescriptorAbstract>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
214
            if ($url === false || $url[0] !== DIRECTORY_SEPARATOR) {
215
                return null;
216
            }
217
218
            $path = $transformation->getTransformer()->getTarget()
219
                . str_replace('/', DIRECTORY_SEPARATOR, $url);
220
        }
221
222
        $finder = new Pathfinder();
223
        $destination = preg_replace_callback(
224
            '/{{([^}]+)}}/', // explicitly do not use the unicode modifier; this breaks windows
225
            function ($query) use ($descriptor, $finder) {
226
                // strip any surrounding \ or /
227
                $filepart = trim((string) current($finder->find($descriptor, $query[1])), '\\/');
228
229
                // make it windows proof
230
                if (extension_loaded('iconv')) {
231
                    $filepart = iconv('UTF-8', 'ASCII//TRANSLIT', $filepart);
232
                }
233
234
                return strpos($filepart, '/') !== false
235
                    ? implode('/', array_map('urlencode', explode('/', $filepart)))
236
                    : implode('\\', array_map('urlencode', explode('\\', $filepart)));
237
            },
238
            $path
239
        );
240
241
        // replace any \ with the directory separator to be compatible with the
242
        // current filesystem and allow the next file_exists to do its work
243
        $destination = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $destination);
244
245
        // create directory if it does not exist yet
246
        if (!file_exists(dirname($destination))) {
247
            mkdir(dirname($destination), 0777, true);
248
        }
249
250
        return $destination;
251
    }
252
}
253