Completed
Push — master ( 8f6a24...fe68e4 )
by Marco
63:35 queued 61:07
created

SQLite3Cache::doSave()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 14
c 0
b 0
f 0
ccs 0
cts 12
cp 0
rs 9.4285
cc 2
eloc 9
nc 1
nop 3
crap 6
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
    public function __construct(SQLite3 $sqlite, $table)
69
    {
70
        $this->sqlite = $sqlite;
71
        $this->table  = (string) $table;
72
73
        list($id, $data, $exp) = $this->getFields();
74
75
        return $this->sqlite->exec(sprintf(
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
76
            'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)',
77
            $table,
78
            $id,
79
            $data,
80
            $exp
81
        ));
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    protected function doFetch($id)
88
    {
89
        $item = $this->findById($id);
90
91
        if (!$item) {
92
            return false;
93
        }
94
95
        return unserialize($item[self::DATA_FIELD]);
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    protected function doContains($id)
102
    {
103
        return null !== $this->findById($id, false);
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    protected function doSave($id, $data, $lifeTime = 0)
110
    {
111
        $statement = $this->sqlite->prepare(sprintf(
112
            'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)',
113
            $this->table,
114
            implode(',', $this->getFields())
115
        ));
116
117
        $statement->bindValue(':id', $id);
118
        $statement->bindValue(':data', serialize($data), SQLITE3_BLOB);
119
        $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null);
120
121
        return $statement->execute() instanceof SQLite3Result;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    protected function doDelete($id)
128
    {
129
        list($idField) = $this->getFields();
130
131
        $statement = $this->sqlite->prepare(sprintf(
132
            'DELETE FROM %s WHERE %s = :id',
133
            $this->table,
134
            $idField
135
        ));
136
137
        $statement->bindValue(':id', $id);
138
139
        return $statement->execute() instanceof SQLite3Result;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    protected function doFlush()
146
    {
147
        return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table));
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    protected function doGetStats()
154
    {
155
        // no-op.
156
    }
157
158
    /**
159
     * Find a single row by ID.
160
     *
161
     * @param mixed $id
162
     * @param bool $includeData
163
     *
164
     * @return array|null
165
     */
166
    private function findById($id, $includeData = true)
167
    {
168
        list($idField) = $fields = $this->getFields();
169
170
        if (!$includeData) {
171
            $key = array_search(static::DATA_FIELD, $fields);
172
            unset($fields[$key]);
173
        }
174
175
        $statement = $this->sqlite->prepare(sprintf(
176
            'SELECT %s FROM %s WHERE %s = :id LIMIT 1',
177
            implode(',', $fields),
178
            $this->table,
179
            $idField
180
        ));
181
182
        $statement->bindValue(':id', $id, SQLITE3_TEXT);
183
184
        $item = $statement->execute()->fetchArray(SQLITE3_ASSOC);
185
186
        if ($item === false) {
187
            return null;
188
        }
189
190
        if ($this->isExpired($item)) {
191
            $this->doDelete($id);
192
193
            return null;
194
        }
195
196
        return $item;
197
    }
198
199
    /**
200
     * Gets an array of the fields in our table.
201
     *
202
     * @return array
203
     */
204
    private function getFields()
205
    {
206
        return [static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD];
207
    }
208
209
    /**
210
     * Check if the item is expired.
211
     *
212
     * @param array $item
213
     *
214
     * @return bool
215
     */
216
    private function isExpired(array $item)
217
    {
218
        return isset($item[static::EXPIRATION_FIELD]) &&
219
            $item[self::EXPIRATION_FIELD] !== null &&
220
            $item[self::EXPIRATION_FIELD] < time();
221
    }
222
}
223