1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file is part of Cecil. |
||
5 | * |
||
6 | * (c) Arnaud Ligny <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | declare(strict_types=1); |
||
13 | |||
14 | namespace Cecil\Collection\Page; |
||
15 | |||
16 | use Cecil\Collection\Item; |
||
17 | use Cecil\Exception\RuntimeException; |
||
18 | use Cecil\Util; |
||
19 | use Cocur\Slugify\Slugify; |
||
20 | use Symfony\Component\Finder\SplFileInfo; |
||
21 | |||
22 | /** |
||
23 | * Page class. |
||
24 | * |
||
25 | * Represents a page in the collection, which can be created from a file or be virtual. |
||
26 | * Provides methods to manage page properties, variables, and rendering. |
||
27 | */ |
||
28 | class Page extends Item |
||
29 | { |
||
30 | public const SLUGIFY_PATTERN = '/(^\/|[^._a-z0-9\/]|-)+/'; // should be '/^\/|[^_a-z0-9\/]+/' |
||
31 | |||
32 | /** @var bool True if page is not created from a file. */ |
||
33 | protected $virtual; |
||
34 | |||
35 | /** @var SplFileInfo */ |
||
36 | protected $file; |
||
37 | |||
38 | /** @var Type Type */ |
||
39 | protected $type; |
||
40 | |||
41 | /** @var string */ |
||
42 | protected $folder; |
||
43 | |||
44 | /** @var string */ |
||
45 | protected $slug; |
||
46 | |||
47 | /** @var string path = folder + slug. */ |
||
48 | protected $path; |
||
49 | |||
50 | /** @var string */ |
||
51 | protected $section; |
||
52 | |||
53 | /** @var string */ |
||
54 | protected $frontmatter; |
||
55 | |||
56 | /** @var array Front matter before conversion. */ |
||
57 | protected $fmVariables = []; |
||
58 | |||
59 | /** @var string Body before conversion. */ |
||
60 | protected $body; |
||
61 | |||
62 | /** @var string Body after conversion. */ |
||
63 | protected $html; |
||
64 | |||
65 | /** @var array Output, by format. */ |
||
66 | protected $rendered = []; |
||
67 | |||
68 | /** @var Collection pages list. */ |
||
69 | protected $pages; |
||
70 | |||
71 | /** @var array */ |
||
72 | protected $paginator = []; |
||
73 | |||
74 | /** @var \Cecil\Collection\Taxonomy\Vocabulary Terms of a vocabulary. */ |
||
75 | protected $terms; |
||
76 | |||
77 | /** @var Slugify */ |
||
78 | private static $slugifier; |
||
79 | |||
80 | public function __construct(mixed $id) |
||
81 | { |
||
82 | if (!\is_string($id) && !$id instanceof SplFileInfo) { |
||
83 | throw new RuntimeException('Create a page with a string ID or a SplFileInfo.'); |
||
84 | } |
||
85 | |||
86 | // default properties |
||
87 | $this->setVirtual(true); |
||
88 | $this->setType(Type::PAGE->value); |
||
89 | $this->setVariables([ |
||
90 | 'title' => 'Page Title', |
||
91 | 'date' => new \DateTime(), |
||
92 | 'updated' => new \DateTime(), |
||
93 | 'weight' => null, |
||
94 | 'filepath' => null, |
||
95 | 'published' => true, |
||
96 | 'content_template' => 'page.content.twig', |
||
97 | ]); |
||
98 | |||
99 | if ($id instanceof SplFileInfo) { |
||
100 | $file = $id; |
||
101 | $this->setFile($file); |
||
102 | $id = self::createIdFromFile($file); |
||
103 | } |
||
104 | |||
105 | parent::__construct($id); |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * {@inheritdoc} |
||
110 | */ |
||
111 | public function setId(string $id): self |
||
112 | { |
||
113 | return parent::setId($id); |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * toString magic method to prevent Twig get_attribute fatal error. |
||
118 | * |
||
119 | * @return string |
||
120 | */ |
||
121 | public function __toString() |
||
122 | { |
||
123 | return $this->getId(); |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Turns a path (string) into a slug (URI). |
||
128 | */ |
||
129 | public static function slugify(string $path): string |
||
130 | { |
||
131 | if (!self::$slugifier instanceof Slugify) { |
||
132 | self::$slugifier = Slugify::create(['regexp' => self::SLUGIFY_PATTERN]); |
||
133 | } |
||
134 | |||
135 | return self::$slugifier->slugify($path); |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Returns the ID of a page without language. |
||
140 | */ |
||
141 | public function getIdWithoutLang(): string |
||
142 | { |
||
143 | $langPrefix = $this->getVariable('language') . '/'; |
||
144 | if ($this->hasVariable('language') && Util\Str::startsWith($this->getId(), $langPrefix)) { |
||
145 | return substr($this->getId(), \strlen($langPrefix)); |
||
146 | } |
||
147 | |||
148 | return $this->getId(); |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Set file. |
||
153 | */ |
||
154 | public function setFile(SplFileInfo $file): self |
||
155 | { |
||
156 | $this->file = $file; |
||
157 | $this->setVirtual(false); |
||
158 | |||
159 | /* |
||
160 | * File path components |
||
161 | */ |
||
162 | $fileRelativePath = str_replace(DIRECTORY_SEPARATOR, '/', $this->file->getRelativePath()); |
||
163 | $fileExtension = $this->file->getExtension(); |
||
164 | $fileName = $this->file->getBasename('.' . $fileExtension); |
||
165 | // renames "README" to "index" |
||
166 | $fileName = strtolower($fileName) == 'readme' ? 'index' : $fileName; |
||
167 | // case of "index" = home page |
||
168 | if (empty($this->file->getRelativePath()) && PrefixSuffix::sub($fileName) == 'index') { |
||
169 | $this->setType(Type::HOMEPAGE->value); |
||
170 | } |
||
171 | /* |
||
172 | * Set page properties and variables |
||
173 | */ |
||
174 | $this->setFolder($fileRelativePath); |
||
175 | $this->setSlug($fileName); |
||
176 | $this->setPath($this->getFolder() . '/' . $this->getSlug()); |
||
177 | $this->setVariables([ |
||
178 | 'title' => PrefixSuffix::sub($fileName), |
||
179 | 'date' => (new \DateTime())->setTimestamp($this->file->getMTime()), |
||
180 | 'updated' => (new \DateTime())->setTimestamp($this->file->getMTime()), |
||
181 | 'filepath' => $this->file->getRelativePathname(), |
||
182 | ]); |
||
183 | /* |
||
184 | * Set specific variables |
||
185 | */ |
||
186 | // is file has a prefix? |
||
187 | if (PrefixSuffix::hasPrefix($fileName)) { |
||
188 | $prefix = PrefixSuffix::getPrefix($fileName); |
||
189 | if ($prefix !== null) { |
||
190 | // prefix is an integer: used for sorting |
||
191 | if (is_numeric($prefix)) { |
||
192 | $this->setVariable('weight', (int) $prefix); |
||
193 | } |
||
194 | // prefix is a valid date? |
||
195 | if (Util\Date::isValid($prefix)) { |
||
196 | $this->setVariable('date', (string) $prefix); |
||
197 | } |
||
198 | } |
||
199 | } |
||
200 | // is file has a language suffix? |
||
201 | if (PrefixSuffix::hasSuffix($fileName)) { |
||
202 | $this->setVariable('language', PrefixSuffix::getSuffix($fileName)); |
||
203 | } |
||
204 | // set reference between page's translations, even if it exist in only one language |
||
205 | $this->setVariable('langref', $this->getPath()); |
||
206 | |||
207 | return $this; |
||
208 | } |
||
209 | |||
210 | /** |
||
211 | * Returns file name, with extension. |
||
212 | */ |
||
213 | public function getFileName(): ?string |
||
214 | { |
||
215 | if ($this->file === null) { |
||
216 | return null; |
||
217 | } |
||
218 | |||
219 | return $this->file->getBasename(); |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Returns file real path. |
||
224 | */ |
||
225 | public function getFilePath(): ?string |
||
226 | { |
||
227 | if ($this->file === null) { |
||
228 | return null; |
||
229 | } |
||
230 | |||
231 | return $this->file->getRealPath() === false ? null : $this->file->getRealPath(); |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * Parse file content. |
||
236 | */ |
||
237 | public function parse(): self |
||
238 | { |
||
239 | $parser = new Parser($this->file); |
||
240 | $parsed = $parser->parse(); |
||
241 | $this->frontmatter = $parsed->getFrontmatter(); |
||
242 | $this->body = $parsed->getBody(); |
||
243 | |||
244 | return $this; |
||
245 | } |
||
246 | |||
247 | /** |
||
248 | * Get front matter. |
||
249 | */ |
||
250 | public function getFrontmatter(): ?string |
||
251 | { |
||
252 | return $this->frontmatter; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * Get body as raw. |
||
257 | */ |
||
258 | public function getBody(): ?string |
||
259 | { |
||
260 | return $this->body; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Set virtual status. |
||
265 | */ |
||
266 | public function setVirtual(bool $virtual): self |
||
267 | { |
||
268 | $this->virtual = $virtual; |
||
269 | |||
270 | return $this; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Is current page is virtual? |
||
275 | */ |
||
276 | public function isVirtual(): bool |
||
277 | { |
||
278 | return $this->virtual; |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * Set page type. |
||
283 | */ |
||
284 | public function setType(string $type): self |
||
285 | { |
||
286 | $this->type = Type::from($type); |
||
287 | |||
288 | return $this; |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Get page type. |
||
293 | */ |
||
294 | public function getType(): string |
||
295 | { |
||
296 | return $this->type->value; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Set path without slug. |
||
301 | */ |
||
302 | public function setFolder(string $folder): self |
||
303 | { |
||
304 | $this->folder = self::slugify($folder); |
||
305 | |||
306 | return $this; |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Get path without slug. |
||
311 | */ |
||
312 | public function getFolder(): ?string |
||
313 | { |
||
314 | return $this->folder; |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * Set slug. |
||
319 | */ |
||
320 | public function setSlug(string $slug): self |
||
321 | { |
||
322 | if (!$this->slug) { |
||
323 | $slug = self::slugify(PrefixSuffix::sub($slug)); |
||
324 | } |
||
325 | // force slug and update path |
||
326 | if ($this->slug && $this->slug != $slug) { |
||
327 | $this->setPath($this->getFolder() . '/' . $slug); |
||
328 | } |
||
329 | $this->slug = $slug; |
||
330 | |||
331 | return $this; |
||
332 | } |
||
333 | |||
334 | /** |
||
335 | * Get slug. |
||
336 | */ |
||
337 | public function getSlug(): string |
||
338 | { |
||
339 | return $this->slug; |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Set path. |
||
344 | */ |
||
345 | public function setPath(string $path): self |
||
346 | { |
||
347 | $path = trim($path, '/'); |
||
348 | |||
349 | // case of homepage |
||
350 | if ($path == 'index') { |
||
351 | $this->path = ''; |
||
352 | |||
353 | return $this; |
||
354 | } |
||
355 | |||
356 | // case of custom sections' index (ie: section/index.md -> section) |
||
357 | if (substr($path, -6) == '/index') { |
||
358 | $path = substr($path, 0, \strlen($path) - 6); |
||
359 | } |
||
360 | $this->path = $path; |
||
361 | |||
362 | $lastslash = strrpos($this->path, '/'); |
||
363 | |||
364 | // case of root/top-level pages |
||
365 | if ($lastslash === false) { |
||
366 | $this->slug = $this->path; |
||
367 | |||
368 | return $this; |
||
369 | } |
||
370 | |||
371 | // case of sections' pages: set section |
||
372 | if (!$this->virtual && $this->getSection() === null) { |
||
373 | $this->section = explode('/', $this->path)[0]; |
||
374 | } |
||
375 | // set/update folder and slug |
||
376 | $this->folder = substr($this->path, 0, $lastslash); |
||
377 | $this->slug = substr($this->path, -(\strlen($this->path) - $lastslash - 1)); |
||
378 | |||
379 | return $this; |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * Get path. |
||
384 | */ |
||
385 | public function getPath(): ?string |
||
386 | { |
||
387 | return $this->path; |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * @see getPath() |
||
392 | */ |
||
393 | public function getPathname(): ?string |
||
394 | { |
||
395 | return $this->getPath(); |
||
396 | } |
||
397 | |||
398 | /** |
||
399 | * Set section. |
||
400 | */ |
||
401 | public function setSection(string $section): self |
||
402 | { |
||
403 | $this->section = $section; |
||
404 | |||
405 | return $this; |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * Get section. |
||
410 | */ |
||
411 | public function getSection(): ?string |
||
412 | { |
||
413 | return !empty($this->section) ? $this->section : null; |
||
414 | } |
||
415 | |||
416 | /** |
||
417 | * Unset section. |
||
418 | */ |
||
419 | public function unSection(): self |
||
420 | { |
||
421 | $this->section = null; |
||
422 | |||
423 | return $this; |
||
424 | } |
||
425 | |||
426 | /** |
||
427 | * Set body as HTML. |
||
428 | */ |
||
429 | public function setBodyHtml(string $html): self |
||
430 | { |
||
431 | $this->html = $html; |
||
432 | |||
433 | return $this; |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Get body as HTML. |
||
438 | */ |
||
439 | public function getBodyHtml(): ?string |
||
440 | { |
||
441 | return $this->html; |
||
442 | } |
||
443 | |||
444 | /** |
||
445 | * @see getBodyHtml() |
||
446 | */ |
||
447 | public function getContent(): ?string |
||
448 | { |
||
449 | return $this->getBodyHtml(); |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * Add rendered. |
||
454 | */ |
||
455 | public function addRendered(array $rendered): self |
||
456 | { |
||
457 | $this->rendered += $rendered; |
||
458 | |||
459 | return $this; |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * Get rendered. |
||
464 | */ |
||
465 | public function getRendered(): array |
||
466 | { |
||
467 | return $this->rendered; |
||
468 | } |
||
469 | |||
470 | /** |
||
471 | * Set pages list. |
||
472 | */ |
||
473 | public function setPages(Collection $pages): self |
||
474 | { |
||
475 | $this->pages = $pages; |
||
476 | |||
477 | return $this; |
||
478 | } |
||
479 | |||
480 | /** |
||
481 | * Get pages list. |
||
482 | */ |
||
483 | public function getPages(): ?Collection |
||
484 | { |
||
485 | return $this->pages; |
||
486 | } |
||
487 | |||
488 | /** |
||
489 | * Set paginator. |
||
490 | */ |
||
491 | public function setPaginator(array $paginator): self |
||
492 | { |
||
493 | $this->paginator = $paginator; |
||
494 | |||
495 | return $this; |
||
496 | } |
||
497 | |||
498 | /** |
||
499 | * Get paginator. |
||
500 | */ |
||
501 | public function getPaginator(): array |
||
502 | { |
||
503 | return $this->paginator; |
||
504 | } |
||
505 | |||
506 | /** |
||
507 | * Paginator backward compatibility. |
||
508 | */ |
||
509 | public function getPagination(): array |
||
510 | { |
||
511 | return $this->getPaginator(); |
||
512 | } |
||
513 | |||
514 | /** |
||
515 | * Set vocabulary terms. |
||
516 | */ |
||
517 | public function setTerms(\Cecil\Collection\Taxonomy\Vocabulary $terms): self |
||
518 | { |
||
519 | $this->terms = $terms; |
||
520 | |||
521 | return $this; |
||
522 | } |
||
523 | |||
524 | /** |
||
525 | * Get vocabulary terms. |
||
526 | */ |
||
527 | public function getTerms(): \Cecil\Collection\Taxonomy\Vocabulary |
||
528 | { |
||
529 | return $this->terms; |
||
530 | } |
||
531 | |||
532 | /* |
||
533 | * Helpers to set and get variables. |
||
534 | */ |
||
535 | |||
536 | /** |
||
537 | * Set an array as variables. |
||
538 | * |
||
539 | * @throws RuntimeException |
||
540 | */ |
||
541 | public function setVariables(array $variables): self |
||
542 | { |
||
543 | foreach ($variables as $key => $value) { |
||
544 | $this->setVariable($key, $value); |
||
545 | } |
||
546 | |||
547 | return $this; |
||
548 | } |
||
549 | |||
550 | /** |
||
551 | * Get all variables. |
||
552 | */ |
||
553 | public function getVariables(): array |
||
554 | { |
||
555 | return $this->properties; |
||
556 | } |
||
557 | |||
558 | /** |
||
559 | * Set a variable. |
||
560 | * |
||
561 | * @param string $name Name of the variable |
||
562 | * @param mixed $value Value of the variable |
||
563 | * |
||
564 | * @throws RuntimeException |
||
565 | */ |
||
566 | public function setVariable(string $name, $value): self |
||
567 | { |
||
568 | $this->filterBool($value); |
||
569 | switch ($name) { |
||
570 | case 'date': |
||
571 | case 'updated': |
||
572 | case 'lastmod': |
||
573 | try { |
||
574 | $date = Util\Date::toDatetime($value); |
||
575 | } catch (\Exception) { |
||
576 | throw new \Exception(\sprintf('The value of "%s" is not a valid date: "%s".', $name, var_export($value, true))); |
||
577 | } |
||
578 | $this->offsetSet($name == 'lastmod' ? 'updated' : $name, $date); |
||
579 | break; |
||
580 | |||
581 | case 'schedule': |
||
582 | /* |
||
583 | * publish: 2012-10-08 |
||
584 | * expiry: 2012-10-09 |
||
585 | */ |
||
586 | $this->offsetSet('published', false); |
||
587 | if (\is_array($value)) { |
||
588 | if (\array_key_exists('publish', $value) && Util\Date::toDatetime($value['publish']) <= Util\Date::toDatetime('now')) { |
||
589 | $this->offsetSet('published', true); |
||
590 | } |
||
591 | if (\array_key_exists('expiry', $value) && Util\Date::toDatetime($value['expiry']) >= Util\Date::toDatetime('now')) { |
||
592 | $this->offsetSet('published', true); |
||
593 | } |
||
594 | } |
||
595 | break; |
||
596 | case 'draft': |
||
597 | // draft: true = published: false |
||
598 | if ($value === true) { |
||
599 | $this->offsetSet('published', false); |
||
600 | } |
||
601 | break; |
||
602 | case 'path': |
||
603 | case 'slug': |
||
604 | $slugify = self::slugify((string) $value); |
||
605 | if ($value != $slugify) { |
||
606 | throw new RuntimeException(\sprintf('"%s" variable should be "%s" (not "%s") in "%s".', $name, $slugify, (string) $value, $this->getId())); |
||
607 | } |
||
608 | $method = 'set' . ucfirst($name); |
||
609 | $this->$method($value); |
||
610 | break; |
||
611 | default: |
||
612 | $this->offsetSet($name, $value); |
||
613 | } |
||
614 | |||
615 | return $this; |
||
616 | } |
||
617 | |||
618 | /** |
||
619 | * Is variable exists? |
||
620 | * |
||
621 | * @param string $name Name of the variable |
||
622 | */ |
||
623 | public function hasVariable(string $name): bool |
||
624 | { |
||
625 | return $this->offsetExists($name); |
||
626 | } |
||
627 | |||
628 | /** |
||
629 | * Get a variable. |
||
630 | * |
||
631 | * @param string $name Name of the variable |
||
632 | * @param mixed|null $default Default value |
||
633 | * |
||
634 | * @return mixed|null |
||
635 | */ |
||
636 | public function getVariable(string $name, $default = null) |
||
637 | { |
||
638 | if ($this->offsetExists($name)) { |
||
639 | return $this->offsetGet($name); |
||
640 | } |
||
641 | |||
642 | return $default; |
||
643 | } |
||
644 | |||
645 | /** |
||
646 | * Unset a variable. |
||
647 | * |
||
648 | * @param string $name Name of the variable |
||
649 | */ |
||
650 | public function unVariable(string $name): self |
||
651 | { |
||
652 | if ($this->offsetExists($name)) { |
||
653 | $this->offsetUnset($name); |
||
654 | } |
||
655 | |||
656 | return $this; |
||
657 | } |
||
658 | |||
659 | /** |
||
660 | * Magic method to handle variables. |
||
661 | * |
||
662 | * @param string $method Method name |
||
663 | * @param array $arguments Method arguments |
||
664 | * |
||
665 | * @return mixed |
||
666 | */ |
||
667 | public function __call($method, $arguments) |
||
668 | { |
||
669 | if (method_exists($this, $method)) { |
||
670 | return $method($arguments); |
||
671 | } |
||
672 | |||
673 | if (str_starts_with($method, 'has') && \count($arguments) == 0) { |
||
674 | $variable = strtolower(substr($method, 3)); |
||
675 | return $this->hasVariable($variable); |
||
676 | } |
||
677 | if (str_starts_with($method, 'get') && \count($arguments) == 0) { |
||
678 | $variable = strtolower(substr($method, 3)); |
||
679 | if ($this->hasVariable($variable)) { |
||
680 | return $this->getVariable($variable); |
||
681 | } |
||
682 | } |
||
683 | if (str_starts_with($method, 'set') && \count($arguments) == 1) { |
||
684 | $variable = strtolower(substr($method, 3)); |
||
685 | return $this->setVariable($variable, $arguments[0]); |
||
686 | } |
||
687 | if (str_starts_with($method, 'un') && \count($arguments) == 0) { |
||
688 | $variable = strtolower(substr($method, 2)); |
||
689 | return $this->unVariable($variable); |
||
690 | } |
||
691 | |||
692 | trigger_error('Call to undefined method ' . __CLASS__ . '::' . $method . '()', E_USER_ERROR); |
||
693 | } |
||
694 | |||
695 | /** |
||
696 | * Magic method to handle isset. |
||
697 | * |
||
698 | * @param string $name Name of the variable |
||
699 | * |
||
700 | * @return bool |
||
701 | */ |
||
702 | public function __isset($name) |
||
703 | { |
||
704 | if ($this->hasVariable($name)) { |
||
705 | return true; |
||
706 | } |
||
707 | |||
708 | return false; |
||
709 | } |
||
710 | |||
711 | public function __get($name) |
||
712 | { |
||
713 | throw new RuntimeException(\sprintf('Call to undefined property %s::$%s', __CLASS__, $name)); |
||
714 | |||
715 | $this->getVariable($name); |
||
0 ignored issues
–
show
|
|||
716 | } |
||
717 | |||
718 | /** |
||
719 | * Set front matter (only) variables. |
||
720 | */ |
||
721 | public function setFmVariables(array $variables): self |
||
722 | { |
||
723 | $this->fmVariables = $variables; |
||
724 | |||
725 | return $this; |
||
726 | } |
||
727 | |||
728 | /** |
||
729 | * Get front matter variables. |
||
730 | */ |
||
731 | public function getFmVariables(): array |
||
732 | { |
||
733 | return $this->fmVariables; |
||
734 | } |
||
735 | |||
736 | /** |
||
737 | * Creates a page ID from a file (based on path). |
||
738 | */ |
||
739 | private static function createIdFromFile(SplFileInfo $file): string |
||
740 | { |
||
741 | $relativePath = self::slugify(str_replace(DIRECTORY_SEPARATOR, '/', $file->getRelativePath())); |
||
742 | $basename = self::slugify(PrefixSuffix::subPrefix($file->getBasename('.' . $file->getExtension()))); |
||
743 | // if file is "README.md", ID is "index" |
||
744 | $basename = strtolower($basename) == 'readme' ? 'index' : $basename; |
||
745 | // if file is section's index: "section/index.md", ID is "section" |
||
746 | if (!empty($relativePath) && PrefixSuffix::sub($basename) == 'index') { |
||
747 | // case of a localized section's index: "section/index.fr.md", ID is "fr/section" |
||
748 | if (PrefixSuffix::hasSuffix($basename)) { |
||
749 | return PrefixSuffix::getSuffix($basename) . '/' . $relativePath; |
||
750 | } |
||
751 | |||
752 | return $relativePath; |
||
753 | } |
||
754 | // localized page |
||
755 | if (PrefixSuffix::hasSuffix($basename)) { |
||
756 | return trim(Util::joinPath(/** @scrutinizer ignore-type */ PrefixSuffix::getSuffix($basename), $relativePath, PrefixSuffix::sub($basename)), '/'); |
||
757 | } |
||
758 | |||
759 | return trim(Util::joinPath($relativePath, $basename), '/'); |
||
760 | } |
||
761 | |||
762 | /** |
||
763 | * Cast "boolean" string (or array of strings) to boolean. |
||
764 | * |
||
765 | * @param mixed $value Value to filter |
||
766 | * |
||
767 | * @return bool|mixed |
||
768 | * |
||
769 | * @see strToBool() |
||
770 | */ |
||
771 | private function filterBool(&$value) |
||
772 | { |
||
773 | \Cecil\Util\Str::strToBool($value); |
||
774 | if (\is_array($value)) { |
||
775 | array_walk_recursive($value, '\Cecil\Util\Str::strToBool'); |
||
776 | } |
||
777 | } |
||
778 | } |
||
779 |
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.