CacheDatabaseRepository   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Importance

Changes 6
Bugs 2 Features 0
Metric Value
eloc 85
dl 0
loc 261
rs 10
c 6
b 2
f 0
wmc 26

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getCurrentDateTime() 0 3 2
A update() 0 10 1
A getAll() 0 17 2
A renew() 0 16 2
A getRenewExpirationQueryWithDriver() 0 9 2
A flush() 0 3 1
A clear() 0 9 1
A retrieve() 0 16 2
A getDeleteQueryWithDriver() 0 7 3
A getUpdateQueryWithDriver() 0 7 3
A serialize() 0 3 2
A hasValidCache() 0 12 1
A store() 0 20 3
A __construct() 0 3 1
1
<?php
2
3
namespace Silviooosilva\CacheerPhp\Repositories;
4
5
use PDO;
6
use Silviooosilva\CacheerPhp\Core\Connect;
7
8
/**
9
 * Class CacheDatabaseRepository
10
 * @author Sílvio Silva <https://github.com/silviooosilva>
11
 * @package Silviooosilva\CacheerPhp
12
 */
13
class CacheDatabaseRepository
14
{
15
16
    /** @var PDO */
17
    private $connection = null;
18
19
   
20
    /**
21
     * CacheDatabaseRepository constructor.
22
     * Initializes the database connection using the Connect class.
23
     * 
24
     */
25
    public function __construct()
26
    {
27
        $this->connection = Connect::getInstance();
28
    }
29
30
31
    /**
32
     * Stores cache data in the database.
33
     * 
34
     * @param string $cacheKey
35
     * @param mixed  $cacheData
36
     * @param string $namespace
37
     * @param string|int $ttl
38
     * @return bool
39
     */
40
    public function store(string $cacheKey, mixed $cacheData, string $namespace, string|int $ttl = 3600)
41
    {
42
        if (!empty($this->retrieve($cacheKey, $namespace))) {
43
            return $this->update($cacheKey, $cacheData, $namespace);
44
        }
45
46
        $expirationTime = date('Y-m-d H:i:s', time() + $ttl);
47
        $createdAt = date('Y-m-d H:i:s');
48
49
        $stmt = $this->connection->prepare(
50
            "INSERT INTO cacheer_table (cacheKey, cacheData, cacheNamespace, expirationTime, created_at) 
51
            VALUES (:cacheKey, :cacheData, :namespace, :expirationTime, :createdAt)"
52
        );
53
        $stmt->bindValue(':cacheKey', $cacheKey);
54
        $stmt->bindValue(':cacheData', $this->serialize($cacheData));
55
        $stmt->bindValue(':namespace', $namespace);
56
        $stmt->bindValue(':expirationTime', $expirationTime);
57
        $stmt->bindValue(':createdAt', $createdAt);
58
59
        return $stmt->execute() && $stmt->rowCount() > 0;
60
    }
61
62
    /**
63
    * Retrieves cache data from the database.
64
    * 
65
    * @param string $cacheKey
66
    * @param string $namespace
67
    * @return mixed
68
    */
69
    public function retrieve(string $cacheKey, string $namespace = '')
70
    {
71
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
72
        $nowFunction = $this->getCurrentDateTime($driver);
73
74
        $stmt = $this->connection->prepare(
75
            "SELECT cacheData FROM cacheer_table 
76
            WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > $nowFunction
77
            LIMIT 1"
78
        );
79
        $stmt->bindValue(':cacheKey', $cacheKey);
80
        $stmt->bindValue(':namespace', $namespace);
81
        $stmt->execute();
82
83
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
84
        return (!empty($data)) ? $this->serialize($data['cacheData'], false) : null;
85
    }
86
87
    /**
88
    * Retrieves multiple cache items by their keys. 
89
    * @param array $cacheKeys
90
    * @param string $namespace
91
    * @return array
92
    */
93
    public function getAll(string $namespace = '')
94
    {
95
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
96
        $nowFunction = $this->getCurrentDateTime($driver);
97
98
        $stmt = $this->connection->prepare(
99
            "SELECT cacheKey, cacheData FROM cacheer_table 
100
            WHERE cacheNamespace = :namespace AND expirationTime > $nowFunction"
101
        );
102
        $stmt->bindValue(':namespace', $namespace);
103
        $stmt->execute();
104
105
        $results = [];
106
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
107
            $results[$row['cacheKey']] = $this->serialize($row['cacheData'], false);
108
        }
109
        return $results;
110
    }
111
112
    /**
113
    * Get Update query based on the database driver.
114
    *
115
    * @return string
116
    */
117
    private function getUpdateQueryWithDriver()
118
    {
119
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
120
        if ($driver === 'mysql' || $driver === 'mariadb') {
121
            return "UPDATE cacheer_table SET cacheData = :cacheData, cacheNamespace = :namespace WHERE cacheKey = :cacheKey LIMIT 1";
122
        }
123
        return "UPDATE cacheer_table SET cacheData = :cacheData, cacheNamespace = :namespace WHERE cacheKey = :cacheKey";
124
    }
