8 private static bool $dirsReady =
false;
9 private const FULL_PAGE_CACHE_VERSION =
'v3';
10 private const FULL_PAGE_GENERATION_FILE =
'.generation';
15 $configFile = rtrim((
string)
dbx()->
get_base_dir(),
'/\\') .
'/dbx/modules/dbx/cfg/config.php';
16 }
catch (\Throwable $e) {
17 $configFile = dirname(__DIR__, 2) .
'/dbx/cfg/config.php';
19 if (is_file($configFile) && is_readable($configFile)) {
20 $readConfig =
static function(
string $path): array {
25 $fileConfig = $readConfig($configFile);
26 if (array_key_exists(
'cache_content', $fileConfig)) {
27 return (
int)$fileConfig[
'cache_content'] === 1;
31 $enabled =
dbx()->get_config(
'dbx',
'cache_content');
32 if ($enabled ===
'undef' || $enabled ===
'' || $enabled ===
null) {
36 return (
int) $enabled === 1;
45 $config[
'cache_content'] = $enabled ? 1 : 0;
58 if (!self::isGuestSession() || self::hasPersonalizedGuestState()) {
62 $method = strtoupper(trim((
string)(
$_SERVER[
'REQUEST_METHOD'] ??
'GET')));
67 if ((
int)
dbx()->get_request_var(
'dbx_sync', 1,
'int') !== 1) {
71 if ((
int)
dbx()->get_system_var(
'dbx_edit', 0,
'int') > 0) {
75 if ((
int)
dbx()->get_system_var(
'dbx_ajax', 0,
'int')) {
79 if ((
int)
dbx()->get_system_var(
'dbx_window', 0,
'int')) {
92 private static function isGuestSession(): bool {
97 }
catch (\Throwable $e) {
98 return (
int)(
$_SESSION[
'dbx'][
'current_user'][
'id'] ?? 0) <= 0;
103 private static function hasPersonalizedGuestState(): bool {
105 if (!is_array(
$cart)) {
109 foreach (
$cart as $quantity) {
110 if ((
int)$quantity > 0) {
119 if (
dbx()->get_system_var(
'dbx_modul') !==
'dbxContent') {
123 $action = (string)
dbx()->get_system_var(
'dbx_run1',
'');
124 return $action ===
'show';
132 if ((
int)
dbx()->get_system_var(
'dbx_content_permalink_request', 0,
'int') !== 1) {
136 return (
int)
dbx()->get_system_var(
'dbx_content_route_cid', 0,
'int') > 0;
140 private static function isRawPermalinkRequest(): bool {
142 if (!self::isReadEnabled()) {
146 if (trim((
string)
dbx()->get_request_var(
'dbx_modul',
'',
'parameter')) !==
''
147 || trim((
string)
dbx()->get_request_var(
'dbx_run1',
'',
'parameter')) !==
''
148 || (
int)
dbx()->get_request_var(
'cid', 0,
'int') > 0
149 || (
int)
dbx()->get_request_var(
'dbx_cid', 0,
'int') > 0) {
153 $permalink = self::currentPermalink();
154 if ($permalink ===
'' || in_array($permalink, array(
'admin',
'sitemap',
'sitemap.xml',
'robots.txt'),
true)) {
160 foreach (array_keys(
$_GET) as $name) {
161 if (!in_array((
string)$name, array(
'dbx_lng',
'dbx_design',
'dbx_color'),
true)) {
175 dbx()->set_system_var(
'dbx_full_page_cache_prepared', 0);
176 dbx()->set_system_var(
'dbx_full_page_cache_path',
'');
177 dbx()->set_system_var(
'dbx_full_page_cache_file',
'');
178 dbx()->set_system_var(
'dbx_full_page_cache_generation',
'');
179 dbx()->set_system_var(
'dbx_full_page_cache_cid', 0);
180 dbx()->set_system_var(
'dbx_full_page_cache_lng',
'');
182 if (!self::isRawPermalinkRequest()) {
188 $rawPermalink = self::normalizePermalink((
string)
dbx()->get_system_var(
'dbx_permalink',
''));
189 if ($rawPermalink ===
'') {
190 dbx()->set_system_var(
'dbx_permalink',
'home');
191 dbx()->set_system_var(
'dbx_self_url',
'home');
194 $permalink = self::currentPermalink();
195 $lng = self::safeToken(self::currentLng(),
'de');
196 $design = self::currentDesign();
197 $skin =
dbx()->normalize_skin((
string)
dbx()->get_system_var(
'dbx_color',
'blau'));
198 $generation = self::cacheGeneration();
199 if ($generation ===
'') {
202 $path = self::fullPagePathForGeneration($permalink,
$lng,
$design, $skin, $generation);
204 dbx()->set_system_var(
'dbx_full_page_cache_prepared', 1);
205 dbx()->set_system_var(
'dbx_full_page_cache_path', $path);
206 dbx()->set_system_var(
'dbx_full_page_cache_file', basename($path));
207 dbx()->set_system_var(
'dbx_full_page_cache_permalink', $permalink);
208 dbx()->set_system_var(
'dbx_full_page_cache_design',
$design);
209 dbx()->set_system_var(
'dbx_full_page_cache_lng',
$lng);
210 dbx()->set_system_var(
'dbx_full_page_cache_generation', $generation);
225 $preparedLng = (string)
dbx()->get_system_var(
'dbx_full_page_cache_lng',
'');
226 $currentLng = self::safeToken(self::currentLng(),
'de');
227 if ($preparedLng !== $currentLng && !self::prepareFullPageRequest()) {
234 $preparedPermalink = (string)
dbx()->get_system_var(
'dbx_full_page_cache_permalink',
'');
235 if ((
int)
dbx()->get_system_var(
'dbx_content_not_found', 0,
'int') === 1
236 || $preparedPermalink !== self::currentPermalink()) {
237 dbx()->set_system_var(
'dbx_full_page_cache_prepared', 0);
238 dbx()->set_system_var(
'dbx_full_page_cache_path',
'');
239 dbx()->set_system_var(
'dbx_full_page_cache_file',
'');
243 $cid = (int)
dbx()->get_system_var(
'dbx_content_route_cid', 0,
'int');
244 if ((
int)
dbx()->get_system_var(
'dbx_content_permalink_request', 0,
'int') !== 1 || $cid <= 0) {
245 dbx()->set_system_var(
'dbx_full_page_cache_prepared', 0);
246 dbx()->set_system_var(
'dbx_full_page_cache_path',
'');
247 dbx()->set_system_var(
'dbx_full_page_cache_file',
'');
251 dbx()->set_system_var(
'dbx_full_page_cache_cid', $cid);
256 return (int)
dbx()->get_system_var(
'dbx_full_page_cache_prepared', 0,
'int') === 1
257 && self::isGuestSession()
258 && !self::hasPersonalizedGuestState()
259 && self::currentPermalink() !==
''
260 && trim((string)
dbx()->get_system_var(
'dbx_full_page_cache_path',
'')) !==
'';
269 $preparedGeneration = (string)
dbx()->get_system_var(
'dbx_full_page_cache_generation',
'');
270 if ($preparedGeneration ===
'' || !hash_equals($preparedGeneration, self::cacheGeneration())) {
274 $path = (string)
dbx()->get_system_var(
'dbx_full_page_cache_path',
'');
275 if (!is_file($path) || !is_readable($path)) {
279 $html = file_get_contents($path);
280 if (!is_string(
$html) || !self::isCompleteHtml(
$html) || !self::hasCurrentBaseHref(
$html)) {
282 @unlink(self::fullPageMetaPath($path));
288 if (!hash_equals($preparedGeneration, self::cacheGeneration())) {
301 || http_response_code() !== 200
302 || (string)
dbx()->get_system_var(
'dbx_master_modul',
'') !==
'dbxContent'
303 || (int)
dbx()->get_system_var(
'dbx_full_page_cache_cid', 0,
'int') <= 0
304 || !self::isCompleteHtml(
$html)
305 || !self::hasCurrentBaseHref(
$html)
306 || preg_match(self::secureInputPattern(),
$html)) {
310 $preparedGeneration = (string)
dbx()->get_system_var(
'dbx_full_page_cache_generation',
'');
311 if ($preparedGeneration ===
'' || !hash_equals($preparedGeneration, self::cacheGeneration())) {
319 $path = (string)
dbx()->get_system_var(
'dbx_full_page_cache_path',
'');
320 return self::atomicWrite($path,
$html);
329 return
'flat-' . ((int) $flat === 0 ? 0 : 1);
333 $deep = max(1, (int) $deep);
339 $label = trim((
string) $label);
340 $labelKey = $label !==
'' ? substr(sha1($label), 0, 8) :
'menu';
342 return 'load-' . $deep .
'-' .
$mode .
'-' . $labelKey;
346 $lng = trim((string)
dbx()->get_system_var(
'dbx_lng',
'de'));
357 $userDefault = trim((
string)(
$config[
'default_design_user'] ??
'dbxapp'));
358 if ($userDefault ===
'') {
359 $userDefault =
'dbxapp';
362 $design = trim((
string)
dbx()->get_system_var(
'dbx_design', $userDefault));
372 return self::safeToken(
$design,
'dbxapp');
376 if (self::$dirsReady) {
380 foreach (array(
'content',
'content/full-page',
'meta') as $sub) {
381 $dir = self::baseDir() . $sub;
383 @mkdir(
$dir, 0755,
true);
387 self::purgeLegacyMenuCache();
388 self::purgeLegacyPageCache();
389 self::purgeStaleFullPages();
390 self::$dirsReady =
true;
394 private static function purgeLegacyPageCache(): void {
396 self::baseDir() .
'meta/pages/*.json',
397 self::baseDir() .
'meta/permalinks_*.json',
398 self::baseDir() .
'meta/home_*.json',
400 foreach (glob($pattern) ?: array() as
$file) {
405 $contentDir = self::baseDir() .
'content/';
406 foreach (array_merge(glob($contentDir .
'*.htm') ?: array(), glob($contentDir .
'*.html') ?: array()) as
$file) {
408 @unlink(self::fullPageMetaPath(
$file));
410 foreach (glob($contentDir .
'*.html.meta.json') ?: array() as $metaFile) {
411 $htmlFile = preg_replace(
'/\.meta\.json$/',
'', $metaFile);
412 if (!is_string($htmlFile) || !is_file($htmlFile)) {
418 foreach (glob($contentDir .
'full-page/*.htm.meta.json') ?: array() as $metaFile) {
423 foreach (glob($contentDir .
'full-page/*.htm') ?: array() as $fullPageFile) {
424 if (!str_ends_with(strtolower(basename($fullPageFile)),
'_' . self::FULL_PAGE_CACHE_VERSION .
'.htm')) {
425 @unlink($fullPageFile);
428 foreach (glob($contentDir .
'full-page/*', GLOB_ONLYDIR) ?: array() as $legacyDir) {
429 foreach (glob($legacyDir .
'/*') ?: array() as $legacyFile) {
430 if (is_file($legacyFile)) {
431 @unlink($legacyFile);
446 foreach (glob(
$dir .
'*.html') ?: array() as $path) {
447 if (@unlink($path)) {
458 return self::baseDir() .
'content/cid-' . $cid .
'.' .
$lng .
'.htm';
462 return self::fullPagePathForGeneration($permalink,
$lng,
$design, $skin, self::cacheGeneration());
465 private static function fullPagePathForGeneration(
string $permalink,
string $lng,
string $design,
string $skin,
string $generation): string {
466 $normalizedPermalink = self::normalizePermalink($permalink);
467 $permalink = self::permalinkFileToken($normalizedPermalink);
470 $skin =
dbx()->normalize_skin($skin !==
'' ? $skin : (
string)
dbx()->get_system_var(
'dbx_color',
'blau'));
471 $skin = self::safeToken($skin,
'blau');
472 $generation = self::safeToken($generation,
'invalid');
473 $origin = self::requestOriginToken();
475 return self::baseDir() .
'content/full-page/'
476 . $permalink .
'_' .
$lng .
'_' .
$design .
'_' . $skin .
'_'
477 . $origin .
'_' . $generation .
'_' . self::FULL_PAGE_CACHE_VERSION .
'.htm';
481 $permalink = self::permalinkFileToken($permalink);
484 return self::baseDir() .
'content/' . $permalink .
'.' .
$lng .
'.htm';
490 $variant = self::safeToken($variant,
'flat-1');
492 return self::baseDir() .
'menu/' .
$root .
'_' .
$lng .
'_' . $variant .
'.html';
496 return self::
baseDir() .
'meta/pages/' . (int) $cid .
'.json';
514 public static function readMenu(
int $root,
string $variant =
'flat-1',
string $lng =
''): ?string {
523 $meta = self::buildContentMeta($cid, self::
currentLng());
524 return count($meta) ? $meta :
null;
527 private static function readPageMetaFile(
int $cid): ?array {
528 $path = self::pageMetaPath($cid);
529 if (!is_file($path) || !is_readable($path)) {
533 $json = file_get_contents($path);
538 $data = json_decode(
$json,
true);
539 return is_array($data) ? $data :
null;
555 self::invalidateAllFullPages();
557 $permalinks = array();
558 $meta = self::readPageMetaFile($cid);
559 if (is_array($meta)) {
560 $permalink = trim((
string) ($meta[
'permalink'] ??
''));
561 $lng = trim((
string) ($meta[
'lng'] ??
''));
562 if ($permalink !==
'') {
563 $permalinks[] = array(
'permalink' => $permalink,
'lng' =>
$lng);
567 foreach (glob(self::baseDir() .
'meta/permalinks_*.json') ?: array() as $indexFile) {
568 $json = file_get_contents($indexFile);
577 if (preg_match(
'/^permalinks_([a-z]{2,3})\.json$/i', basename($indexFile), $match)) {
578 $lng = strtolower($match[1]);
580 foreach (
$index as $permalink => $row) {
581 if (is_array($row) && (
int) ($row[
'cid'] ?? 0) === $cid) {
582 $permalinks[] = array(
'permalink' => (
string) $permalink,
'lng' =>
$lng);
588 foreach ($permalinks as $item) {
589 $permalink = trim((
string) ($item[
'permalink'] ??
''));
590 if ($permalink ===
'') {
593 $lng = trim((
string) ($item[
'lng'] ??
''));
594 $token = self::permalinkFileToken($permalink);
596 if (isset($seen[$key])) {
600 $legacyToken = self::legacyPermalinkFileToken($permalink);
601 $pattern =
$lng !==
''
602 ? self::baseDir() .
'content/' .
$token .
'.' . self::safeToken(
$lng,
'de') .
'.htm'
603 : self::baseDir() .
'content/' .
$token .
'.*.htm';
604 foreach (glob($pattern) ?: array() as
$file) {
609 self::baseDir() .
'content/' .
$legacyToken .
'__*.html',
610 ) as $legacyPattern) {
611 foreach (glob($legacyPattern) ?: array() as
$file) {
617 foreach (glob(self::baseDir() .
'content/cid-' . $cid .
'.*.htm') ?: array() as
$file) {
620 foreach (glob(self::baseDir() .
'content/cid-' . $cid .
'.*.full-*.html') ?: array() as
$file) {
623 foreach (glob(self::baseDir() .
'content/full-page/*/*.htm.meta.json') ?: array() as $metaFile) {
624 $cacheMeta = self::readJsonFile($metaFile);
625 if (!is_array($cacheMeta) || (
int)($cacheMeta[
'cid'] ?? 0) !== $cid) {
628 $htmlFile = preg_replace(
'/\.meta\.json$/',
'', $metaFile);
629 if (is_string($htmlFile)) {
634 foreach (glob(self::baseDir() .
'content/' . $cid .
'_*.html') ?: array() as
$file) {
638 @unlink(self::pageMetaPath($cid));
639 if (class_exists(__NAMESPACE__ .
'\\dbxContentSitemap')) {
640 dbxContentSitemap::invalidate();
646 $pattern = self::baseDir() .
'menu/' .
$root .
'_*.html';
647 foreach (glob($pattern) ?: array() as
$file) {
650 self::invalidateAllFullPages();
657 self::invalidateAllFullPages();
658 self::invalidateSitemap();
666 $generation = self::cacheGeneration(true);
668 $removed = self::purgeStaleFullPages($generation);
669 foreach (array(
'permalink-*.*.full-*.html',
'cid-*.*.full-*.html') as $pattern) {
670 foreach (glob(self::baseDir() .
'content/' . $pattern) ?: array() as
$file) {
671 if (@unlink(
$file)) {
674 @unlink(self::fullPageMetaPath(
$file));
677 foreach (glob(self::baseDir() .
'content/full-page/*/*.htm') ?: array() as
$file) {
678 if (@unlink(
$file)) {
681 @unlink(self::fullPageMetaPath(
$file));
682 @rmdir(dirname(
$file));
684 foreach (glob(self::baseDir() .
'content/full-page/*.htm.meta.json') ?: array() as $metaFile) {
687 foreach (glob(self::baseDir() .
'content/*.html.meta.json') ?: array() as $metaFile) {
690 foreach (glob(self::baseDir() .
'content/full-page/*/*.htm.meta.json') ?: array() as $metaFile) {
692 @rmdir(dirname($metaFile));
703 if ($generation ===
'') {
704 $generation = self::cacheGeneration();
706 if (!preg_match(
'/^[a-f0-9]{24}$/', $generation)) {
711 $currentSuffix =
'_' . $generation .
'_' . self::FULL_PAGE_CACHE_VERSION .
'.htm';
712 foreach (glob(self::baseDir() .
'content/full-page/*.htm') ?: array() as
$file) {
713 if (str_ends_with(strtolower(basename(
$file)), $currentSuffix)) {
716 if (@unlink(
$file)) {
719 @unlink(self::fullPageMetaPath(
$file));
721 foreach (glob(self::baseDir() .
'content/full-page/*.tmp-*') ?: array() as $temporary) {
722 if (is_file($temporary) && (
int)@filemtime($temporary) < time() - 300) {
730 if (class_exists(__NAMESPACE__ .
'\\dbxContentSitemap')) {
735 @unlink(self::baseDir() .
'meta/sitemap.xml');
745 foreach (array(
'*.htm',
'*.html') as $pattern) {
746 foreach (glob(self::baseDir() .
'content/' . $pattern) ?: array() as
$file) {
747 if (@unlink(
$file)) {
752 foreach (glob(self::baseDir() .
'content/*.html.meta.json') ?: array() as
$file) {
753 if (@unlink(
$file)) {
757 $stats[
'content'] += self::invalidateAllFullPages();
759 foreach (glob(self::baseDir() .
'menu/*.html') ?: array() as
$file) {
760 if (@unlink(
$file)) {
765 foreach (glob(self::baseDir() .
'meta/pages/*.json') ?: array() as
$file) {
766 if (@unlink(
$file)) {
771 foreach (glob(self::baseDir() .
'meta/permalinks_*.json') ?: array() as
$file) {
772 if (@unlink(
$file)) {
777 foreach (glob(self::baseDir() .
'meta/home_*.json') ?: array() as
$file) {
778 if (@unlink(
$file)) {
788 self::purgeStaleFullPages();
789 $content = array_values(array_filter(
790 glob(self::baseDir() .
'content/full-page/*.htm') ?: array(),
791 static fn(
string $path):
bool => str_ends_with(strtolower(basename($path)),
'_' . self::FULL_PAGE_CACHE_VERSION .
'.htm')
793 $sitemapPath = self::baseDir() .
'meta/sitemap.xml';
801 'sitemap' => is_file($sitemapPath) ? 1 : 0,
802 'base_dir' => self::baseDir(),
807 if (!is_object(
$db)) {
811 $folderId = (int) $folderId;
812 self::invalidateMenu($folderId);
813 self::invalidateMenu(0);
815 $folderIds = self::collectFolderIds(
$db, $folderId);
816 foreach ($folderIds as $id) {
817 self::invalidateMenu((
int) $id);
820 $pages =
$db->select(dbxContentLng::ddContent(),
'folder IN (' . implode(
',', array_map(
'intval', $folderIds)) .
')',
'id',
'id',
'ASC',
'', 0, 0, 0);
823 self::invalidateContent((
int) (
$page[
'id'] ?? 0));
828 private static function collectFolderIds(
$db,
int $folderId): array {
829 $folderId = (int) $folderId;
830 $ids = array($folderId);
831 $queue = array($folderId);
833 while (count($queue)) {
834 $current = (int) array_shift($queue);
835 $rows =
$db->select(dbxContentLng::ddFolder(),
'parent_id = ' . $current,
'id',
'id',
'ASC',
'', 0, 0, 0);
836 if (!is_array(
$rows)) {
839 foreach (
$rows as $row) {
840 $id = (int) ($row[
'id'] ?? 0);
841 if ($id > 0 && !in_array($id, $ids,
true)) {
851 private static function buildContentMeta(
int $cid,
string $lng =
''): array {
859 $currentLng = (string)
dbx()->get_system_var(
'dbx_lng',
'de');
860 if (
$lng !== $currentLng) {
861 $prevLng = $currentLng;
862 dbx()->set_system_var(
'dbx_lng',
$lng);
866 require_once __DIR__ .
'/dbxContentRenderer.class.php';
867 $db =
dbx()->get_system_obj(
'dbxDB');
868 if (!is_object(
$db)) {
869 if ($prevLng !==
null) {
870 dbx()->set_system_var(
'dbx_lng', $prevLng);
875 $rec =
$db->select1(dbxContentLng::ddContent(), $cid,
'permalink,activ,folder,title,seo_title,description,keywords,meta_robots,seo_image_id,update_date,lng_uid', 0);
876 if (!is_array($rec)) {
877 if ($prevLng !==
null) {
878 dbx()->set_system_var(
'dbx_lng', $prevLng);
884 $rights =
$renderer->getPublicFolderRights((
int)($rec[
'folder'] ?? 0));
887 'permalink' => (
string)($rec[
'permalink'] ??
''),
889 'activ' => (
int)($rec[
'activ'] ?? 1),
890 'seo' => dbxContentRenderer::seoMetaFromRecord($rec),
892 if ($prevLng !==
null) {
893 dbx()->set_system_var(
'dbx_lng', $prevLng);
898 private static function currentPermalink(): string {
899 $permalink = self::normalizePermalink((string)
dbx()->get_system_var(
'dbx_permalink',
''));
900 return $permalink ===
'' ?
'home' : $permalink;
903 private static function normalizePermalink(
string $permalink): string {
904 $permalink = trim(str_replace(
'\\',
'/', $permalink));
905 if ($permalink ===
'undef' || $permalink ===
'/') {
909 return trim($permalink,
'/');
912 private static function fullPageMetaPath(
string $htmlPath): string {
913 return $htmlPath .
'.meta.json';
916 private static function readJsonFile(
string $path): ?array {
917 if (!is_file($path) || !is_readable($path)) {
921 $json = file_get_contents($path);
926 $data = json_decode(
$json,
true);
927 return is_array($data) ? $data :
null;
930 private static function isCompleteHtml(
string $html): bool {
935 return (
bool) preg_match(
'/^\s*<!doctype\s+html\b/i',
$html)
936 && stripos(
$html,
'</html>') !==
false;
945 private static function hasCurrentBaseHref(
string $html): bool {
951 if (!preg_match(
'~<head\b[^>]*>(.*?)</head>~is',
$html, $headMatch)) {
954 $head = preg_replace(
'~<!--.*?-->|<script\b.*?</script>|<style\b.*?</style>~is',
'', (
string)$headMatch[1]) ??
'';
957 '~<base\b[^>]*\bhref\s*=\s*(?:"([^"]*)"|\'([^\']*)\'|([^\s>]+))~i',
964 $actual = (string)($baseMatch[1] !==
''
966 : (($baseMatch[2] ??
'') !==
'' ? $baseMatch[2] : ($baseMatch[3] ??
'')));
968 return self::normalizeBaseHref($actual) ===
$expected;
972 private static function normalizeBaseHref(
string $href): string {
973 $href = html_entity_decode(trim($href), ENT_QUOTES | ENT_HTML5,
'UTF-8');
974 $href = str_replace(
'\\',
'/', preg_replace(
'/[\x00-\x1F\x7F]/',
'', $href) ??
'');
980 if (!is_array($parts) || empty($parts[
'scheme']) || empty($parts[
'host'])) {
984 $scheme = strtolower((
string)$parts[
'scheme']);
985 $host = strtolower((
string)$parts[
'host']);
986 $port = isset($parts[
'port']) ? (int)$parts[
'port'] : 0;
987 $defaultPort = ($scheme ===
'https') ? 443 : (($scheme ===
'http') ? 80 : 0);
990 if ($port > 0 && $port !== $defaultPort) {
994 if (isset($parts[
'query'])) {
997 if (isset($parts[
'fragment'])) {
1005 private static function secureInputPattern(): string {
1006 return
'/<input\b(?=[^>]*\bname\s*=\s*["\']?_[^"\'\s>]+)(?=[^>]*\bvalue\s*=\s*["\']?[a-f0-9]{64}["\']?)[^>]*>/i';
1009 private static function atomicWrite(
string $path,
string $html): bool {
1014 $dir = dirname($path);
1015 if (!is_dir(
$dir) && !@mkdir(
$dir, 0755,
true) && !is_dir(
$dir)) {
1020 $suffix = bin2hex(random_bytes(6));
1021 }
catch (\Throwable $e) {
1022 $suffix = str_replace(
'.',
'', uniqid(
'',
true));
1025 $tmp = $path .
'.tmp-' . $suffix;
1026 $bytes = @file_put_contents(
$tmp,
$html, LOCK_EX);
1027 if ($bytes ===
false || $bytes !== strlen(
$html)) {
1032 if (@rename(
$tmp, $path)) {
1038 if (is_file($path) && is_readable($path)) {
1053 private static function cacheGeneration(
bool $rotate =
false): string {
1054 $dir = self::baseDir() .
'content/full-page/';
1055 if (!is_dir(
$dir) && !@mkdir(
$dir, 0755,
true) && !is_dir(
$dir)) {
1059 $handle = @fopen(
$dir . self::FULL_PAGE_GENERATION_FILE,
'c+b');
1060 if (!is_resource($handle)) {
1063 if (!@flock($handle, $rotate ? LOCK_EX : LOCK_SH)) {
1069 $generation = trim((
string)stream_get_contents($handle));
1070 if (!$rotate && !preg_match(
'/^[a-f0-9]{24}$/', $generation)) {
1072 @flock($handle, LOCK_UN);
1073 if (!@flock($handle, LOCK_EX)) {
1078 $generation = trim((
string)stream_get_contents($handle));
1081 if ($rotate || !preg_match(
'/^[a-f0-9]{24}$/', $generation)) {
1083 $generation = bin2hex(random_bytes(12));
1084 }
catch (\Throwable $e) {
1085 $generation = substr(hash(
'sha256', uniqid(
'',
true) . microtime(
true)), 0, 24);
1090 && @fwrite($handle, $generation) === strlen($generation)
1091 && @fflush($handle);
1097 @flock($handle, LOCK_UN);
1103 private static function requestOriginToken(): string {
1104 $forwardedProto = strtolower(trim(explode(
',', (string)(
$_SERVER[
'HTTP_X_FORWARDED_PROTO'] ??
''))[0]));
1105 if ($forwardedProto ===
'http' || $forwardedProto ===
'https') {
1106 $scheme = $forwardedProto;
1114 $host = strtolower(trim((
string)(
$_SERVER[
'HTTP_HOST'] ??
$_SERVER[
'SERVER_NAME'] ??
'local')));
1115 $host = preg_replace(
'/[^a-z0-9.:[\]-]+/i',
'', $host) ?:
'local';
1116 $script = str_replace(
'\\',
'/', (
string)(
$_SERVER[
'SCRIPT_NAME'] ??
''));
1117 $installPath = trim(str_replace(
'\\',
'/', dirname($script)),
'/.');
1119 return substr(hash(
'sha256', $scheme .
'://' . $host .
'/' . $installPath), 0, 16);
1122 private static function safeToken(
string $value,
string $fallback): string {
1124 if (
$value ===
'' || !preg_match(
'/^[a-z0-9_-]+$/',
$value)) {
1130 private static function permalinkFileToken(
string $permalink): string {
1131 $permalink = strtolower(trim(str_replace(
'\\',
'/', $permalink),
'/'));
1132 if ($permalink ===
'') {
1133 $permalink =
'home';
1136 $token = preg_replace(
'/[^a-z0-9_-]+/i',
'-', $permalink);
1142 if (strlen(
$token) > 72) {
1148 return strtolower(
$token .
'-' . substr(hash(
'sha256', $permalink), 0, 24));
1151 private static function legacyPermalinkFileToken(
string $permalink): string {
1152 $permalink = strtolower(trim(str_replace(
'\\',
'/', $permalink),
'/'));
1153 if ($permalink ===
'') {
1157 $token = preg_replace(
'/[^a-z0-9_-]+/i',
'-', $permalink);
1163 $hash = substr(sha1($permalink), 0, 8);
1164 if (strlen(
$token) > 80) {
1168 return strtolower(
$token .
'-' . $hash);
static writeFullPage(string $html)
Schreibt die finale Ausgabe nach Design, Modulen, Interpreter und Filtern.