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 phpDocumentor\Descriptor\Collection; |
19
|
|
|
use phpDocumentor\Descriptor\DescriptorAbstract; |
20
|
|
|
use phpDocumentor\Descriptor\Type\CollectionDescriptor; |
21
|
|
|
use phpDocumentor\Reflection\Type; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Renders an HTML anchor pointing to the location of the provided element. |
25
|
|
|
*/ |
26
|
|
|
class Renderer |
27
|
|
|
{ |
28
|
|
|
/** @var string */ |
29
|
|
|
protected $destination = ''; |
30
|
|
|
|
31
|
|
|
/** @var Queue */ |
32
|
|
|
private $routers; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Initializes this renderer with a set of routers that are checked. |
36
|
|
|
*/ |
37
|
1 |
|
public function __construct(Queue $routers) |
38
|
|
|
{ |
39
|
1 |
|
$this->routers = $routers; |
40
|
1 |
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Sets the destination directory relative to the Project's Root. |
44
|
|
|
* |
45
|
|
|
* The destination is the target directory containing the resulting |
46
|
|
|
* file. This destination is relative to the Project's root and can |
47
|
|
|
* be used for the calculation of nesting depths, etc. |
48
|
|
|
* |
49
|
|
|
* For this specific extension the destination is provided in the |
50
|
|
|
* Twig writer itself. |
51
|
|
|
* |
52
|
|
|
* @param string $destination |
53
|
|
|
* |
54
|
|
|
* @see \phpDocumentor\Transformer\Writer\Twig for the invocation |
55
|
|
|
* of this method. |
56
|
|
|
*/ |
57
|
1 |
|
public function setDestination($destination) |
58
|
|
|
{ |
59
|
1 |
|
$this->destination = $destination; |
60
|
1 |
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Returns the target directory relative to the Project's Root. |
64
|
|
|
* |
65
|
|
|
* @return string |
66
|
|
|
*/ |
67
|
1 |
|
public function getDestination() |
68
|
|
|
{ |
69
|
1 |
|
return $this->destination; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @param string|DescriptorAbstract $value |
74
|
|
|
* @param string $presentation |
75
|
|
|
* |
76
|
|
|
* @return bool|mixed|string|\string[] |
77
|
|
|
*/ |
78
|
9 |
|
public function render($value, $presentation) |
79
|
|
|
{ |
80
|
9 |
|
if (is_array($value) && current($value) instanceof Type) { |
81
|
|
|
return $this->renderType($value, $presentation); |
82
|
|
|
} |
83
|
|
|
|
84
|
9 |
|
if (is_array($value) || $value instanceof \Traversable || $value instanceof Collection) { |
85
|
3 |
|
return $this->renderASeriesOfLinks($value, $presentation); |
86
|
|
|
} |
87
|
|
|
|
88
|
9 |
|
if ($value instanceof CollectionDescriptor) { |
89
|
2 |
|
return $this->renderTypeCollection($value, $presentation); |
90
|
|
|
} |
91
|
|
|
|
92
|
9 |
|
return $this->renderLink($value, $presentation); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Converts the given path to be relative to the root of the documentation |
97
|
|
|
* target directory. |
98
|
|
|
* |
99
|
|
|
* It is not possible to use absolute paths in documentation templates since |
100
|
|
|
* they may be used locally, or in a subfolder. As such we need to calculate |
101
|
|
|
* the number of levels to go up from the current document's directory and |
102
|
|
|
* then append the given path. |
103
|
|
|
* |
104
|
|
|
* For example: |
105
|
|
|
* |
106
|
|
|
* Suppose you are in <root>/classes/my/class.html and you want open |
107
|
|
|
* <root>/my/index.html then you provide 'my/index.html' to this method |
108
|
|
|
* and it will convert it into ../../my/index.html (<root>/classes/my is |
109
|
|
|
* two nesting levels until the root). |
110
|
|
|
* |
111
|
|
|
* This method does not try to normalize or optimize the paths in order to |
112
|
|
|
* save on development time and performance, and because it adds no real |
113
|
|
|
* value. |
114
|
|
|
* |
115
|
|
|
* @param string $relative_path |
116
|
|
|
* |
117
|
|
|
* @return string|null |
118
|
|
|
*/ |
119
|
6 |
|
public function convertToRootPath($relative_path): ?string |
120
|
|
|
{ |
121
|
|
|
// get the path to the root directory |
122
|
6 |
|
$path_parts = explode(DIRECTORY_SEPARATOR, $this->getDestination()); |
123
|
6 |
|
$path_to_root = (count($path_parts) > 1) |
124
|
1 |
|
? implode('/', array_fill(0, count($path_parts) - 1, '..')) . '/' |
125
|
6 |
|
: ''; |
126
|
|
|
|
127
|
|
|
// append the relative path to the root |
128
|
6 |
|
if (is_string($relative_path) && ($relative_path[0] !== '@')) { |
129
|
4 |
|
return $path_to_root . ltrim($relative_path, '/'); |
130
|
|
|
} |
131
|
|
|
|
132
|
2 |
|
$rule = $this->routers->match($relative_path); |
133
|
2 |
|
if (!$rule) { |
134
|
1 |
|
return null; |
135
|
|
|
} |
136
|
|
|
|
137
|
1 |
|
$generatedPath = $rule->generate($relative_path); |
138
|
|
|
|
139
|
1 |
|
return $generatedPath ? $path_to_root . ltrim($generatedPath, '/') : null; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Returns a series of anchors and strings for the given collection of routable items. |
144
|
|
|
* |
145
|
|
|
* @param array|\Traversable|Collection $value |
146
|
|
|
* @param string $presentation |
147
|
|
|
* |
148
|
|
|
* @return string[] |
149
|
|
|
*/ |
150
|
|
|
protected function renderASeriesOfLinks($value, $presentation): array |
151
|
|
|
{ |
152
|
|
|
if ($value instanceof Collection) { |
153
|
|
|
$value = $value->getAll(); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$result = []; |
157
|
|
|
foreach ($value as $path) { |
158
|
|
|
$result[] = $this->render($path, $presentation); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
return $result; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Renders the view representation for an array or collection. |
166
|
|
|
* |
167
|
|
|
* @param CollectionDescriptor $value |
168
|
|
|
* @param string $presentation |
169
|
|
|
* |
170
|
|
|
* @return string |
171
|
|
|
*/ |
172
|
|
|
protected function renderTypeCollection($value, $presentation) |
173
|
|
|
{ |
174
|
|
|
$baseType = $this->render($value->getBaseType(), $presentation); |
|
|
|
|
175
|
|
|
$keyTypes = $this->render($value->getKeyTypes(), $presentation); |
|
|
|
|
176
|
|
|
$types = $this->render($value->getTypes(), $presentation); |
|
|
|
|
177
|
|
|
|
178
|
|
|
$arguments = []; |
179
|
|
|
if ($keyTypes) { |
180
|
|
|
$arguments[] = implode('|', $keyTypes); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
$arguments[] = implode('|', $types); |
184
|
|
|
|
185
|
|
|
if ($value->getName() === 'array' && count($value->getKeyTypes()) === 0) { |
186
|
|
|
$typeString = (count($types) > 1) ? '(' . reset($arguments) . ')' : reset($arguments); |
187
|
|
|
$collection = $typeString . '[]'; |
188
|
|
|
} else { |
189
|
|
|
$collection = ($baseType ?: $value->getName()) . '<' . implode(',', $arguments) . '>'; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return $collection; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
protected function renderLink($path, $presentation) |
196
|
|
|
{ |
197
|
|
|
$url = false; |
198
|
|
|
$rule = $this->routers->match($path); |
199
|
|
|
if ($rule) { |
200
|
|
|
$generatedUrl = $rule->generate($path); |
201
|
|
|
$url = $generatedUrl ? ltrim($generatedUrl, '/') : false; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
if (is_string($url) |
205
|
|
|
&& $url[0] !== '/' |
206
|
|
|
&& (strpos($url, 'http://') !== 0) |
207
|
|
|
&& (strpos($url, 'https://') !== 0) |
208
|
|
|
&& (strpos($url, 'ftp://') !== 0) |
209
|
|
|
) { |
210
|
|
|
$url = $this->convertToRootPath($url); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
switch ($presentation) { |
214
|
|
|
case 'url': // return the first url |
215
|
|
|
return $url; |
216
|
|
|
case 'class:short': |
217
|
|
|
$parts = explode('\\', (string) $path); |
218
|
|
|
$path = end($parts); |
219
|
|
|
break; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
return $url ? sprintf('<a href="%s">%s</a>', $url, $path) : $path; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
private function renderType($value, string $presentation): array |
|
|
|
|
226
|
|
|
{ |
227
|
|
|
$result = []; |
228
|
|
|
foreach ($value as $type) { |
229
|
|
|
$result[] = (string) $type; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
return $result; |
233
|
|
|
} |
234
|
|
|
} |
235
|
|
|
|
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: