dbXapp 2.0
RAD, CMS, Module und Runtime-IDE fuer dbXapp
Loading...
Searching...
No Matches
dbxContentPageCache.class.php
Go to the documentation of this file.
1<?php
2namespace dbx\dbxContent;
3
4require_once __DIR__ . '/dbxContentLng.class.php';
5
7
8 public static function isConfigEnabled(): bool {
9 $configFile = dirname(__DIR__, 2) . '/dbx/cfg/config.php';
10 if (is_file($configFile) && is_readable($configFile)) {
11 $readConfig = static function(string $path): array {
12 $config = array();
13 include $path;
14 return is_array($config) ? $config : array();
15 };
16 $fileConfig = $readConfig($configFile);
17 if (array_key_exists('cache_content', $fileConfig)) {
18 return (int)$fileConfig['cache_content'] === 1;
19 }
20 }
21
22 $enabled = dbx()->get_config('dbx', 'cache_content');
23 if ($enabled === 'undef' || $enabled === '' || $enabled === null) {
24 return true;
25 }
26
27 return (int) $enabled === 1;
28 }
29
30 public static function setConfigEnabled(bool $enabled): bool {
31 $config = dbx()->get_config('dbx');
32 if (!is_array($config)) {
33 $config = array();
34 }
35
36 $config['cache_content'] = $enabled ? 1 : 0;
37 return (int) dbx()->set_config('dbx', $config) > 0;
38 }
39
40 public static function isEnabled(): bool {
41 $method = strtoupper(trim((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')));
42 if ($method !== 'GET' && $method !== 'HEAD') {
43 return false;
44 }
45
46 if ((int) dbx()->get_request_var('dbx_sync', 1, 'int') !== 1) {
47 return false;
48 }
49
50 if ((int) dbx()->get_system_var('dbx_edit', 0, 'int') > 0) {
51 return false;
52 }
53
54 if ((int) dbx()->get_system_var('dbx_ajax', 0, 'int')) {
55 return false;
56 }
57
58 if ((int) dbx()->get_system_var('dbx_window', 0, 'int')) {
59 return false;
60 }
61
62 return self::isConfigEnabled();
63 }
64
65 public static function isContentRoute(): bool {
66 if (dbx()->get_system_var('dbx_modul') !== 'dbxContent') {
67 return false;
68 }
69
70 $action = (string) dbx()->get_system_var('dbx_run1', '');
71 return $action === 'show';
72 }
73
74 public static function isPermalinkPageRequest(): bool {
75 if (!self::isEnabled()) {
76 return false;
77 }
78
79 if ((int) dbx()->get_system_var('dbx_content_permalink_request', 0, 'int') !== 1) {
80 return false;
81 }
82
83 return (int) dbx()->get_system_var('dbx_content_route_cid', 0, 'int') > 0;
84 }
85
86 public static function baseDir(): string {
87 $dir = rtrim(dbx()->get_file_dir(), '/\\') . '/cache/';
88 return dbx()->os_path($dir);
89 }
90
91 public static function menuVariantFlat(int $flat = 1): string {
92 return 'flat-' . ((int) $flat === 0 ? 0 : 1);
93 }
94
95 public static function menuVariantLoad(int $deep = 9, string $mode = '', string $label = ''): string {
96 $deep = max(1, (int) $deep);
97 $mode = strtolower(trim($mode));
98 if ($mode === '') {
99 $mode = 'default';
100 }
101 $mode = self::safeToken($mode, 'default');
102 $label = trim((string) $label);
103 $labelKey = $label !== '' ? substr(sha1($label), 0, 8) : 'menu';
104
105 return 'load-' . $deep . '-' . $mode . '-' . $labelKey;
106 }
107
108 public static function currentLng(): string {
109 $lng = trim((string) dbx()->get_system_var('dbx_lng', 'de'));
110 return $lng !== '' ? $lng : 'de';
111 }
112
113 public static function ensureDirs(): void {
114 foreach (array('content', 'meta') as $sub) {
115 $dir = self::baseDir() . $sub;
116 if (!is_dir($dir)) {
117 @mkdir($dir, 0755, true);
118 }
119 }
120
121 self::purgeLegacyMenuCache();
122 self::purgeLegacyPageCache();
123 }
124
126 private static function purgeLegacyPageCache(): void {
127 foreach (array(
128 self::baseDir() . 'meta/pages/*.json',
129 self::baseDir() . 'meta/permalinks_*.json',
130 self::baseDir() . 'meta/home_*.json',
131 ) as $pattern) {
132 foreach (glob($pattern) ?: array() as $file) {
133 @unlink($file);
134 }
135 }
136
137 $contentDir = self::baseDir() . 'content/';
138 foreach (array_merge(glob($contentDir . '*.htm') ?: array(), glob($contentDir . '*.html') ?: array()) as $file) {
139 if (preg_match('/^cid-\d+\.[a-z]{2,3}\.htm$/i', basename($file))) {
140 continue;
141 }
142 @unlink($file);
143 }
144 }
145
147 public static function purgeLegacyMenuCache(): int {
148 $dir = self::baseDir() . 'menu/';
149 if (!is_dir($dir)) {
150 return 0;
151 }
152
153 $removed = 0;
154 foreach (glob($dir . '*.html') ?: array() as $path) {
155 if (@unlink($path)) {
156 $removed++;
157 }
158 }
159
160 return $removed;
161 }
162
163 public static function contentPath(int $cid, string $lng): string {
164 $cid = (int) $cid;
165 $lng = self::safeToken($lng, 'de');
166 return self::baseDir() . 'content/cid-' . $cid . '.' . $lng . '.htm';
167 }
168
169 public static function permalinkContentPath(string $permalink, string $lng): string {
170 $permalink = self::permalinkFileToken($permalink);
171 $lng = self::safeToken($lng, 'de');
172
173 return self::baseDir() . 'content/' . $permalink . '.' . $lng . '.htm';
174 }
175
176 public static function menuPath(int $root, string $lng, string $variant = 'flat-1'): string {
177 $root = (int) $root;
178 $lng = self::safeToken($lng, 'de');
179 $variant = self::safeToken($variant, 'flat-1');
180
181 return self::baseDir() . 'menu/' . $root . '_' . $lng . '_' . $variant . '.html';
182 }
183
184 public static function pageMetaPath(int $cid): string {
185 return self::baseDir() . 'meta/pages/' . (int) $cid . '.json';
186 }
187
188 public static function readContent(int $cid, string $lng = ''): ?string {
189 if (!self::isPermalinkPageRequest()) {
190 return null;
191 }
192
193 if ($lng === '') {
194 $lng = self::currentLng();
195 }
196
197 $path = self::contentPath($cid, $lng);
198 if (!is_file($path) || !is_readable($path)) {
199 return null;
200 }
201
202 $html = file_get_contents($path);
203 if (!is_string($html)) {
204 return null;
205 }
206
207 require_once __DIR__ . '/dbxContentRenderer.class.php';
208 (new dbxContentRenderer())->applySeoMeta($cid);
209 return $html;
210 }
211
212 public static function readPermalinkContent(string $permalink, string $lng = ''): ?string {
213 // Ein separater Permalink-Cache existiert nicht mehr.
214 return null;
215 }
216
217 public static function writeContent(int $cid, string $html, array $meta = array(), string $lng = ''): bool {
218 if (!self::isPermalinkPageRequest()) {
219 return false;
220 }
221
222 $cid = (int) $cid;
223 if ($cid <= 0 || trim($html) === '') {
224 return false;
225 }
226
227 if ($lng === '') {
228 $lng = self::currentLng();
229 }
230
231 self::ensureDirs();
232 $path = self::contentPath($cid, $lng);
233 return (bool) @file_put_contents($path, $html, LOCK_EX);
234 }
235
236 public static function readMenu(int $root, string $variant = 'flat-1', string $lng = ''): ?string {
237 return null;
238 }
239
240 public static function writeMenu(string $html, int $root, string $variant = 'flat-1', string $lng = ''): bool {
241 return false;
242 }
243
244 public static function readPageMeta(int $cid): ?array {
245 $meta = self::buildContentMeta($cid, self::currentLng());
246 return count($meta) ? $meta : null;
247 }
248
249 private static function readPageMetaFile(int $cid): ?array {
250 $path = self::pageMetaPath($cid);
251 if (!is_file($path) || !is_readable($path)) {
252 return null;
253 }
254
255 $json = file_get_contents($path);
256 if (!is_string($json) || $json === '') {
257 return null;
258 }
259
260 $data = json_decode($json, true);
261 return is_array($data) ? $data : null;
262 }
263
264 public static function writePageMeta(int $cid, array $meta): bool {
265 // Seiten-Metadaten werden live aus dbxContent gelesen und nicht gecacht.
266 return true;
267 }
268
269 public static function invalidateContent(int $cid): void {
270 $cid = (int) $cid;
271 if ($cid <= 0) {
272 return;
273 }
274
275 $permalinks = array();
276 $meta = self::readPageMetaFile($cid);
277 if (is_array($meta)) {
278 $permalink = trim((string) ($meta['permalink'] ?? ''));
279 $lng = trim((string) ($meta['lng'] ?? ''));
280 if ($permalink !== '') {
281 $permalinks[] = array('permalink' => $permalink, 'lng' => $lng);
282 }
283 }
284
285 foreach (glob(self::baseDir() . 'meta/permalinks_*.json') ?: array() as $indexFile) {
286 $json = file_get_contents($indexFile);
287 if (!is_string($json) || $json === '') {
288 continue;
289 }
290 $index = json_decode($json, true);
291 if (!is_array($index)) {
292 continue;
293 }
294 $lng = '';
295 if (preg_match('/^permalinks_([a-z]{2,3})\.json$/i', basename($indexFile), $match)) {
296 $lng = strtolower($match[1]);
297 }
298 foreach ($index as $permalink => $row) {
299 if (is_array($row) && (int) ($row['cid'] ?? 0) === $cid) {
300 $permalinks[] = array('permalink' => (string) $permalink, 'lng' => $lng);
301 }
302 }
303 }
304
305 $seen = array();
306 foreach ($permalinks as $item) {
307 $permalink = trim((string) ($item['permalink'] ?? ''));
308 if ($permalink === '') {
309 continue;
310 }
311 $lng = trim((string) ($item['lng'] ?? ''));
312 $token = self::permalinkFileToken($permalink);
313 $key = $token . '|' . $lng;
314 if (isset($seen[$key])) {
315 continue;
316 }
317 $seen[$key] = 1;
318 $legacyToken = self::legacyPermalinkFileToken($permalink);
319 $pattern = $lng !== ''
320 ? self::baseDir() . 'content/' . $token . '.' . self::safeToken($lng, 'de') . '.htm'
321 : self::baseDir() . 'content/' . $token . '.*.htm';
322 foreach (glob($pattern) ?: array() as $file) {
323 @unlink($file);
324 }
325 foreach (array(
326 self::baseDir() . 'content/' . $legacyToken . '.*.htm',
327 self::baseDir() . 'content/' . $legacyToken . '__*.html',
328 ) as $legacyPattern) {
329 foreach (glob($legacyPattern) ?: array() as $file) {
330 @unlink($file);
331 }
332 }
333 }
334
335 foreach (glob(self::baseDir() . 'content/cid-' . $cid . '.*.htm') ?: array() as $file) {
336 @unlink($file);
337 }
338 foreach (glob(self::baseDir() . 'content/' . $cid . '_*.html') ?: array() as $file) {
339 @unlink($file);
340 }
341
342 @unlink(self::pageMetaPath($cid));
343 if (class_exists(__NAMESPACE__ . '\\dbxContentSitemap')) {
344 dbxContentSitemap::invalidate();
345 }
346 }
347
348 public static function invalidateMenu(int $root): void {
349 $root = (int) $root;
350 $pattern = self::baseDir() . 'menu/' . $root . '_*.html';
351 foreach (glob($pattern) ?: array() as $file) {
352 @unlink($file);
353 }
354 }
355
356 public static function invalidateAllMenus(): void {
357 foreach (glob(self::baseDir() . 'menu/*.html') ?: array() as $file) {
358 @unlink($file);
359 }
360 self::invalidateSitemap();
361 }
362
363 public static function invalidateSitemap(): void {
364 if (class_exists(__NAMESPACE__ . '\\dbxContentSitemap')) {
366 return;
367 }
368
369 @unlink(self::baseDir() . 'meta/sitemap.xml');
370 }
371
372 public static function invalidateAll(): array {
373 $stats = array(
374 'content' => 0,
375 'menu' => 0,
376 'meta' => 0,
377 );
378
379 foreach (array('*.htm', '*.html') as $pattern) {
380 foreach (glob(self::baseDir() . 'content/' . $pattern) ?: array() as $file) {
381 if (@unlink($file)) {
382 $stats['content']++;
383 }
384 }
385 }
386
387 foreach (glob(self::baseDir() . 'menu/*.html') ?: array() as $file) {
388 if (@unlink($file)) {
389 $stats['menu']++;
390 }
391 }
392
393 foreach (glob(self::baseDir() . 'meta/pages/*.json') ?: array() as $file) {
394 if (@unlink($file)) {
395 $stats['meta']++;
396 }
397 }
398
399 foreach (glob(self::baseDir() . 'meta/permalinks_*.json') ?: array() as $file) {
400 if (@unlink($file)) {
401 $stats['meta']++;
402 }
403 }
404
405 foreach (glob(self::baseDir() . 'meta/home_*.json') ?: array() as $file) {
406 if (@unlink($file)) {
407 $stats['meta']++;
408 }
409 }
410
411 return $stats;
412 }
413
414 public static function cacheStats(): array {
415 self::ensureDirs();
416 $content = array_values(array_filter(
417 glob(self::baseDir() . 'content/cid-*.*.htm') ?: array(),
418 static fn(string $path): bool => (bool) preg_match('/^cid-\d+\.[a-z]{2,3}\.htm$/i', basename($path))
419 ));
420 $sitemapPath = self::baseDir() . 'meta/sitemap.xml';
421
422 return array(
423 'content' => count($content),
424 'menu' => 0,
425 'meta' => 0,
426 'permalinks' => 0,
427 'home' => 0,
428 'sitemap' => is_file($sitemapPath) ? 1 : 0,
429 'base_dir' => self::baseDir(),
430 );
431 }
432
433 public static function invalidateFolderTree($db, int $folderId): void {
434 if (!is_object($db)) {
435 return;
436 }
437
438 $folderId = (int) $folderId;
439 self::invalidateMenu($folderId);
440 self::invalidateMenu(0);
441
442 $folderIds = self::collectFolderIds($db, $folderId);
443 foreach ($folderIds as $id) {
444 self::invalidateMenu((int) $id);
445 }
446
447 $pages = $db->select(dbxContentLng::ddContent(), 'folder IN (' . implode(',', array_map('intval', $folderIds)) . ')', 'id', 'id', 'ASC', '', 0, 0, 0);
448 if (is_array($pages)) {
449 foreach ($pages as $page) {
450 self::invalidateContent((int) ($page['id'] ?? 0));
451 }
452 }
453 }
454
455 private static function collectFolderIds($db, int $folderId): array {
456 $folderId = (int) $folderId;
457 $ids = array($folderId);
458 $queue = array($folderId);
459
460 while (count($queue)) {
461 $current = (int) array_shift($queue);
462 $rows = $db->select(dbxContentLng::ddFolder(), 'parent_id = ' . $current, 'id', 'id', 'ASC', '', 0, 0, 0);
463 if (!is_array($rows)) {
464 continue;
465 }
466 foreach ($rows as $row) {
467 $id = (int) ($row['id'] ?? 0);
468 if ($id > 0 && !in_array($id, $ids, true)) {
469 $ids[] = $id;
470 $queue[] = $id;
471 }
472 }
473 }
474
475 return $ids;
476 }
477
478 private static function buildContentMeta(int $cid, string $lng = ''): array {
479 $cid = (int) $cid;
480 if ($cid <= 0) {
481 return array();
482 }
483
484 $prevLng = null;
485 if ($lng !== '') {
486 $currentLng = (string) dbx()->get_system_var('dbx_lng', 'de');
487 if ($lng !== $currentLng) {
488 $prevLng = $currentLng;
489 dbx()->set_system_var('dbx_lng', $lng);
490 }
491 }
492
493 require_once __DIR__ . '/dbxContentRenderer.class.php';
494 $db = dbx()->get_system_obj('dbxDB');
495 if (!is_object($db)) {
496 if ($prevLng !== null) {
497 dbx()->set_system_var('dbx_lng', $prevLng);
498 }
499 return array();
500 }
501
502 $rec = $db->select1(dbxContentLng::ddContent(), $cid, 'permalink,activ,folder,title,seo_title,description,keywords,meta_robots,seo_image_id,update_date,lng_uid', 0);
503 if (!is_array($rec)) {
504 if ($prevLng !== null) {
505 dbx()->set_system_var('dbx_lng', $prevLng);
506 }
507 return array();
508 }
509
510 $renderer = new dbxContentRenderer();
511 $rights = $renderer->getPublicFolderRights((int)($rec['folder'] ?? 0));
512 $meta = array(
513 'cid' => $cid,
514 'permalink' => (string)($rec['permalink'] ?? ''),
515 'rights' => $rights,
516 'activ' => (int)($rec['activ'] ?? 1),
517 'seo' => dbxContentRenderer::seoMetaFromRecord($rec),
518 );
519 if ($prevLng !== null) {
520 dbx()->set_system_var('dbx_lng', $prevLng);
521 }
522 return $meta;
523 }
524
525 private static function safeToken(string $value, string $fallback): string {
526 $value = strtolower(trim($value));
527 if ($value === '' || !preg_match('/^[a-z0-9_-]+$/', $value)) {
528 return $fallback;
529 }
530 return $value;
531 }
532
533 private static function permalinkFileToken(string $permalink): string {
534 $permalink = strtolower(trim(str_replace('\\', '/', $permalink), '/'));
535 if ($permalink === '') {
536 return 'home';
537 }
538
539 $token = preg_replace('/[^a-z0-9_-]+/i', '-', $permalink);
540 $token = trim((string) $token, '-_');
541 if ($token === '') {
542 $token = 'page';
543 }
544
545 if (strlen($token) > 160) {
546 $token = substr($token, 0, 160);
547 }
548
549 return strtolower($token);
550 }
551
552 private static function legacyPermalinkFileToken(string $permalink): string {
553 $permalink = strtolower(trim(str_replace('\\', '/', $permalink), '/'));
554 if ($permalink === '') {
555 return 'home';
556 }
557
558 $token = preg_replace('/[^a-z0-9_-]+/i', '-', $permalink);
559 $token = trim((string) $token, '-_');
560 if ($token === '') {
561 $token = 'page';
562 }
563
564 $hash = substr(sha1($permalink), 0, 8);
565 if (strlen($token) > 80) {
566 $token = substr($token, 0, 80);
567 }
568
569 return strtolower($token . '-' . $hash);
570 }
571}
$index['name']
Definition .dd.php:162
static permalinkContentPath(string $permalink, string $lng)
static readPermalinkContent(string $permalink, string $lng='')
static writeMenu(string $html, int $root, string $variant='flat-1', string $lng='')
static readMenu(int $root, string $variant='flat-1', string $lng='')
static writeContent(int $cid, string $html, array $meta=array(), string $lng='')
static menuVariantLoad(int $deep=9, string $mode='', string $label='')
static readContent(int $cid, string $lng='')
static menuPath(int $root, string $lng, string $variant='flat-1')
static writePageMeta(int $cid, array $meta)
static purgeLegacyMenuCache()
Entfernt den nicht mehr verwendeten Menu-Cache.
$config['version']
Definition config.php:2
if( $syncRequest)
Definition index.php:520
DBX schema administration.
$_SERVER['REQUEST_URI']
foreach( $topics as $file=> $html)