125
126
    /**
127
    * Get Delete query based on the database driver.
128
    * 
129
    * @return string
130
    */
131
    private function getDeleteQueryWithDriver()
132
    {
133
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
134
        if ($driver === 'mysql' || $driver === 'mariadb') {
135
            return "DELETE FROM cacheer_table WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace LIMIT 1";
136
        }
137
        return "DELETE FROM cacheer_table WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace";
138
    }
139
140
    /**
141
    * Updates an existing cache item in the database.
142
    * 
143
    * @param string $cacheKey
144
    * @param mixed  $cacheData
145
    * @param string $namespace
146
    * @return bool
147
    */
148
    public function update(string $cacheKey, mixed $cacheData, string $namespace = '')
149
    {
150
        $query = $this->getUpdateQueryWithDriver();
151
        $stmt = $this->connection->prepare($query);
152
        $stmt->bindValue(':cacheData', $this->serialize($cacheData));
153
        $stmt->bindValue(':namespace', $namespace);
154
        $stmt->bindValue(':cacheKey', $cacheKey);
155
        $stmt->execute();
156
157
        return $stmt->rowCount() > 0;
158
    }
159
160
    /**
161
    * Clears a specific cache item from the database.
162
    * 
163
    * @param string $cacheKey
164
    * @param string $namespace
165
    * @return bool
166
    */
167
    public function clear(string $cacheKey, string $namespace = '')
168
    {
169
        $query = $this->getDeleteQueryWithDriver();
170
        $stmt = $this->connection->prepare($query);
171
        $stmt->bindValue(':cacheKey', $cacheKey);
172
        $stmt->bindValue(':namespace', $namespace);
173
        $stmt->execute();
174
175
        return $stmt->rowCount() > 0;
176
    }
177
178
    /**
179
    * Gets the query to renew the expiration time of a cache item based on the database driver.
180
    *  
181
    * @return string
182
    */
183
    private function getRenewExpirationQueryWithDriver(): string
184
    {
185
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
186
        if ($driver === 'sqlite') {
187
            return "UPDATE cacheer_table
188
                    SET expirationTime = DATETIME(expirationTime, '+' || :ttl || ' seconds')
189
                    WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime";
190
        }
191
        return "UPDATE cacheer_table
192
                SET expirationTime = DATE_ADD(expirationTime, INTERVAL :ttl SECOND)
193
                WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime";
194
    }
195
196
    /**
197
    * Checks if a cache item is valid based on its key, namespace, and current time.
198
    * 
199
    * @param string $cacheKey
200
    * @param string $namespace
201
    * @param string $currentTime
202
    * @return bool
203
    */
204
    private function hasValidCache(string $cacheKey, string $namespace, string $currentTime): bool
205
    {
206
        $stmt = $this->connection->prepare(
207
            "SELECT 1 FROM cacheer_table 
208
            WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime
209
            LIMIT 1"
210
        );
211
        $stmt->bindValue(':cacheKey', $cacheKey);
212
        $stmt->bindValue(':namespace', $namespace);
213
        $stmt->bindValue(':currentTime', $currentTime);
214
        $stmt->execute();
215
        return $stmt->fetchColumn() !== false;
216
    }
217
218
    /**
219
    * Renews the expiration time of a cache item.
220
    * 
221
    * @param string $cacheKey
222
    * @param string|int $ttl
223
    * @param string $namespace
224
    * @return bool
225
    */
226
    public function renew(string $cacheKey, string|int $ttl, string $namespace = '')
227
    {
228
        $currentTime = date('Y-m-d H:i:s');
229
        if (!$this->hasValidCache($cacheKey, $namespace, $currentTime)) {
230
            return false;
231
        }
232
233
        $query = $this->getRenewExpirationQueryWithDriver();
234
        $stmt = $this->connection->prepare($query);
235
        $stmt->bindValue(':ttl', (int) $ttl, PDO::PARAM_INT);
236
        $stmt->bindValue(':cacheKey', $cacheKey);
237
        $stmt->bindValue(':namespace', $namespace);
238
        $stmt->bindValue(':currentTime', $currentTime);
239
        $stmt->execute();
240
241
        return $stmt->rowCount() > 0;
242
    }
243
244
    /**
245
    * Flushes all cache items from the database.
246
    * 
247
    * @return bool
248
    */
249
    public function flush()
250
    {
251
        return $this->connection->exec("DELETE FROM cacheer_table") !== false;
252
    }
253
254
    /**
255
    * Serializes or unserializes data based on the provided flag.
256
    * 
257
    * @param mixed $data
258
    * @return string
259
    */
260
    private function serialize(mixed $data, bool $serialize = true)
261
    {
262
        return $serialize ? serialize($data) : unserialize($data);
263
    }
264
265
    /**
266
    * Gets the current date and time based on the database driver.
267
    * 
268
    * @param string $driver
269
    * @return string
270
    */
271
    private function getCurrentDateTime(string $driver)
272
    {
273
        return ($driver === 'sqlite') ? "DATETIME('now', 'localtime')" : "NOW()";
274
    }
275
}
276