Issues (3083)

smarty/smarty/demo/plugins/cacheresource.pdo.php (2 issues)

1
<?php
2
3
/**
4
 * PDO Cache Handler
5
 * Allows you to store Smarty Cache files into your db.
6
 * Example table :
7
 * CREATE TABLE `smarty_cache` (
8
 * `id` char(40) NOT NULL COMMENT 'sha1 hash',
9
 * `name` varchar(250) NOT NULL,
10
 * `cache_id` varchar(250) DEFAULT NULL,
11
 * `compile_id` varchar(250) DEFAULT NULL,
12
 * `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
13
 * `expire` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
14
 * `content` mediumblob NOT NULL,
15
 * PRIMARY KEY (`id`),
16
 * KEY `name` (`name`),
17
 * KEY `cache_id` (`cache_id`),
18
 * KEY `compile_id` (`compile_id`),
19
 * KEY `modified` (`modified`),
20
 * KEY `expire` (`expire`)
21
 * ) ENGINE=InnoDB
22
 * Example usage :
23
 *      $cnx    =   new PDO("mysql:host=localhost;dbname=mydb", "username", "password");
24
 *      $smarty->setCachingType('pdo');
25
 *      $smarty->loadPlugin('Smarty_CacheResource_Pdo');
26
 *      $smarty->registerCacheResource('pdo', new Smarty_CacheResource_Pdo($cnx, 'smarty_cache'));
27
 *
28
 * @author Beno!t POLASZEK - 2014
29
 */
