SQLite3Cache::doFlush()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Doctrine\Common\Cache;
4
5
use SQLite3;
6
use SQLite3Result;
7
use const SQLITE3_ASSOC;
8
use const SQLITE3_BLOB;
9
use const SQLITE3_TEXT;
10
use function array_search;
11
use function implode;
12
use function serialize;
13
use function sprintf;
14
use function time;
15
use function unserialize;
16
17
/**
18
 * SQLite3 cache provider.
19
 */
20
class SQLite3Cache extends CacheProvider
21
{
22
    /**
23
     * The ID field will store the cache key.
24
     */
25
    public const ID_FIELD = 'k';
26
27
    /**
28
     * The data field will store the serialized PHP value.
29
     */
30
    public const DATA_FIELD = 'd';
31
32
    /**
33
     * The expiration field will store a date value indicating when the
34
     * cache entry should expire.
35
     */
36
    public const EXPIRATION_FIELD = 'e';
37
38
    /** @var SQLite3 */
39
    private $sqlite;
40
41
    /** @var string */
42
    private $table;
43
44
    /**
45
     * Calling the constructor will ensure that the database file and table
46
     * exist and will create both if they don't.
47
     *
48
     * @param string $table
49
     */
50 78
    public function __construct(SQLite3 $sqlite, $table)
51
    {
52 78
        $this->sqlite = $sqlite;
53 78
        $this->table  = (string) $table;
54
55 78
        $this->ensureTableExists();
56 78
    }
57
58 78
    private function ensureTableExists() : void
59
    {
60 78
        $this->sqlite->exec(
61 78
            sprintf(
62 78
                'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)',
63 78
                $this->table,
64 78
                static::ID_FIELD,
65 78
                static::DATA_FIELD,
66 78
                static::EXPIRATION_FIELD
67
            )
68
        );
69 78
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74 76
    protected function doFetch($id)
75
    {
76 76
        $item = $this->findById($id);
77
78 76
        if (! $item) {
79 76
            return false;
80
        }
81
82 65
        return unserialize($item[self::DATA_FIELD]);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 73
    protected function doContains($id)
89
    {
90 73
        return $this->findById($id, false) !== null;
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 74
    protected function doSave($id, $data, $lifeTime = 0)
97
    {
98 74
        $statement = $this->sqlite->prepare(sprintf(
99 74
            'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)',
100 74
            $this->table,
101 74
            implode(',', $this->getFields())
102
        ));
103
104 74
        $statement->bindValue(':id', $id);
105 74
        $statement->bindValue(':data', serialize($data), SQLITE3_BLOB);
106 74
        $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null);
107
108 74
        return $statement->execute() instanceof SQLite3Result;
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 46
    protected function doDelete($id)
115
    {
116 46
        [$idField] = $this->getFields();
117
118 46
        $statement = $this->sqlite->prepare(sprintf(
119 46
            'DELETE FROM %s WHERE %s = :id',
120 46
            $this->table,
121 46
            $idField
122
        ));
123
124 46
        $statement->bindValue(':id', $id);
125
126 46
        return $statement->execute() instanceof SQLite3Result;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132 2
    protected function doFlush()
133
    {
134 2
        return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table));
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140 1
    protected function doGetStats()
141
    {
142
        // no-op.
143 1
    }
144
145
    /**
146
     * Find a single row by ID.
147
     *
148
     * @param mixed $id
149
     *
150
     * @return array|null
151
     */
152 76
    private function findById($id, bool $includeData = true) : ?array
153
    {
154 76
        [$idField] = $fields = $this->getFields();
155
156 76
        if (! $includeData) {
157 73
            $key = array_search(static::DATA_FIELD, $fields);
158 73
            unset($fields[$key]);
159
        }
160
161 76
        $statement = $this->sqlite->prepare(sprintf(
162 76
            'SELECT %s FROM %s WHERE %s = :id LIMIT 1',
163 76
            implode(',', $fields),
164 76
            $this->table,
165 76
            $idField
166
        ));
167
168 76
        $statement->bindValue(':id', $id, SQLITE3_TEXT);
169
170 76
        $item = $statement->execute()->fetchArray(SQLITE3_ASSOC);
171
172 76
        if ($item === false) {
173 76
            return null;
174
        }
175
176 70
        if ($this->isExpired($item)) {
177 1
            $this->doDelete($id);
178
179 1
            return null;
180
        }
181
182 70
        return $item;
183
    }
184
185
    /**
186
     * Gets an array of the fields in our table.
187
     *
188
     * @return array
189
     */
190 76
    private function getFields() : array
191
    {
192 76
        return [static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD];
193
    }
194
195
    /**
196
     * Check if the item is expired.
197
     *
198
     * @param array $item
199
     */
200 70
    private function isExpired(array $item) : bool
201
    {
202 70
        return isset($item[static::EXPIRATION_FIELD]) &&
203 70
            $item[self::EXPIRATION_FIELD] !== null &&
204 70
            $item[self::EXPIRATION_FIELD] < time();
205
    }
206
}
207