GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 31eefc...c517ee )
by Jad
13s
created

UrlFilters::typeLinks()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the ApiGen (http://apigen.org)
5
 *
6
 * For the full copyright and license information, please view
7
 * the file LICENSE that was distributed with this source code.
8
 */
9
10
namespace ApiGen\Templating\Filters;
11
12
use ApiGen\Configuration\Configuration;
13
use ApiGen\Configuration\ConfigurationOptions as CO;
14
use ApiGen\Contracts\Generator\Resolvers\ElementResolverInterface;
15
use ApiGen\Contracts\Parser\Reflection\ClassReflectionInterface;
16
use ApiGen\Contracts\Parser\Reflection\ElementReflectionInterface;
17
use ApiGen\Contracts\Parser\Reflection\FunctionReflectionInterface;
18
use ApiGen\Generator\Markups\Markup;
19
use ApiGen\Generator\SourceCodeHighlighter\SourceCodeHighlighter;
20
use ApiGen\Templating\Filters\Helpers\ElementLinkFactory;
21
use ApiGen\Templating\Filters\Helpers\LinkBuilder;
22
use ApiGen\Templating\Filters\Helpers\Strings;
23
use Latte\Runtime\Filters as LatteFilters;
24
use Nette\Utils\Validators;
25
26
class UrlFilters extends Filters
27
{
28
29
    /**
30
     * @var SourceCodeHighlighter
31
     */
32
    private $highlighter;
33
34
    /**
35
     * @var Markup
36
     */
37
    private $markup;
38
39
    /**
40
     * @var ElementResolverInterface
41
     */
42
    private $elementResolver;
43
44
    /**
45
     * @var Configuration
46
     */
47
    private $configuration;
48
49
    /**
50
     * @var LinkBuilder
51
     */
52
    private $linkBuilder;
53
54
    /**
55
     * @var ElementLinkFactory
56
     */
57
    private $elementLinkFactory;
58
59
60
    public function __construct(
61
        Configuration $configuration,
62
        SourceCodeHighlighter $highlighter,
63
        Markup $markup,
64
        ElementResolverInterface $elementResolver,
65
        LinkBuilder $linkBuilder,
66
        ElementLinkFactory $elementLinkFactory
67
    ) {
68
        $this->highlighter = $highlighter;
69
        $this->markup = $markup;
70
        $this->elementResolver = $elementResolver;
71
        $this->configuration = $configuration;
72
        $this->linkBuilder = $linkBuilder;
73
        $this->elementLinkFactory = $elementLinkFactory;
74
    }
75
76
77
    /**
78
     * Tries to parse a definition of a class/method/property/constant/function
79
     * and returns the appropriate link if successful.
80
     *
81
     * @param string $definition
82
     * @param ElementReflectionInterface $reflectionElement
83
     * @return string|NULL
84
     */
85
    public function resolveLink($definition, ElementReflectionInterface $reflectionElement)
86
    {
87
        if (empty($definition)) {
88
            return null;
89
        }
90
91
        $suffix = '';
92
        if (substr($definition, -2) === '[]') {
93
            $definition = substr($definition, 0, -2);
94
            $suffix = '[]';
95
        }
96
97
        $element = $this->elementResolver->resolveElement($definition, $reflectionElement, $expectedName);
0 ignored issues
show
Documentation introduced by
$reflectionElement is of type object<ApiGen\Contracts\...entReflectionInterface>, but the function expects a string.

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...
98
        if ($element === null || $element instanceof FunctionReflectionInterface) {
99
            return $expectedName;
100
        }
101
102
        $classes = [];
103
        if ($element->isDeprecated()) {
104
            $classes[] = 'deprecated';
105
        }
106
107
        /** @var FunctionReflectionInterface $element */
108
        if (! $element->isValid()) {
109
            $classes[] = 'invalid';
110
        }
111
112
        $link = $this->createLinkForElement($element, $classes);
113
        return '<code>' . $link . $suffix . '</code>';
114
    }
115
116
117
    /**
118
     * @param string $value
119
     * @param string $name
120
     * @param ElementReflectionInterface $reflectionElement
121
     * @return string
122
     */
123
    public function annotation($value, $name, ElementReflectionInterface $reflectionElement)
124
    {
125
        $annotationProcessors = [
126
            'return' => $this->processReturnAnnotations($value, $reflectionElement),
127
            'throws' => $this->processThrowsAnnotations($value, $reflectionElement),
128
            'license' => $this->processLicenseAnnotations($value),
129
            'link' => $this->processLinkAnnotations($value),
130
            'see' => $this->processSeeAnnotations($value, $reflectionElement),
131
            'uses' => $this->processUsesAndUsedbyAnnotations($value, $reflectionElement),
132
            'usedby' => $this->processUsesAndUsedbyAnnotations($value, $reflectionElement),
133
        ];
134
135
        if (isset($annotationProcessors[$name])) {
136
            return $annotationProcessors[$name];
137
        }
138
139
        return $this->doc($value, $reflectionElement);
140
    }
141
142
143
    /**
144
     * Returns links for types.
145
     *
146
     * @param string $annotation
147
     * @param ElementReflectionInterface $reflectionElement
148
     * @return string
149
     */
150
    public function typeLinks($annotation, ElementReflectionInterface $reflectionElement)
151
    {
152
        $links = [];
153
154
        // typehints can not contains spaces
155
        // valid typehint is:
156
        // [TYPE[|TYPE[|...]][SPACE[METHOD|PARAM][DESCRIPTION]]
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
157
        $parts = explode(' ', $annotation);
158
159
        foreach (explode('|', $parts[0]) as $type) {
160
            $type = $this->getTypeName($type, false);
161
            $links[] = $this->resolveLink($type, $reflectionElement) ?: LatteFilters::escapeHtml(ltrim($type, '\\'));
162
        }
163
164
        return implode('|', $links);
165
    }
166
167
168
    /********************* description *********************/
169
170
171
    /**
172
     * @param string $annotation
173
     * @param ElementReflectionInterface $reflectionElement
174
     * @return string
175
     */
176
    public function description($annotation, ElementReflectionInterface $reflectionElement)
177
    {
178
        $description = trim(strpbrk($annotation, "\n\r\t $")) ?: $annotation;
179
        return $this->doc($description, $reflectionElement);
180
    }
181
182
183
    /**
184
     * @param ElementReflectionInterface $reflectionElement
185
     * @param bool $block
186
     * @return string
187
     */
188
    public function shortDescription(ElementReflectionInterface $reflectionElement, $block = false)
189
    {
190
        return $this->doc($reflectionElement->getShortDescription(), $reflectionElement, $block);
191
    }
192
193
194
    /**
195
     * @return string
196
     */
197
    public function longDescription(ElementReflectionInterface $element)
198
    {
199
        $long = $element->getLongDescription();
200
201
        // Merge lines
202
        $long = preg_replace_callback('~(?:<(code|pre)>.+?</\1>)|([^<]*)~s', function ($matches) {
203
            return ! empty($matches[2])
204
                ? preg_replace('~\n(?:(\s+\n){2,})+~', ' ', $matches[2])
205
                : $matches[0];
206
        }, $long);
207
208
        return $this->doc($long, $element, true);
209
    }
210
211
212
    /********************* text formatter *********************/
213
214
215
    /**
216
     * @param string $text
217
     * @param ElementReflectionInterface $reflectionElement
218
     * @param bool $block
219
     * @return string
220
     */
221
    public function doc($text, ElementReflectionInterface $reflectionElement, $block = false)
222
    {
223
        $text = $this->resolveInternalAnnotation($text);
224
225
        // Process markup
226
        if ($block) {
227
            $text = $this->markup->block($text);
228
        } else {
229
            $text = $this->markup->line($text);
230
        }
231
232
        return $this->resolveLinkAndSeeAnnotation($text, $reflectionElement);
233
    }
234
235
236
    /**
237
     * @param string $text
238
     * @return string
239
     */
240
    private function resolveInternalAnnotation($text)
241
    {
242
        $pattern = '~\\{@(\\w+)(?:(?:\\s+((?>(?R)|[^{}]+)*)\\})|\\})~';
243
        return preg_replace_callback($pattern, function ($matches) {
244
            if ($matches[1] !== 'internal') {
245
                return $matches[0];
246
            }
247
248
            if ($this->configuration->getOption(CO::INTERNAL) && isset($matches[2])) {
249
                return $matches[2];
250
            }
251
252
            return '';
253
        }, $text);
254
    }
255
256
257
    /**
258
     * @param string $text
259
     * @param ElementReflectionInterface $reflectionElement
260
     * @return string
261
     */
262
    private function resolveLinkAndSeeAnnotation($text, ElementReflectionInterface $reflectionElement)
263
    {
264
        return preg_replace_callback('~{@(?:link|see)\\s+([^}]+)}~', function ($matches) use ($reflectionElement) {
265
            list($url, $description) = Strings::split($matches[1]);
266
267
            if (Validators::isUri($url)) {
268
                return $this->linkBuilder->build($url, $description ?: $url);
269
            }
270
271
            if ($link = $this->resolveLink($matches[1], $reflectionElement)) {
272
                return $link;
273
            }
274
275
            return $matches[1];
276
        }, $text);
277
    }
278
279
280
    /********************* highlight *********************/
281
282
283
    /**
284
     * @param string $source
285
     * @param ElementReflectionInterface $reflectionElement
286
     * @return string
287
     */
288
    public function highlightPhp($source, ElementReflectionInterface $reflectionElement)
289
    {
290
        return $this->resolveLink($this->getTypeName($source), $reflectionElement)
291
            ?: $this->highlighter->highlight((string) $source);
292
    }
293
294
295
    /**
296
     * @param string $definition
297
     * @param ElementReflectionInterface $reflectionElement
298
     * @return string
299
     */
300
    public function highlightValue($definition, ElementReflectionInterface $reflectionElement)
301
    {
302
        return $this->highlightPhp(preg_replace('~^(?:[ ]{4}|\t)~m', '', $definition), $reflectionElement);
303
    }
304
305
306
    /**
307
     * @return string
308
     */
309
    private function createLinkForElement($reflectionElement, array $classes)
310
    {
311
        return $this->elementLinkFactory->createForElement($reflectionElement, $classes);
312
    }
313
314
315
    /**
316
     * @param string $value
317
     * @param ElementReflectionInterface $reflectionElement
318
     * @return string
319
     */
320
    private function processReturnAnnotations($value, ElementReflectionInterface $reflectionElement)
321
    {
322
        $description = $this->getDescriptionFromValue($value, $reflectionElement);
323
        $typeLinks = $this->typeLinks($value, $reflectionElement);
324
        return $typeLinks . $description;
325
    }
326
327
328
    /**
329
     * @param string $value
330
     * @param ElementReflectionInterface $elementReflection
331
     * @return string
332
     */
333
    private function processThrowsAnnotations($value, ElementReflectionInterface $elementReflection)
334
    {
335
        $description = $this->getDescriptionFromValue($value, $elementReflection);
336
        $typeLinks = $this->typeLinks($value, $elementReflection);
337
        return $typeLinks . $description;
338
    }
339
340
341
    /**
342
     * @param mixed $value
343
     * @param ElementReflectionInterface $elementReflection
344
     * @return string
345
     */
346
    private function getDescriptionFromValue($value, ElementReflectionInterface $elementReflection)
347
    {
348
        $description = trim(strpbrk($value, "\n\r\t $")) ?: null;
349
        if ($description) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $description of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
350
            $description = '<br>' . $this->doc($description, $elementReflection);
351
        }
352
        return $description;
353
    }
354
355
356
    /**
357
     * @param string $value
358
     * @return string
359
     */
360
    private function processLicenseAnnotations($value)
361
    {
362
        list($url, $description) = Strings::split($value);
363
        return $this->linkBuilder->build($url, $description ?: $url);
364
    }
365
366
367
    /**
368
     * @param string $value
369
     * @return string
370
     */
371
    private function processLinkAnnotations($value)
372
    {
373
        list($url, $description) = Strings::split($value);
374
        if (Validators::isUrl($url)) {
375
            return $this->linkBuilder->build($url, $description ?: $url);
376
        }
377
        return null;
378
    }
379
380
381
    /**
382
     * @param string $value
383
     * @param ElementReflectionInterface $reflectionElement
384
     * @return string
385
     */
386
    private function processSeeAnnotations($value, ElementReflectionInterface $reflectionElement)
387
    {
388
        $doc = [];
389
        foreach (preg_split('~\\s*,\\s*~', $value) as $link) {
390
            if ($this->elementResolver->resolveElement($link, $reflectionElement) !== null) {
0 ignored issues
show
Documentation introduced by
$reflectionElement is of type object<ApiGen\Contracts\...entReflectionInterface>, but the function expects a string.

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...
391
                $doc[] = $this->typeLinks($link, $reflectionElement);
392
            } else {
393
                $doc[] = $this->doc($link, $reflectionElement);
394
            }
395
        }
396
        return implode(', ', $doc);
397
    }
398
399
400
    /**
401
     * @param string $value
402
     * @param ElementReflectionInterface $reflectionElement
403
     * @return string
404
     */
405
    private function processUsesAndUsedbyAnnotations($value, ElementReflectionInterface $reflectionElement)
406
    {
407
        list($link, $description) = Strings::split($value);
408
        $separator = $reflectionElement instanceof ClassReflectionInterface || ! $description ? ' ' : '<br>';
409
        if ($this->elementResolver->resolveElement($link, $reflectionElement) !== null) {
0 ignored issues
show
Documentation introduced by
$reflectionElement is of type object<ApiGen\Contracts\...entReflectionInterface>, but the function expects a string.

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...
410
            $value = $this->typeLinks($link, $reflectionElement) . $separator . $description;
411
            return trim($value);
412
        }
413
        return null;
414
    }
415
}
416