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