Completed
Push — master ( 212129...5a5e00 )
by Marco
12s
created

SQLite3Cache::doSave()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 9
cts 9
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 1
nop 3
crap 2
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\Common\Cache;
21
22
use SQLite3;
23
use SQLite3Result;
24
25
/**
26
 * SQLite3 cache provider.
27
 *
28
 * @since  1.4
29
 * @author Jake Bell <[email protected]>
30
 */
31
class SQLite3Cache extends CacheProvider
32
{
33
    /**
34
     * The ID field will store the cache key.
35
     */
36
    const ID_FIELD = 'k';
37
38
    /**
39
     * The data field will store the serialized PHP value.
40
     */
41
    const DATA_FIELD = 'd';
42
43
    /**
44
     * The expiration field will store a date value indicating when the
45
     * cache entry should expire.
46
     */
47
    const EXPIRATION_FIELD = 'e';
48
49
    /**
50
     * @var SQLite3
51
     */
52
    private $sqlite;
53
54
    /**
55
     * @var string
56
     */
57
    private $table;
58
59
    /**
60
     * Constructor.
61
     *
62
     * Calling the constructor will ensure that the database file and table
63
     * exist and will create both if they don't.
64
     *
65
     * @param SQLite3 $sqlite
66
     * @param string $table
67
     */
68 78
    public function __construct(SQLite3 $sqlite, $table)
69
    {
70 78
        $this->sqlite = $sqlite;
71 78
        $this->table  = (string) $table;
72
73 78
        $this->ensureTableExists();
74 78
    }
75
76 78
    private function ensureTableExists() : void
77
    {
78 78
        $this->sqlite->exec(
79 78
            sprintf(
80 78
                'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)',
81 78
                $this->table,
82 78
                static::ID_FIELD,
83 78
                static::DATA_FIELD,
84 78
                static::EXPIRATION_FIELD
85
            )
86
        );
87 78
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92 76
    protected function doFetch($id)
93
    {
94 76
        $item = $this->findById($id);
95
96 76
        if ( ! $item) {
97 76
            return false;
98
        }
99
100 65
        return unserialize($item[self::DATA_FIELD]);
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 73
    protected function doContains($id)
107
    {
108 73
        return null !== $this->findById($id, false);
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 74
    protected function doSave($id, $data, $lifeTime = 0)
115
    {
116 74
        $statement = $this->sqlite->prepare(sprintf(
117 74
            'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)',
118 74
            $this->table,
119 74
            implode(',', $this->getFields())
120
        ));
121
122 74
        $statement->bindValue(':id', $id);
123 74
        $statement->bindValue(':data', serialize($data), SQLITE3_BLOB);
124 74
        $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null);
125
126 74
        return $statement->execute() instanceof SQLite3Result;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132 46
    protected function doDelete($id)
133
    {
134 46
        list($idField) = $this->getFields();
135
136 46
        $statement = $this->sqlite->prepare(sprintf(
137 46
            'DELETE FROM %s WHERE %s = :id',
138 46
            $this->table,
139 46
            $idField
140
        ));
141
142 46
        $statement->bindValue(':id', $id);
143
144 46
        return $statement->execute() instanceof SQLite3Result;
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 2
    protected function doFlush()
151
    {
152 2
        return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table));
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158 1
    protected function doGetStats()
159
    {
160
        // no-op.
161 1
    }
162
163
    /**
164
     * Find a single row by ID.
165
     *
166
     * @param mixed $id
167
     * @param bool $includeData
168
     *
169
     * @return array|null
170
     */
171 76
    private function findById($id, bool $includeData = true) : ?array
172
    {
173 76
        list($idField) = $fields = $this->getFields();
174
175 76
        if ( ! $includeData) {
176 73
            $key = array_search(static::DATA_FIELD, $fields);
177 73
            unset($fields[$key]);
178
        }
179
180 76
        $statement = $this->sqlite->prepare(sprintf(
181 76
            'SELECT %s FROM %s WHERE %s = :id LIMIT 1',
182 76
            implode(',', $fields),
183 76
            $this->table,
184 76
            $idField
185
        ));
186
187 76
        $statement->bindValue(':id', $id, SQLITE3_TEXT);
188
189 76
        $item = $statement->execute()->fetchArray(SQLITE3_ASSOC);
190
191 76
        if ($item === false) {
192 76
            return null;
193
        }
194
195 70
        if ($this->isExpired($item)) {
196 1
            $this->doDelete($id);
197
198 1
            return null;
199
        }
200
201 70
        return $item;
202
    }
203
204
    /**
205
     * Gets an array of the fields in our table.
206
     *
207
     * @return array
208
     */
209 76
    private function getFields() : array
210
    {
211 76
        return [static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD];
212
    }
213
214
    /**
215
     * Check if the item is expired.
216
     *
217
     * @param array $item
218
     *
219
     * @return bool
220
     */
221 70
    private function isExpired(array $item) : bool
222
    {
223 70
        return isset($item[static::EXPIRATION_FIELD]) &&
224 70
            $item[self::EXPIRATION_FIELD] !== null &&
225 70
            $item[self::EXPIRATION_FIELD] < time();
226
    }
227
}
228