generator: use a proper destination
[mdref/mdref] / mdref / Entry.php
1 <?php
2
3 namespace mdref;
4
5 use IteratorAggregate;
6 use function array_pop;
7 use function count;
8 use function ctype_lower;
9 use function ctype_upper;
10 use function dirname;
11 use function end;
12 use function explode;
13 use function implode;
14 use function strlen;
15 use function strtr;
16 use function substr;
17 use function trim;
18
19 /**
20 * A single reference entry
21 */
22 class Entry implements IteratorAggregate {
23 /**
24 * Compound name
25 * @var string
26 */
27 private $name;
28
29 /**
30 * Split name
31 * @var array
32 */
33 private $list;
34
35 /**
36 * The containing repository
37 * @var \mdref\Repo
38 */
39 private $repo;
40
41 /**
42 * The file path, if the refentry exists
43 * @var ?string
44 */
45 private $path;
46
47 /**
48 * The file instance of this entry
49 * @var \mdref\File
50 */
51 private $file;
52
53 /**
54 * @param string $name the compound name of the ref entry, e.g. "pq/Connection/exec"
55 * @param Repo $repo the containing repository
56 */
57 public function __construct(string $name, Repo $repo) {
58 $this->repo = $repo;
59 $this->name = $name;
60 $this->list = explode("/", $name);
61 $this->path = $repo->hasEntry($name);
62 }
63
64 /**
65 * Get the compound name, e.g. "pq/Connection/exec"
66 * @return string
67 */
68 public function getName() : string {
69 return $this->name;
70 }
71
72 /**
73 * Get the containing repository
74 * @return \mdref\Repo
75 */
76 public function getRepo() : Repo {
77 return $this->repo;
78 }
79
80 /**
81 * Get the file path, if any
82 * @return string
83 */
84 public function getPath() : ?string {
85 return $this->path;
86 }
87
88 /**
89 * Get the file instance of this entry
90 *
91 * @return \mdref\File
92 * @throws Exception
93 */
94 public function getFile() : File {
95 if (!$this->file) {
96 $this->file = new File($this->path);
97 }
98 return $this->file;
99 }
100
101 /**
102 * Get edit URL
103 * @return string
104 */
105 public function getEditUrl() : string {
106 return $this->repo->getEditUrl($this->name);
107 }
108
109 /**
110 * Read the title of the ref entry file
111 *
112 * @return string
113 * @throws Exception
114 */
115 public function getTitle() : string {
116 if ($this->isFile()) {
117 return trim($this->getFile()->readTitle());
118 }
119 if ($this->isRoot()) {
120 return trim($this->repo->getRootEntry()->getTitle());
121 }
122 return $this->name;
123 }
124
125 /**
126 * Read the first line of the description of the ref entry file
127 *
128 * @return string
129 * @throws Exception
130 */
131 public function getDescription() : string {
132 if ($this->isFile()) {
133 return trim($this->getFile()->readDescription());
134 }
135 if ($this->isRoot()) {
136 return trim($this->repo->getRootEntry()->getDescription());
137 }
138 return $this;
139 }
140
141 /**
142 * Read the full description of the ref entry file
143 *
144 * @return string
145 * @throws Exception
146 */
147 public function getFullDescription() : string {
148 if ($this->isFile()) {
149 return trim($this->getFile()->readFullDescription());
150 }
151 if ($this->isRoot()) {
152 return trim($this->repo->getRootEntry()->getFullDescription());
153 }
154 return $this;
155 }
156
157 /**
158 * Read the intriductory section of the refentry file
159 *
160 * @return string
161 * @throws Exception
162 */
163 public function getIntro() : string {
164 if ($this->isFile()) {
165 return trim($this->getFile()->readIntro());
166 }
167 if ($this->isRoot()) {
168 return trim($this->repo->getRootEntry()->getIntro());
169 }
170 return "";
171 }
172
173 /**
174 * Check if the refentry exists
175 * @return bool
176 */
177 public function isFile() : bool {
178 return strlen($this->path) > 0;
179 }
180
181 /**
182 * Check if this is the first entry of the reference tree
183 * @return bool
184 */
185 public function isRoot() : bool {
186 return count($this->list) === 1;
187 }
188
189 /**
190 * Get the parent ref entry
191 * @return \mdref\Entry
192 */
193 public function getParent() : ?Entry {
194 switch ($dirn = dirname($this->name)) {
195 case ".":
196 case "/":
197 return null;
198 default:
199 return $this->repo->getEntry($dirn);
200 }
201 }
202
203 /**
204 * Get the list of parents up-down
205 * @return array
206 */
207 public function getParents() : array {
208 $parents = array();
209 for ($parent = $this->getParent(); $parent; $parent = $parent->getParent()) {
210 array_unshift($parents, $parent);
211 }
212 return $parents;
213 }
214
215 /**
216 * Guess whether this ref entry is about a function or method
217 * @return bool
218 */
219 public function isFunction() : bool {
220 $base = end($this->list);
221 return $base[0] === "_" || ctype_lower($base[0]);
222 }
223
224 /**
225 * Guess whether this ref entry is about a namespace, interface or class
226 * @return bool
227 */
228 public function isNsClass() : bool {
229 $base = end($this->list);
230 return ctype_upper($base[0]);
231 }
232
233 /**
234 * @return mixed
235 */
236 public function getEntryName() {
237 return end($this->list);
238 }
239
240 /**
241 * Get namespaced name
242 * @return string
243 */
244 public function getNsName() : string {
245 if ($this->isRoot()) {
246 return $this->getName();
247 } elseif ($this->isFunction()) {
248 $parts = explode("/", trim($this->getName(), "/"));
249 $self = array_pop($parts);
250 return implode("\\", $parts) . "::" . $self;
251 } else {
252 return strtr($this->getName(), "/", "\\");
253 }
254 }
255
256 /**
257 * Display name
258 * @return string
259 */
260 public function __toString() : string {
261 $parts = explode("/", trim($this->getName(), "/"));
262 $myself = array_pop($parts);
263 if (!$parts) {
264 return $myself;
265 }
266 $parent = end($parts);
267
268 switch ($myself[0]) {
269 case ":":
270 return "★" . substr($myself, 1);
271
272 default:
273 if (!ctype_lower($myself[0]) || ctype_lower($parent[0])) {
274 return $myself;
275 }
276 case "_":
277 return $parent . "::" . $myself;
278 }
279 }
280
281 /**
282 * Get the dirname for child entries
283 * @return string
284 */
285 public function getNextDirname() : string {
286 return dirname($this->path) . "/" . basename($this->path, ".md");
287 }
288
289 /**
290 * Guess whether there are any child nodes
291 * @param string $glob
292 * @param bool $loose
293 * @return bool
294 */
295 function hasIterator(?string $glob = null, bool $loose = false) : bool {
296 if (isset($glob) && strlen($glob)) {
297 return glob($this->getNextDirname() . "/$glob") ||
298 ($loose && glob($this->getNextDirname() . "/*/$glob"));
299 } elseif ($this->isRoot()) {
300 return true;
301 } elseif ($this->getNextDirname() !== "/") {
302 return is_dir($this->getNextDirname());
303 } else {
304 return false;
305 }
306 }
307
308 /**
309 * Guess whether there are namespace/interface/class child nodes
310 * @return bool
311 */
312 function hasNsClasses() : bool {
313 return $this->hasIterator("/[A-Z]*.md", true);
314 }
315
316 /**
317 * Guess whether there are function/method child nodes
318 * @return bool
319 */
320 function hasFunctions() : bool {
321 return $this->hasIterator("/[a-z_]*.md");
322 }
323
324 /**
325 * Implements IteratorAggregate
326 * @return Tree child nodes
327 */
328 function getIterator() : Tree {
329 return new Tree($this->getNextDirname(), $this->repo);
330 }
331
332 /**
333 * Get the structure of the refentry
334 * @return Structure
335 */
336 function getStructure() : Structure {
337 return new Structure($this);
338 }
339 }