30
class Smarty_CacheResource_Pdo extends Smarty_CacheResource_Custom
31
{
32
    /**
33
     * @var string[]
34
     */
35
    protected $fetchStatements = array('default' => 'SELECT %2$s
36
                                                                                    FROM %1$s 
37
                                                                                    WHERE 1 
38
                                                                                    AND id          = :id 
39
                                                                                    AND cache_id    IS NULL 
40
                                                                                    AND compile_id  IS NULL',
41
                                       'withCacheId' => 'SELECT %2$s
42
                                                                                FROM %1$s 
43
                                                                                WHERE 1 
44
                                                                                AND id          = :id 
45
                                                                                AND cache_id    = :cache_id 
46
                                                                                AND compile_id  IS NULL',
47
                                       'withCompileId' => 'SELECT %2$s
48
                                                                                FROM %1$s 
49
                                                                                WHERE 1 
50
                                                                                AND id          = :id 
51
                                                                                AND compile_id  = :compile_id 
52
                                                                                AND cache_id    IS NULL',
53
                                       'withCacheIdAndCompileId' => 'SELECT %2$s
54
                                                                                FROM %1$s 
55
                                                                                WHERE 1 
56
                                                                                AND id          = :id 
57
                                                                                AND cache_id    = :cache_id 
58
                                                                                AND compile_id  = :compile_id');
59
60
    /**
61
     * @var string
62
     */
63
    protected $insertStatement = 'INSERT INTO %s
64
65
                                                SET id          =   :id, 
66
                                                    name        =   :name, 
67
                                                    cache_id    =   :cache_id, 
68
                                                    compile_id  =   :compile_id, 
69
                                                    modified    =   CURRENT_TIMESTAMP, 
70
                                                    expire      =   DATE_ADD(CURRENT_TIMESTAMP, INTERVAL :expire SECOND), 
71
                                                    content     =   :content 
72
73
                                                ON DUPLICATE KEY UPDATE 
74
                                                    name        =   :name, 
75
                                                    cache_id    =   :cache_id, 
76
                                                    compile_id  =   :compile_id, 
77
                                                    modified    =   CURRENT_TIMESTAMP, 
78
                                                    expire      =   DATE_ADD(CURRENT_TIMESTAMP, INTERVAL :expire SECOND), 
79
                                                    content     =   :content';
80
81
    /**
82
     * @var string
83
     */
84
    protected $deleteStatement = 'DELETE FROM %1$s WHERE %2$s';
85
86
    /**
87
     * @var string
88
     */
89
    protected $truncateStatement = 'TRUNCATE TABLE %s';
90
91
    /**
92
     * @var string
93
     */
94
    protected $fetchColumns = 'modified, content';
95
96
    /**
97
     * @var string
98
     */
99
    protected $fetchTimestampColumns = 'modified';
100
101
    /**
102
     * @var \PDO
103
     */
104
    protected $pdo;
105
106
    /**
107
     * @var
108
     */
109
    protected $table;
110
111
    /**
112
     * @var null
113
     */
114
    protected $database;
115
116
    /**
117
     * Constructor
118
     *
119
     * @param PDO    $pdo      PDO : active connection
120
     * @param string $table    : table (or view) name
121
     * @param string $database : optional - if table is located in another db
122
     *
123
     * @throws \SmartyException
124
     */
125
    public function __construct(PDO $pdo, $table, $database = null)
126
    {
127
        if (is_null($table)) {
128
            throw new SmartyException("Table name for caching can't be null");
129
        }
130
        $this->pdo = $pdo;
131
        $this->table = $table;
132
        $this->database = $database;
0 ignored issues
show
Documentation Bug introduced by
It seems like $database can also be of type string. However, the property $database is declared as type null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
133
        $this->fillStatementsWithTableName();
134
    }
135
136
    /**
137
     * Fills the table name into the statements.
138
     *
139
     * @return $this Current Instance
140
     * @access protected
141
     */
142
    protected function fillStatementsWithTableName()
143
    {
144
        foreach ($this->fetchStatements as &$statement) {
145
            $statement = sprintf($statement, $this->getTableName(), '%s');
146
        }
147
        $this->insertStatement = sprintf($this->insertStatement, $this->getTableName());
148
        $this->deleteStatement = sprintf($this->deleteStatement, $this->getTableName(), '%s');
149
        $this->truncateStatement = sprintf($this->truncateStatement, $this->getTableName());
150
        return $this;
151
    }
152
153
    /**
154
     * Gets the fetch statement, depending on what you specify
155
     *
156
     * @param string      $columns    : the column(s) name(s) you want to retrieve from the database
157
     * @param string      $id         unique cache content identifier
158
     * @param string|null $cache_id   cache id
159
     * @param string|null $compile_id compile id
160
     *
161
     * @access protected
162
     * @return \PDOStatement
163
     */
164
    protected function getFetchStatement($columns, $id, $cache_id = null, $compile_id = null)
165
    {
166
        $args = array();
167
        if (!is_null($cache_id) && !is_null($compile_id)) {
168
            $query = $this->fetchStatements[ 'withCacheIdAndCompileId' ] and
169
            $args = array('id' => $id, 'cache_id' => $cache_id, 'compile_id' => $compile_id);
170
        } elseif (is_null($cache_id) && !is_null($compile_id)) {
171
            $query = $this->fetchStatements[ 'withCompileId' ] and
172
            $args = array('id' => $id, 'compile_id' => $compile_id);
173
        } elseif (!is_null($cache_id) && is_null($compile_id)) {
174
            $query = $this->fetchStatements[ 'withCacheId' ] and $args = array('id' => $id, 'cache_id' => $cache_id);
175
        } else {
176
            $query = $this->fetchStatements[ 'default' ] and $args = array('id' => $id);
177
        }
178
        $query = sprintf($query, $columns);
179
        $stmt = $this->pdo->prepare($query);
180
        foreach ($args as $key => $value) {
181
            $stmt->bindValue($key, $value);
182
        }
183
        return $stmt;
184
    }
185
186
    /**
187
     * fetch cached content and its modification time from data source
188
     *
189
     * @param string      $id         unique cache content identifier
190
     * @param string      $name       template name
191
     * @param string|null $cache_id   cache id
192
     * @param string|null $compile_id compile id
193
     * @param string      $content    cached content
194
     * @param integer     $mtime      cache modification timestamp (epoch)
195
     *
196
     * @return void
197
     * @access protected
198
     */
199
    protected function fetch($id, $name, $cache_id = null, $compile_id = null, &$content, &$mtime)
200
    {
201
        $stmt = $this->getFetchStatement($this->fetchColumns, $id, $cache_id, $compile_id);
202
        $stmt->execute();
203
        $row = $stmt->fetch();
204
        $stmt->closeCursor();
205
        if ($row) {
206
            $content = $this->outputContent($row[ 'content' ]);
207
            $mtime = strtotime($row[ 'modified' ]);
208
        } else {
209
            $content = null;
210
            $mtime = null;
211
        }
212
    }
213
214
    /**
215
     * Fetch cached content's modification timestamp from data source
216
     * {@internal implementing this method is optional.
217
     *  Only implement it if modification times can be accessed faster than loading the complete cached content.}}
218
     *
219
     * @param string      $id         unique cache content identifier
220
     * @param string      $name       template name
221
     * @param string|null $cache_id   cache id
222
     * @param string|null $compile_id compile id
223
     *
224
     * @return integer|boolean timestamp (epoch) the template was modified, or false if not found
225
     * @access protected
226
     */
227
    //    protected function fetchTimestamp($id, $name, $cache_id = null, $compile_id = null) {
228
    //        $stmt       =   $this->getFetchStatement($this->fetchTimestampColumns, $id, $cache_id, $compile_id);
229
    //        $stmt       ->  execute();
230
    //        $mtime      =   strtotime($stmt->fetchColumn());
231
    //        $stmt       ->  closeCursor();
232
    //        return $mtime;
233
    //    }
234
    /**
235
     * Save content to cache
236
     *
237
     * @param string       $id         unique cache content identifier
238
     * @param string       $name       template name
239
     * @param string|null  $cache_id   cache id
240
     * @param string|null  $compile_id compile id
241
     * @param integer|null $exp_time   seconds till expiration time in seconds or null
242
     * @param string       $content    content to cache
243
     *
244
     * @return boolean success
245
     * @access protected
246
     */
247
    protected function save($id, $name, $cache_id = null, $compile_id = null, $exp_time, $content)
248
    {
249
        $stmt = $this->pdo->prepare($this->insertStatement);
250
        $stmt->bindValue('id', $id);
251
        $stmt->bindValue('name', $name);
252
        $stmt->bindValue('cache_id', $cache_id, (is_null($cache_id)) ? PDO::PARAM_NULL : PDO::PARAM_STR);
253
        $stmt->bindValue('compile_id', $compile_id, (is_null($compile_id)) ? PDO::PARAM_NULL : PDO::PARAM_STR);
254
        $stmt->bindValue('expire', (int)$exp_time, PDO::PARAM_INT);
255
        $stmt->bindValue('content', $this->inputContent($content));
256
        $stmt->execute();
257
        return !!$stmt->rowCount();
258
    }
259
260
    /**
261
     * Encodes the content before saving to database
262
     *
263
     * @param string $content
264
     *
265
     * @return string $content
266
     * @access protected
267
     */
268
    protected function inputContent($content)
269
    {
270
        return $content;
271
    }
272
273
    /**
274
     * Decodes the content before saving to database
275
     *
276
     * @param string $content
277
     *
278
     * @return string $content
279
     * @access protected
280
     */
281
    protected function outputContent($content)
282
    {
283
        return $content;
284
    }
285
286
    /**
287
     * Delete content from cache
288
     *
289
     * @param string|null $name       template name
290
     * @param string|null $cache_id   cache id
291
     * @param string|null $compile_id compile id
292
     * @param              integer|null|-1 $exp_time   seconds till expiration or null
0 ignored issues
show
Documentation Bug introduced by
The doc comment integer|null|-1 at position 4 could not be parsed: Unknown type name '-1' at position 4 in integer|null|-1.
Loading history...
293
     *
294
     * @return integer number of deleted caches
295
     * @access             protected
296
     */
297
    protected function delete($name = null, $cache_id = null, $compile_id = null, $exp_time = null)
298
    {
299
        // delete the whole cache
300
        if ($name === null && $cache_id === null && $compile_id === null && $exp_time === null) {
301
            // returning the number of deleted caches would require a second query to count them
302
            $this->pdo->query($this->truncateStatement);
303
            return -1;
304
        }
305
        // build the filter
306
        $where = array();
307
        // equal test name
308
        if ($name !== null) {
309
            $where[] = 'name = ' . $this->pdo->quote($name);
310
        }
311
        // equal test cache_id and match sub-groups
312
        if ($cache_id !== null) {
313
            $where[] =
314
                '(cache_id = ' .
315
                $this->pdo->quote($cache_id) .
316
                ' OR cache_id LIKE ' .
317
                $this->pdo->quote($cache_id . '|%') .
318
                ')';
319
        }
320
        // equal test compile_id
321
        if ($compile_id !== null) {
322
            $where[] = 'compile_id = ' . $this->pdo->quote($compile_id);
323
        }
324
        // for clearing expired caches
325
        if ($exp_time === Smarty::CLEAR_EXPIRED) {
326
            $where[] = 'expire < CURRENT_TIMESTAMP';
327
        } // range test expiration time
328
        elseif ($exp_time !== null) {
329
            $where[] = 'modified < DATE_SUB(NOW(), INTERVAL ' . intval($exp_time) . ' SECOND)';
330
        }
331
        // run delete query
332
        $query = $this->pdo->query(sprintf($this->deleteStatement, join(' AND ', $where)));
333
        return $query->rowCount();
334
    }
335
336
    /**
337
     * Gets the formatted table name
338
     *
339
     * @return string
340
     * @access protected
341
     */
342
    protected function getTableName()
343
    {
344
        return (is_null($this->database)) ? "`{$this->table}`" : "`{$this->database}`.`{$this->table}`";
345
    }
346
}
347