Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
SitemapGenerator
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
8 / 8
17
100.00% covered (success)
100.00%
1 / 1
 generate
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getXml
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 constructBaseElement
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 addRoute
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 getLastModDate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getPriority
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
6
 getFormattedProcessingTime
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 resolveRouteLink
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3/** @noinspection PhpComposerExtensionStubsInspection */
4
5declare(strict_types=1);
6
7namespace Hyde\Framework\Features\XmlGenerators;
8
9use Hyde\Hyde;
10use SimpleXMLElement;
11use Hyde\Facades\Config;
12use Hyde\Pages\BladePage;
13use Hyde\Pages\MarkdownPage;
14use Hyde\Pages\MarkdownPost;
15use Hyde\Support\Models\Route;
16use Hyde\Pages\DocumentationPage;
17use Hyde\Foundation\Facades\Routes;
18use Hyde\Framework\Concerns\TracksExecutionTime;
19
20use function blank;
21use function filemtime;
22use function in_array;
23use function date;
24use function time;
25use function str_starts_with;
26
27/**
28 * @see https://www.sitemaps.org/protocol.html
29 */
30class SitemapGenerator extends BaseXmlGenerator
31{
32    use TracksExecutionTime;
33
34    public function generate(): static
35    {
36        Routes::all()->each(function (Route $route): void {
37            $this->addRoute($route);
38        });
39
40        return $this;
41    }
42
43    public function getXml(): string
44    {
45        $this->xmlElement->addAttribute('processing_time_ms', $this->getFormattedProcessingTime());
46
47        return parent::getXml();
48    }
49
50    protected function constructBaseElement(): void
51    {
52        $this->startClock();
53
54        $this->xmlElement = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"></urlset>');
55        $this->xmlElement->addAttribute('generator', 'HydePHP v'.Hyde::version());
56    }
57
58    protected function addRoute(Route $route): void
59    {
60        $urlItem = $this->xmlElement->addChild('url');
61
62        $this->addChild($urlItem, 'loc', $this->resolveRouteLink($route));
63        $this->addChild($urlItem, 'lastmod', $this->getLastModDate($route->getSourcePath()));
64        $this->addChild($urlItem, 'changefreq', 'daily');
65
66        if (Config::getBool('hyde.sitemap.dynamic_priority', true)) {
67            $this->addChild($urlItem, 'priority', $this->getPriority(
68                $route->getPageClass(), $route->getPage()->getIdentifier()
69            ));
70        }
71    }
72
73    protected function getLastModDate(string $file): string
74    {
75        return date('c', @filemtime($file) ?: time());
76    }
77
78    protected function getPriority(string $pageClass, string $slug): string
79    {
80        $priority = 0.5;
81
82        if (in_array($pageClass, [BladePage::class, MarkdownPage::class])) {
83            $priority = 0.9;
84            if ($slug === 'index') {
85                $priority = 1;
86            }
87            if ($slug === '404') {
88                $priority = 0.5;
89            }
90        }
91
92        if ($pageClass === DocumentationPage::class) {
93            $priority = 0.9;
94        }
95
96        if ($pageClass === MarkdownPost::class) {
97            $priority = 0.75;
98        }
99
100        return (string) $priority;
101    }
102
103    /** @return numeric-string */
104    protected function getFormattedProcessingTime(): string
105    {
106        return (string) $this->getExecutionTimeInMs();
107    }
108
109    protected function resolveRouteLink(Route $route): string
110    {
111        $baseUrl = Config::getNullableString('hyde.url');
112
113        if (blank($baseUrl) || str_starts_with($baseUrl, 'http://localhost')) {
114            // While the sitemap spec requires a full URL, we rather fall back
115            // to using relative links instead of using localhost links.
116
117            return $route->getLink();
118        } else {
119            return Hyde::url($route->getOutputPath());
120        }
121    }
122}