2121
2222use phpMyFAQ \Core \Exception ;
2323use phpMyFAQ \Twig \TemplateException ;
24+ use Symfony \Component \HttpFoundation \RedirectResponse ;
2425use Symfony \Component \HttpFoundation \Response ;
2526use Symfony \Component \Routing \Attribute \Route ;
2627
2728final class SitemapController extends AbstractController
2829{
2930 private const int PMF_SITEMAP_GOOGLE_MAX_URLS = 50000 ;
3031
32+ /**
33+ * Returns gzipped sitemap.xml or redirects to plain XML if zlib is not available
34+ *
35+ * @throws TemplateException|Exception|\Exception
36+ */
37+ #[Route(path: '/sitemap.gz ' , name: 'public.sitemap.gz ' )]
38+ public function sitemapGz (): Response
39+ {
40+ return $ this ->generateGzippedSitemap ();
41+ }
42+
43+ /**
44+ * Returns gzipped sitemap.xml or redirects to plain XML if zlib is not available
45+ *
46+ * @throws TemplateException|Exception|\Exception
47+ */
48+ #[Route(path: '/sitemap.xml.gz ' , name: 'public.sitemap.xml.gz ' )]
49+ public function sitemapXmlGz (): Response
50+ {
51+ return $ this ->generateGzippedSitemap ();
52+ }
53+
3154 /**
3255 * @throws TemplateException|Exception|\Exception
3356 */
@@ -36,15 +59,35 @@ public function index(): Response
3659 {
3760 $ response = new Response ();
3861
39- $ siteMapEnabled = $ this ->configuration ->get (item: 'seo.enableXMLSitemap ' );
40- if (!$ siteMapEnabled ) {
62+ $ xml = $ this ->generateSitemapXml ();
63+
64+ if ($ xml === null ) {
4165 $ response ->setStatusCode (Response::HTTP_NOT_FOUND );
4266 $ response ->setContent ('XML Sitemap is disabled. ' );
4367 return $ response ;
4468 }
4569
46- $ faqStatistics = $ this ->container ->get (id: 'phpmyfaq.faq.statistics ' );
70+ $ response ->headers ->set ('Content-Type ' , 'text/xml ' );
71+ $ response ->setStatusCode (Response::HTTP_OK );
72+ $ response ->setContent ($ xml );
4773
74+ return $ response ;
75+ }
76+
77+ /**
78+ * Generates the sitemap XML content
79+ *
80+ * @throws TemplateException|Exception|\Exception
81+ * @return string|null Returns XML content or null if sitemap is disabled
82+ */
83+ private function generateSitemapXml (): ?string
84+ {
85+ $ siteMapEnabled = $ this ->configuration ->get (item: 'seo.enableXMLSitemap ' );
86+ if (!$ siteMapEnabled ) {
87+ return null ;
88+ }
89+
90+ $ faqStatistics = $ this ->container ->get (id: 'phpmyfaq.faq.statistics ' );
4891 $ items = $ faqStatistics ->getTopTenData (self ::PMF_SITEMAP_GOOGLE_MAX_URLS - 1 );
4992
5093 $ urls = [];
@@ -56,11 +99,37 @@ public function index(): Response
5699 ];
57100 }
58101
59- $ xml = $ this ->renderView (pathToTwigFile: './sitemap.xml.twig ' , templateVars: ['urls ' => $ urls ]);
102+ return $ this ->renderView (pathToTwigFile: './sitemap.xml.twig ' , templateVars: ['urls ' => $ urls ]);
103+ }
104+
105+ /**
106+ * Generates a gzipped sitemap response or redirects if zlib is unavailable
107+ *
108+ * @throws TemplateException|Exception|\Exception
109+ */
110+ private function generateGzippedSitemap (): Response
111+ {
112+ // Fallback to plain XML if zlib extension is not available
113+ if (!extension_loaded ('zlib ' )) {
114+ return new RedirectResponse ('/sitemap.xml ' , Response::HTTP_MOVED_PERMANENTLY );
115+ }
116+
117+ $ xml = $ this ->generateSitemapXml ();
60118
61- $ response ->headers ->set (key: 'Content-Type ' , values: 'text/xml ' );
119+ if ($ xml === null ) {
120+ $ response = new Response ();
121+ $ response ->setStatusCode (Response::HTTP_NOT_FOUND );
122+ $ response ->setContent ('XML Sitemap is disabled. ' );
123+ return $ response ;
124+ }
125+
126+ $ gzipped = gzencode ($ xml , 9 );
127+
128+ $ response = new Response ($ gzipped );
129+ $ response ->headers ->set ('Content-Type ' , 'application/x-gzip ' );
130+ $ response ->headers ->set ('Content-Encoding ' , 'gzip ' );
131+ $ response ->headers ->set ('Content-Disposition ' , 'attachment; filename="sitemap.xml.gz" ' );
62132 $ response ->setStatusCode (Response::HTTP_OK );
63- $ response ->setContent ($ xml );
64133
65134 return $ response ;
66135 }
0 commit comments