Passed
Pull Request — main (#24)
by Sílvio
03:29
created

getUpdateQueryWithDriver()   A

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 $connection = null;
18
19
    public function __construct()
20
    {
21
        $this->connection = Connect::getInstance();
22
    }
23
24
25
    /**
26
     * @param string $cacheKey
27
     * @param mixed  $cacheData
28
     * @param string $namespace
29
     * @param string|int $ttl
30
     * @return bool
31
     */
32
    public function store(string $cacheKey, mixed $cacheData, string $namespace, string|int $ttl = 3600)
33
    {
34
35
        if(!empty($this->retrieve($cacheKey, $namespace))) {
36
            $this->update($cacheKey, $cacheData, $namespace);
37
        }
38
39
        $expirationTime = date('Y-m-d H:i:s', time() + $ttl);
40
        $createdAt = date('Y-m-d H:i:s');
41
42
        $stmt = $this->connection->prepare(
43
            "INSERT INTO cacheer_table (cacheKey, cacheData, cacheNamespace, expirationTime, created_at) 
44
            VALUES (:cacheKey, :cacheData, :namespace, :expirationTime, :createdAt)"
45
        );
46
        $stmt->bindValue(':cacheKey', $cacheKey);
47
        $stmt->bindValue(':cacheData', $this->serialize($cacheData));
48
        $stmt->bindValue(':namespace', $namespace);
49
        $stmt->bindValue(':expirationTime', $expirationTime);
50
        $stmt->bindValue(':createdAt', $createdAt);
51
52
        return $stmt->execute() && $stmt->rowCount() > 0;
53
    }
54
55
    /**
56
    * @param string $cacheKey
57
    * @param string $namespace
58
    * @return mixed
59
    */
60
    public function retrieve(string $cacheKey, string $namespace = '')
61
    {
62
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
63
        $nowFunction = $this->getCurrentDateTime($driver);
64
65
        $stmt = $this->connection->prepare(
66
            "SELECT cacheData FROM cacheer_table 
67
            WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > $nowFunction
68
            LIMIT 1"
69
        );
70
        $stmt->bindValue(':cacheKey', $cacheKey);
71
        $stmt->bindValue(':namespace', $namespace);
72
        $stmt->execute();
73
74
        $data = $stmt->fetch(PDO::FETCH_ASSOC);
75
        return (!empty($data)) ? $this->serialize($data['cacheData'], false) : null;
76
    }
77
78
    /**
79
     * @return string
80
     */
81
    private function getUpdateQueryWithDriver()
82
    {
83
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
84
        if ($driver === 'mysql' || $driver === 'mariadb') {
85
            return "UPDATE cacheer_table SET cacheData = :cacheData, cacheNamespace = :namespace WHERE cacheKey = :cacheKey LIMIT 1";
86
        }
87
        return "UPDATE cacheer_table SET cacheData = :cacheData, cacheNamespace = :namespace WHERE cacheKey = :cacheKey";
88
    }
89
90
    /**
91
     * @return string
92
     */
93
    private function getDeleteQueryWithDriver()
94
    {
95
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
96
        if ($driver === 'mysql' || $driver === 'mariadb') {
97
            return "DELETE FROM cacheer_table WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace LIMIT 1";
98
        }
99
        return "DELETE FROM cacheer_table WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace";
100
    }
101
102
    /**
103
     * @param string $cacheKey
104
     * @param mixed  $cacheData
105
     * @param string $namespace
106
     * @return bool
107
     */
108
    public function update(string $cacheKey, mixed $cacheData, string $namespace = '')
109
    {
110
        $query = $this->getUpdateQueryWithDriver();
111
        $stmt = $this->connection->prepare($query);
112
        $stmt->bindValue(':cacheData', $this->serialize($cacheData));
113
        $stmt->bindValue(':namespace', $namespace);
114
        $stmt->bindValue(':cacheKey', $cacheKey);
115
        $stmt->execute();
116
117
        return $stmt->rowCount() > 0;
118
    }
119
120
    /**
121
     * @param string $cacheKey
122
     * @param string $namespace
123
     * @return bool
124
     */
125
    public function clear(string $cacheKey, string $namespace = '')
126
    {
127
        $query = $this->getDeleteQueryWithDriver();
128
        $stmt = $this->connection->prepare($query);
129
        $stmt->bindValue(':cacheKey', $cacheKey);
130
        $stmt->bindValue(':namespace', $namespace);
131
        $stmt->execute();
132
133
        return $stmt->rowCount() > 0;
134
    }
135
136
    /**
137
     * @return string
138
     */
139
    private function getRenewExpirationQueryWithDriver(): string
140
    {
141
        $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
142
        if ($driver === 'sqlite') {
143
            return "UPDATE cacheer_table
144
                    SET expirationTime = DATETIME(expirationTime, '+' || :ttl || ' seconds')
145
                    WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime";
146
        }
147
        return "UPDATE cacheer_table
148
                SET expirationTime = DATE_ADD(expirationTime, INTERVAL :ttl SECOND)
149
                WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime";
150
    }
151
152
    /**
153
     * @param string $cacheKey
154
     * @param string $namespace
155
     * @param string $currentTime
156
     * @return bool
157
     */
158
    private function hasValidCache(string $cacheKey, string $namespace, string $currentTime): bool
159
    {
160
        $stmt = $this->connection->prepare(
161
            "SELECT 1 FROM cacheer_table 
162
            WHERE cacheKey = :cacheKey AND cacheNamespace = :namespace AND expirationTime > :currentTime
163
            LIMIT 1"
164
        );
165
        $stmt->bindValue(':cacheKey', $cacheKey);
166
        $stmt->bindValue(':namespace', $namespace);
167
        $stmt->bindValue(':currentTime', $currentTime);
168
        $stmt->execute();
169
        return $stmt->fetchColumn() !== false;
170
    }
171
172
    /**
173
    * @param string $cacheKey
174
    * @param string|int $ttl
175
    * @param string $namespace
176
    * @return bool
177
    */
178
    public function renew(string $cacheKey, string|int $ttl, string $namespace = '')
179
    {
180
        $currentTime = date('Y-m-d H:i:s');
181
        if (!$this->hasValidCache($cacheKey, $namespace, $currentTime)) {
182
            return false;
183
        }
184
185
        $query = $this->getRenewExpirationQueryWithDriver();
186
        $stmt = $this->connection->prepare($query);
187
        $stmt->bindValue(':ttl', (int) $ttl, PDO::PARAM_INT);
188
        $stmt->bindValue(':cacheKey', $cacheKey);
189
        $stmt->bindValue(':namespace', $namespace);
190
        $stmt->bindValue(':currentTime', $currentTime);
191
        $stmt->execute();
192
193
        return $stmt->rowCount() > 0;
194
    }
195
196
    /**
197
     * @return bool
198
     */
199
    public function flush()
200
    {
201
        return $this->connection->exec("DELETE FROM cacheer_table") !== false;
202
    }
203
204
    /**
205
     * @param mixed $data
206
     * @return string
207
     */
208
    private function serialize(mixed $data, bool $serialize = true)
209
    {
210
        return $serialize ? serialize($data) : unserialize($data);
211
    }
212
213
    /**
214
    * @param string $driver
215
    * @return string
216
    */
217
    private function getCurrentDateTime(string $driver)
218
    {
219
        return ($driver === 'sqlite') ? "DATETIME('now', 'localtime')" : "NOW()";
220
    }
221
}
222