getDeleteQueryWithDriver()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 4
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
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 ?PDO $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): bool
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(
0 ignored issues
show
Bug introduced by
The method prepare() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

49
        /** @scrutinizer ignore-call */ 
50
        $stmt = $this->connection->prepare(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
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 = ''): mixed
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 string $namespace
90
     * @return array
91
     */
92
    public function getAll(string $namespace = ''): array
93
    {
94
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
95
        $nowFunction = $this->getCurrentDateTime($driver);
96
97
        $stmt = $this->connection->prepare(
98
            "SELECT cacheKey, cacheData FROM cacheer_table 
99
            WHERE cacheNamespace = :namespace AND expirationTime > $nowFunction"
100
        );
101
        $stmt->bindValue(':namespace', $namespace);
102
        $stmt->execute();
103
104
        $results = [];
105
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
106
            $results[$row['cacheKey']] = $this->serialize($row['cacheData'], false);
107
        }
108
        return $results;
109
    }
110
111
    /**
112
    * Get Update query based on the database driver.
113
    *
114
    * @return string
115
    */
116
    private function getUpdateQueryWithDriver(): string
117
    {
118
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
119
        if ($driver === 'mysql' || $driver === 'mariadb') {
120
            return "UPDATE cacheer_table SET cacheData = :cacheData, cacheNamespace = :namespace WHERE cacheKey = :cacheKey LIMIT 1";
121
        }
122
        return "UPDATE cacheer_table SET cacheData = :cacheData, cacheNamespace = :namespace WHERE cacheKey = :cacheKey";
123
    }
124
125
    /**
126
    * Get Delete query based on the database driver.
127
    * 
128
    * @return string
129
    */
130
    private function getDeleteQueryWithDriver(): string
131
    {
132
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
133
        if ($driver === 'mysql' || $driver === 'mariadb') {
134
            return "DELETE FROM cacheer_table WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace LIMIT 1";
135
        }
136
        return "DELETE FROM cacheer_table WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace";
137
    }
138
139
    /**
140
    * Updates an existing cache item in the database.
141
    * 
142
    * @param string $cacheKey
143
    * @param mixed  $cacheData
144
    * @param string $namespace
145
    * @return bool
146
    */
147
    public function update(string $cacheKey, mixed $cacheData, string $namespace = ''): bool
148
    {
149
        $query = $this->getUpdateQueryWithDriver();
150
        $stmt = $this->connection->prepare($query);
151
        $stmt->bindValue(':cacheData', $this->serialize($cacheData));
152
        $stmt->bindValue(':namespace', $namespace);
153
        $stmt->bindValue(':cacheKey', $cacheKey);
154
        $stmt->execute();
155
156
        return $stmt->rowCount() > 0;
157
    }
158
159
    /**
160
    * Clears a specific cache item from the database.
161
    * 
162
    * @param string $cacheKey
163
    * @param string $namespace
164
    * @return bool
165
    */
166
    public function clear(string $cacheKey, string $namespace = ''): bool
167
    {
168
        $query = $this->getDeleteQueryWithDriver();
169
        $stmt = $this->connection->prepare($query);
170
        $stmt->bindValue(':cacheKey', $cacheKey);
171
        $stmt->bindValue(':namespace', $namespace);
172
        $stmt->execute();
173
174
        return $stmt->rowCount() > 0;
175
    }
176
177
    /**
178
    * Gets the query to renew the expiration time of a cache item based on the database driver.
179
    *  
180
    * @return string
181
    */
182
    private function getRenewExpirationQueryWithDriver(): string
183
    {
184
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
185
        if ($driver === 'sqlite') {
186
            return "UPDATE cacheer_table
187
                    SET expirationTime = DATETIME(expirationTime, '+' || :ttl || ' seconds')
188
                    WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime";
189
        }
190
        return "UPDATE cacheer_table
191
                SET expirationTime = DATE_ADD(expirationTime, INTERVAL :ttl SECOND)
192
                WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime";
193
    }
194
195
    /**
196
    * Checks if a cache item is valid based on its key, namespace, and current time.
197
    * 
198
    * @param string $cacheKey
199
    * @param string $namespace
200
    * @param string $currentTime
201
    * @return bool
202
    */
203
    private function hasValidCache(string $cacheKey, string $namespace, string $currentTime): bool
204
    {
205
        $stmt = $this->connection->prepare(
206
            "SELECT 1 FROM cacheer_table 
207
            WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime
208
            LIMIT 1"
209
        );
210
        $stmt->bindValue(':cacheKey', $cacheKey);
211
        $stmt->bindValue(':namespace', $namespace);
212
        $stmt->bindValue(':currentTime', $currentTime);
213
        $stmt->execute();
214
        return $stmt->fetchColumn() !== false;
215
    }
216
217
    /**
218
    * Renews the expiration time of a cache item.
219
    * 
220
    * @param string $cacheKey
221
    * @param string|int $ttl
222
    * @param string $namespace
223
    * @return bool
224
    */
225
    public function renew(string $cacheKey, string|int $ttl, string $namespace = ''): bool
226
    {
227
        $currentTime = date('Y-m-d H:i:s');
228
        if (!$this->hasValidCache($cacheKey, $namespace, $currentTime)) {
229
            return false;
230
        }
231
232
        $query = $this->getRenewExpirationQueryWithDriver();
233
        $stmt = $this->connection->prepare($query);
234
        $stmt->bindValue(':ttl', (int) $ttl, PDO::PARAM_INT);
235
        $stmt->bindValue(':cacheKey', $cacheKey);
236
        $stmt->bindValue(':namespace', $namespace);
237
        $stmt->bindValue(':currentTime', $currentTime);
238
        $stmt->execute();
239
240
        return $stmt->rowCount() > 0;
241
    }
242
243
    /**
244
    * Flushes all cache items from the database.
245
    * 
246
    * @return bool
247
    */
248
    public function flush(): bool
249
    {
250
        return $this->connection->exec("DELETE FROM cacheer_table") !== false;
251
    }
252
253
    /**
254
     * Serializes or unserializes data based on the provided flag.
255
     *
256
     * @param mixed $data
257
     * @param bool $serialize
258
     * @return mixed
259
     */
260
    private function serialize(mixed $data, bool $serialize = true): mixed
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): string
272
    {
273
        return ($driver === 'sqlite') ? "DATETIME('now', 'localtime')" : "NOW()";
274
    }
275
}
276