Completed
Push — 4.x ( bb307a...95d6cf )
by Torben
07:18 queued 06:00
created

PageCache::calculatePageCacheTimeout()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.7
c 0
b 0
f 0
cc 4
nc 5
nop 1
1
<?php
2
namespace DERHANSEN\SfEventMgt\Hooks;
3
4
/*
5
 * This file is part of the Extension "sf_event_mgt" for TYPO3 CMS.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.txt file that was distributed with this source code.
9
 */
10
11
use TYPO3\CMS\Core\Database\ConnectionPool;
12
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
13
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
14
use TYPO3\CMS\Core\Utility\GeneralUtility;
15
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
16
17
/**
18
 * PageCache class which implementes the TYPO3 Core hook:
19
 * $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['get_cache_timeout']
20
 */
21
class PageCache
22
{
23
    /**
24
     * Calculates the page cache timeout for configured pages with event records. Pages must be configured in TypoScript
25
     * using cache.config (similar to starttime/endtime cache handling)
26
     *
27
     * Example: config.cache.3 = tx_sfeventmgt_domain_model_event:2
28
     *
29
     * The cache for PID 3 will respect registration_startdate/registration_deadline of event record in PID 2
30
     *
31
     * @param array $params
32
     * @param TypoScriptFrontendController $pObj
33
     * @return int|mixed
34
     */
35
    public function getCacheTimeout(array $params, TypoScriptFrontendController $pObj)
36
    {
37
        $eventBasedCacheTimeout = $this->calculatePageCacheTimeout($pObj);
38
        if ($eventBasedCacheTimeout === PHP_INT_MAX || $eventBasedCacheTimeout >= $params['cacheTimeout']) {
39
            // Return previous calculated timeout, since event based cache timeout is either not determined or larger
40
            return $params['cacheTimeout'];
41
        } else {
42
            return $eventBasedCacheTimeout;
43
        }
44
    }
45
46
    /**
47
     * Calculates page cache timeout according to the events with registration_startdate/registration_deadline
48
     * on the page.
49
     *
50
     * Nearly similar to TypoScriptFrontendController::calculatePageCacheTimeout()
51
     *
52
     * @return int Page cache timeout or PHP_INT_MAX if cannot be determined
53
     */
54
    protected function calculatePageCacheTimeout(TypoScriptFrontendController $pObj)
55
    {
56
        $result = PHP_INT_MAX;
57
        $tablesToConsider = $this->getCurrentPageCacheConfiguration($pObj);
58
59
        if (empty($tablesToConsider)) {
60
            return $result;
61
        }
62
63
        $now = $GLOBALS['ACCESS_TIME'];
64
65
        foreach ($tablesToConsider as $tableDef) {
66
            $result = min($result, $this->getFirstTimeValueForEvent($tableDef, $now));
67
        }
68
69
        return $result === PHP_INT_MAX ? PHP_INT_MAX : $result - $now + 1;
70
    }
71
72
    /**
73
     * Nearly similar to TypoScriptFrontendController::getCurrentPageCacheConfiguration, but only returns
74
     * entries that are relevant for sf_event_mgt
75
     *
76
     * @return array
77
     */
78
    protected function getCurrentPageCacheConfiguration(TypoScriptFrontendController $pObj): array
79
    {
80
        $tables = ['tt_content:' . $pObj->id];
81
        if (isset($pObj->config['config']['cache.'][$pObj->id])) {
82
            $cacheConfig = str_replace(':current', ':' . $pObj->id, $pObj->config['config']['cache.'][$pObj->id]);
83
            $tables = array_merge($tables, GeneralUtility::trimExplode(',', $cacheConfig));
84
        }
85
        if (isset($pObj->config['config']['cache.']['all'])) {
86
            $cacheConfig = str_replace(':current', ':' . $pObj->id, $pObj->config['config']['cache.']['all']);
87
            $tables = array_merge($tables, GeneralUtility::trimExplode(',', $cacheConfig));
88
        }
89
        $tables = array_unique($tables);
90
91
        $result = [];
92
        foreach ($tables as $table) {
93
            if (strpos($table, 'tx_sfeventmgt_domain_model_event') !== false) {
94
                $result[] = $table;
95
            }
96
        }
97
98
        return $result;
99
    }
100
101
    /**
102
     * Nearly similar to TypoScriptFrontendController::getFirstTimeValueForRecord but only considers event fields
103
     * registration_startdate and registration_deadline.
104
     *
105
     * @param string $tableDef Table definition (format tablename:pid)
106
     * @param int $now "Now" time value
107
     * @return int Value of the next registration_startdate/registration_deadline time or PHP_INT_MAX if not found
108
     */
109
    protected function getFirstTimeValueForEvent(string $tableDef, int $now): int
110
    {
111
        $now = (int)$now;
112
        $result = PHP_INT_MAX;
113
        list($tableName, $pid) = GeneralUtility::trimExplode(':', $tableDef);
114
115
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
116
            ->getQueryBuilderForTable($tableName);
117
        $queryBuilder->getRestrictions()
118
            ->removeByType(StartTimeRestriction::class)
119
            ->removeByType(EndTimeRestriction::class);
120
        $timeFields = ['registration_startdate', 'registration_deadline'];
121
        $timeConditions = $queryBuilder->expr()->orX();
122
        foreach ($timeFields as $field) {
123
            $queryBuilder->addSelectLiteral(
124
                'MIN('
125
                . 'CASE WHEN '
126
                . $queryBuilder->expr()->lte(
127
                    $field,
128
                    $queryBuilder->createNamedParameter($now, \PDO::PARAM_INT)
129
                )
130
                . ' THEN NULL ELSE ' . $queryBuilder->quoteIdentifier($field) . ' END'
131
                . ') AS ' . $queryBuilder->quoteIdentifier($field)
132
            );
133
            $timeConditions->add(
134
                $queryBuilder->expr()->gt(
135
                    $field,
136
                    $queryBuilder->createNamedParameter($now, \PDO::PARAM_INT)
137
                )
138
            );
139
        }
140
141
        // Only consider events where registration is enabled and that have not started yet.
142
        // Also include PID and timeConditions
143
        $row = $queryBuilder
144
            ->from($tableName)
145
            ->where(
146
                $queryBuilder->expr()->eq(
147
                    'pid',
148
                    $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)
149
                ),
150
                $queryBuilder->expr()->eq(
151
                    'enable_registration',
152
                    $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)
153
                ),
154
                $queryBuilder->expr()->gt(
155
                    'startdate',
156
                    $queryBuilder->createNamedParameter($now, \PDO::PARAM_INT)
157
                ),
158
                $timeConditions
159
            )
160
            ->execute()
161
            ->fetch();
162
163
        if ($row) {
164
            foreach ($timeFields as $timeField) {
165
                if ($row[$timeField] !== null && (int)$row[$timeField] > $now) {
166
                    $result = min($result, (int)$row[$timeField]);
167
                }
168
            }
169
        }
170
171
        return $result;
172
    }
173
}
174