Completed
Push — master ( eed11b...a015de )
by Mike
04:11
created

StandardRouter::destination()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
nc 6
nop 2
dl 0
loc 52
ccs 0
cts 28
cp 0
crap 72
rs 7.8028
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 StandardRouter 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\Standard\NamespaceDescriptor $namespaceUrlGenerator,
58
        UrlGenerator\Standard\FileDescriptor $fileUrlGenerator,
59
        UrlGenerator\Standard\PackageDescriptor $packageUrlGenerator,
60
        UrlGenerator\Standard\ClassDescriptor $classUrlGenerator,
61
        UrlGenerator\Standard\MethodDescriptor $methodUrlGenerator,
62
        UrlGenerator\Standard\ConstantDescriptor $constantUrlGenerator,
63
        UrlGenerator\Standard\FunctionDescriptor $functionUrlGenerator,
64
        UrlGenerator\Standard\PropertyDescriptor $propertyUrlGenerator,
65
        UrlGenerator\Standard\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