1: <?php
2:
3: /**
4: * MySQL CacheResource
5: * CacheResource Implementation based on the Custom API to use
6: * MySQL as the storage resource for Smarty's output caching.
7: * Table definition:
8: * <pre>CREATE TABLE IF NOT EXISTS `output_cache` (
9: * `id` CHAR(40) NOT NULL COMMENT 'sha1 hash',
10: * `name` VARCHAR(250) NOT NULL,
11: * `cache_id` VARCHAR(250) NULL DEFAULT NULL,
12: * `compile_id` VARCHAR(250) NULL DEFAULT NULL,
13: * `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
14: * `content` LONGTEXT NOT NULL,
15: * PRIMARY KEY (`id`),
16: * INDEX(`name`),
17: * INDEX(`cache_id`),
18: * INDEX(`compile_id`),
19: * INDEX(`modified`)
20: * ) ENGINE = InnoDB;</pre>
21: *
22: * @package CacheResource-examples
23: * @author Rodney Rehm
24: */
25: class Smarty_CacheResource_Mysql extends Smarty_CacheResource_Custom
26: {
27: /**
28: * @var \PDO
29: */
30: protected $db;
31:
32: /**
33: * @var \PDOStatement
34: */
35: protected $fetch;
36:
37: /**
38: * @var \PDOStatement
39: */
40: protected $fetchTimestamp;
41:
42: /**
43: * @var \PDOStatement
44: */
45: protected $save;
46:
47: /**
48: * Smarty_CacheResource_Mysql constructor.
49: *
50: * @throws \SmartyException
51: */
52: public function __construct()
53: {
54: try {
55: $this->db = new PDO("mysql:dbname=test;host=127.0.0.1", "smarty");
56: } catch (PDOException $e) {
57: throw new SmartyException('Mysql Resource failed: ' . $e->getMessage());
58: }
59: $this->fetch = $this->db->prepare('SELECT modified, content FROM output_cache WHERE id = :id');
60: $this->fetchTimestamp = $this->db->prepare('SELECT modified FROM output_cache WHERE id = :id');
61: $this->save = $this->db->prepare(
62: 'REPLACE INTO output_cache (id, name, cache_id, compile_id, content)
63: VALUES (:id, :name, :cache_id, :compile_id, :content)'
64: );
65: }
66:
67: /**
68: * fetch cached content and its modification time from data source
69: *
70: * @param string $id unique cache content identifier
71: * @param string $name template name
72: * @param string $cache_id cache id
73: * @param string $compile_id compile id
74: * @param string $content cached content
75: * @param integer $mtime cache modification timestamp (epoch)
76: *
77: * @return void
78: */
79: protected function fetch($id, $name, $cache_id, $compile_id, &$content, &$mtime)
80: {
81: $this->fetch->execute(array('id' => $id));
82: $row = $this->fetch->fetch();
83: $this->fetch->closeCursor();
84: if ($row) {
85: $content = $row[ 'content' ];
86: $mtime = strtotime($row[ 'modified' ]);
87: } else {
88: $content = null;
89: $mtime = null;
90: }
91: }
92:
93: /**
94: * Fetch cached content's modification timestamp from data source
95: *
96: * @note implementing this method is optional. Only implement it if modification times can be accessed faster than
97: * loading the complete cached content.
98: *
99: * @param string $id unique cache content identifier
100: * @param string $name template name
101: * @param string $cache_id cache id
102: * @param string $compile_id compile id
103: *
104: * @return integer|boolean timestamp (epoch) the template was modified, or false if not found
105: */
106: protected function fetchTimestamp($id, $name, $cache_id, $compile_id)
107: {
108: $this->fetchTimestamp->execute(array('id' => $id));
109: $mtime = strtotime($this->fetchTimestamp->fetchColumn());
110: $this->fetchTimestamp->closeCursor();
111: return $mtime;
112: }
113:
114: /**
115: * Save content to cache
116: *
117: * @param string $id unique cache content identifier
118: * @param string $name template name
119: * @param string $cache_id cache id
120: * @param string $compile_id compile id
121: * @param integer|null $exp_time seconds till expiration time in seconds or null
122: * @param string $content content to cache
123: *
124: * @return boolean success
125: */
126: protected function save($id, $name, $cache_id, $compile_id, $exp_time, $content)
127: {
128: $this->save->execute(
129: array('id' => $id,
130: 'name' => $name,
131: 'cache_id' => $cache_id,
132: 'compile_id' => $compile_id,
133: 'content' => $content,)
134: );
135: return !!$this->save->rowCount();
136: }
137:
138: /**
139: * Delete content from cache
140: *
141: * @param string $name template name
142: * @param string $cache_id cache id
143: * @param string $compile_id compile id
144: * @param integer|null $exp_time seconds till expiration or null
145: *
146: * @return integer number of deleted caches
147: */
148: protected function delete($name, $cache_id, $compile_id, $exp_time)
149: {
150: // delete the whole cache
151: if ($name === null && $cache_id === null && $compile_id === null && $exp_time === null) {
152: // returning the number of deleted caches would require a second query to count them
153: $query = $this->db->query('TRUNCATE TABLE output_cache');
154: return -1;
155: }
156: // build the filter
157: $where = array();
158: // equal test name
159: if ($name !== null) {
160: $where[] = 'name = ' . $this->db->quote($name);
161: }
162: // equal test compile_id
163: if ($compile_id !== null) {
164: $where[] = 'compile_id = ' . $this->db->quote($compile_id);
165: }
166: // range test expiration time
167: if ($exp_time !== null) {
168: $where[] = 'modified < DATE_SUB(NOW(), INTERVAL ' . intval($exp_time) . ' SECOND)';
169: }
170: // equal test cache_id and match sub-groups
171: if ($cache_id !== null) {
172: $where[] =
173: '(cache_id = ' .
174: $this->db->quote($cache_id) .
175: ' OR cache_id LIKE ' .
176: $this->db->quote($cache_id . '|%') .
177: ')';
178: }
179: // run delete query
180: $query = $this->db->query('DELETE FROM output_cache WHERE ' . join(' AND ', $where));
181: return $query->rowCount();
182: }
183: }
184: