AbstractAdapter::deleteFromDatabase()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 14
rs 9.4286
cc 1
eloc 9
nc 1
nop 1
1
<?php
2
/**
3
 * Author: Nil Portugués Calderó <[email protected]>
4
 * Date: 4/5/15
5
 * Time: 12:16 PM
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace NilPortugues\Cache\Adapter\SQL;
12
13
use DateTime;
14
use InvalidArgumentException;
15
use NilPortugues\Cache\Adapter\Adapter;
16
use NilPortugues\Cache\Adapter\InMemoryAdapter;
17
use NilPortugues\Cache\Adapter\SQL\Connection\AbstractPDOConnection;
18
use NilPortugues\Cache\CacheAdapter;
19
use PDO;
20
use PDOException;
21
22
/**
23
 * Class AbstractAdapter
24
 * @package NilPortugues\Cache\Adapter\SQL
25
 */
26
abstract class AbstractAdapter extends Adapter implements CacheAdapter
27
{
28
    const CACHE_TTL   = 'cache_ttl';
29
    const CACHE_VALUE = 'cache_value';
30
31
    const TABLE_CACHE_ID    = 'cache_id';
32
    const TABLE_CACHE_VALUE = 'cache_value';
33
    const TABLE_CACHE_TTL   = 'cache_ttl';
34
35
    const QUERY_ID_PLACEHOLDER    = ':id';
36
    const QUERY_VALUE_PLACEHOLDER = ':value';
37
    const QUERY_TTL_PLACEHOLDER   = ':ttl';
38
39
    /**
40
     * @var string
41
     */
42
    protected $connectionClass = '';
43
44
    /**
45
     * @var \PDO
46
     */
47
    protected $connection;
48
49
    /**
50
     * @var string
51
     */
52
    protected $cacheTableName;
53
54
    /**
55
     * @var array
56
     */
57
    protected $parameters = [];
58
59
    /**
60
     * @var array
61
     */
62
    protected $requiredKeys = [
63
        AbstractPDOConnection::USER,
64
        AbstractPDOConnection::PASSWORD,
65
        AbstractPDOConnection::DATABASE,
66
    ];
67
68
    /**
69
     * @param array           $connection
70
     * @param string          $tableName
71
     * @param CacheAdapter    $next
72
     */
73
    public function __construct(array $connection, $tableName, CacheAdapter $next = null)
74
    {
75
        $this->checkMandatoryParameterFields($connection);
76
        $this->parameters     = $connection;
77
        $this->connection     = $this->getConnection();
78
        $this->cacheTableName = $tableName;
79
80
        $this->nextAdapter     = (InMemoryAdapter::getInstance() === $next) ? null : $next;
81
    }
82
83
    /**
84
     * @param array $parameters
85
     *
86
     * @throws InvalidArgumentException
87
     */
88
    protected function checkMandatoryParameterFields(array &$parameters)
89
    {
90
        foreach ($this->requiredKeys as $key) {
91
            if (false === \array_key_exists($key, $parameters)) {
92
                throw new InvalidArgumentException(
93
                    \sprintf("Parameter '%s' is required to set up a connection.", $key)
94
                );
95
            }
96
        }
97
    }
98
99
    /**
100
     * @return object
101
     */
102
    public function getConnection()
103
    {
104
        $class = new \ReflectionClass($this->connectionClass);
105
106
        $parameters = [
107
            $this->parameters,
108
            (!empty($this->parameters[AbstractPDOConnection::USER]))
109
                ? $this->parameters[AbstractPDOConnection::USER] : null,
110
            (!empty($this->parameters[AbstractPDOConnection::PASSWORD]))
111
                ? $this->parameters[AbstractPDOConnection::PASSWORD] : null,
112
            (!empty($this->parameters[AbstractPDOConnection::DRIVER_OPTIONS])) ? : [],
113
        ];
114
115
        $pdo = $class->newInstanceArgs($parameters);
116
        return $pdo->getConnection();
117
    }
118
119
    /**
120
     * Get a value identified by $key.
121
     *
122
     * @param  string $key
123
     *
124
     * @return bool|mixed
125
     */
126
    public function get($key)
127
    {
128
        $this->hit = false;
129
130
        $inMemoryValue = InMemoryAdapter::getInstance()->get($key);
131
        if (InMemoryAdapter::getInstance()->isHit()) {
132
            $this->hit = true;
133
            return $inMemoryValue;
134
        }
135
136
        $result = $this->getFromDatabase($key);
137
138
        if (false === empty($result)) {
139
            $ttl = new DateTime($result[self::CACHE_TTL]);
140
141
            if ($ttl >= new DateTime()) {
142
                $this->hit = true;
143
                $value     = $this->restoreDataStructure($result[self::CACHE_VALUE]);
144
                InMemoryAdapter::getInstance()->set($key, $value, 0);
145
                return $value;
146
            }
147
            $this->delete($key);
148
        }
149
150
        return (null !== $this->nextAdapter) ? $this->nextAdapter->get($key) : null;
151
    }
152
153
    /**
154
     * @param string $key
155
     *
156
     * @return array
157
     */
158 View Code Duplication
    protected function getFromDatabase($key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
    {
160
        try {
161
            $stmt = $this->connection->prepare(
162
                \sprintf(
163
                    'SELECT %s, %s FROM %s WHERE %s = %s',
164
                    self::TABLE_CACHE_VALUE,
165
                    self::TABLE_CACHE_TTL,
166
                    $this->cacheTableName,
167
                    self::TABLE_CACHE_ID,
168
                    self::QUERY_ID_PLACEHOLDER
169
                )
170
            );
171
172
            $stmt->bindParam(self::QUERY_ID_PLACEHOLDER, $key, PDO::PARAM_STR);
173
            $stmt->execute();
174
175
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
176
            return (\is_bool($result)) ? [] : $result;
177
        } catch (PDOException $e) {
178
            return [];
179
        }
180
    }
181
182
    /**
183
     * Delete a value identified by $key.
184
     *
185
     * @param string $key
186
     */
187
    public function delete($key)
188
    {
189
        $this->deleteFromDatabase($key);
190
        $this->deleteChain($key);
191
    }
192
193
    /**
194
     * @param string $key
195
     */
196
    protected function deleteFromDatabase($key)
197
    {
198
        $stmt = $this->connection->prepare(
199
            \sprintf(
200
                'DELETE FROM %s WHERE %s = %s',
201
                $this->cacheTableName,
202
                self::TABLE_CACHE_ID,
203
                self::QUERY_ID_PLACEHOLDER
204
            )
205
        );
206
207
        $stmt->bindParam(self::QUERY_ID_PLACEHOLDER, $key, PDO::PARAM_STR);
208
        $stmt->execute();
209
    }
210
211
    /**
212
     * Set a value identified by $key and with an optional $ttl.
213
     *
214
     * @param string $key
215
     * @param mixed  $value
216
     * @param int    $ttl
217
     *
218
     * @return $this
219
     */
220
    public function set($key, $value, $ttl = 0)
221
    {
222
        $this->insertToDatabase($key, $value, $ttl);
223
        $this->setChain($key, $value, $ttl);
224
225
        return $this;
226
    }
227
228
229
    /**
230
     * @param $ttl
231
     *
232
     * @return int
233
     */
234 View Code Duplication
    protected function getCalculatedTtl($ttl)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
235
    {
236
        $calculatedTtl = \strtotime(\sprintf('now +%s seconds', $ttl));
237
        if (0 == $ttl) {
238
            $calculatedTtl = \strtotime('now +10 years');
239
        }
240
        return $calculatedTtl;
241
    }
242
243
    /**
244
     * @param     $key
245
     * @param     $value
246
     * @param int $ttl
247
     *
248
     * @return $this
249
     */
250
    protected function insertToDatabase($key, $value, $ttl = 0)
251
    {
252
        $value = $this->storageDataStructure($value);
253
254
        $calculatedTtl = $this->fromDefaultTtl($ttl);
255
        $calculatedTtl = new DateTime(\date('Y-m-d H:i:s', $this->getCalculatedTtl($calculatedTtl)));
256
257
        $databaseValue = $this->getFromDatabase($key);
258
        if (false === empty($databaseValue)) {
259
            $this->updateToDatabase($key, $value, $calculatedTtl);
260
            return $this;
261
        }
262
263
        $stmt = $this->connection->prepare(
264
            \sprintf(
265
                'INSERT INTO %s(%s, %s, %s) VALUES(%s, %s, %s)',
266
                $this->cacheTableName,
267
                self::TABLE_CACHE_ID,
268
                self::TABLE_CACHE_VALUE,
269
                self::TABLE_CACHE_TTL,
270
                self::QUERY_ID_PLACEHOLDER,
271
                self::QUERY_VALUE_PLACEHOLDER,
272
                self::QUERY_TTL_PLACEHOLDER
273
            )
274
        );
275
276
        $stmt->bindParam(self::QUERY_ID_PLACEHOLDER, $key, PDO::PARAM_STR);
277
        $stmt->bindParam(self::QUERY_VALUE_PLACEHOLDER, $value, PDO::PARAM_STR);
278
        $calculatedTtl = $calculatedTtl->format('Y-m-d H:i:s');
279
        $stmt->bindParam(self::QUERY_TTL_PLACEHOLDER, $calculatedTtl, PDO::PARAM_STR);
280
        $stmt->execute();
281
282
        return $this;
283
    }
284
285
    /**
286
     * @param $key
287
     * @param $value
288
     * @param DateTime $ttl
289
     * @return $this
290
     */
291
    protected function updateToDatabase($key, $value, DateTime $ttl)
292
    {
293
        $stmt = $this->connection->prepare(
294
            \sprintf(
295
                'UPDATE %s SET %s = %s, %s = %s, %s = %s WHERE %s = %s',
296
                $this->cacheTableName,
297
                self::TABLE_CACHE_ID,
298
                self::QUERY_ID_PLACEHOLDER,
299
                self::TABLE_CACHE_VALUE,
300
                self::QUERY_VALUE_PLACEHOLDER,
301
                self::TABLE_CACHE_TTL,
302
                self::QUERY_TTL_PLACEHOLDER,
303
                self::TABLE_CACHE_ID,
304
                self::QUERY_ID_PLACEHOLDER
305
            )
306
        );
307
308
        $stmt->bindParam(self::QUERY_ID_PLACEHOLDER, $key, PDO::PARAM_STR);
309
        $stmt->bindParam(self::QUERY_VALUE_PLACEHOLDER, $value, PDO::PARAM_STR);
310
        $ttl = $ttl->format('Y-m-d H:i:s');
311
        $stmt->bindParam(self::QUERY_TTL_PLACEHOLDER, $ttl, PDO::PARAM_STR);
312
        $stmt->execute();
313
314
        return $this;
315
    }
316
317
    /**
318
     * Checks the availability of the cache service.
319
     *
320
     * @return bool
321
     */
322
    public function isAvailable()
323
    {
324
        $available = true;
325
326
        try {
327
            $this->connection->exec(
328
                \sprintf(
329
                    'SELECT %s FROM %s LIMIT 1',
330
                    self::TABLE_CACHE_ID,
331
                    $this->cacheTableName
332
                )
333
            );
334
        } catch (PDOException $e) {
335
            $available = false;
336
        }
337
338
        return $available;
339
    }
340
341
    /**
342
     * Clears all expired values from cache.
343
     *
344
     * @return mixed
345
     */
346
    public function clear()
347
    {
348
        $this->clearFromDatabase();
349
        $this->clearChain();
350
    }
351
352
    /**
353
     *
354
     */
355
    protected function clearFromDatabase()
356
    {
357
        $stmt = $this->connection->prepare(
358
            \sprintf(
359
                'DELETE FROM %s WHERE %s < %s',
360
                $this->cacheTableName,
361
                self::TABLE_CACHE_TTL,
362
                self::QUERY_TTL_PLACEHOLDER
363
            )
364
        );
365
366
        $now = new DateTime();
367
        $now = $now->format('Y-m-d H:i:s');
368
        $stmt->bindParam(self::QUERY_TTL_PLACEHOLDER, $now, PDO::PARAM_STR);
369
        $stmt->execute();
370
    }
371
372
    /**
373
     * Clears all values from the cache.
374
     *
375
     * @return mixed
376
     */
377
    public function drop()
378
    {
379
        $this->dropFromDatabase();
380
        $this->dropChain();
381
    }
382
383
    /**
384
     *
385
     */
386
    protected function dropFromDatabase()
387
    {
388
        $stmt = $this->connection->prepare(
389
            \sprintf('DELETE FROM %s', $this->cacheTableName)
390
        );
391
392
        $stmt->execute();
393
    }
394
}
395