ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
SQL.php
Go to the documentation of this file.
1<?php
2
3namespace SimpleSAML\Store;
4
5use \SimpleSAML_Configuration as Configuration;
6use \SimpleSAML\Logger;
7use \SimpleSAML\Store;
8
14class SQL extends Store
15{
21 public $pdo;
22
23
29 public $driver;
30
31
37 public $prefix;
38
39
46
47
51 protected function __construct()
52 {
53 $config = Configuration::getInstance();
54
55 $dsn = $config->getString('store.sql.dsn');
56 $username = $config->getString('store.sql.username', null);
57 $password = $config->getString('store.sql.password', null);
58 $this->prefix = $config->getString('store.sql.prefix', 'simpleSAMLphp');
59
60 $this->pdo = new \PDO($dsn, $username, $password);
61 $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
62
63 $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
64
65 if ($this->driver === 'mysql') {
66 $this->pdo->exec('SET time_zone = "+00:00"');
67 }
68
69 $this->initTableVersionTable();
70 $this->initKVTable();
71 }
72
73
77 private function initTableVersionTable()
78 {
79 $this->tableVersions = array();
80
81 try {
82 $fetchTableVersion = $this->pdo->query('SELECT _name, _version FROM '.$this->prefix.'_tableVersion');
83 } catch (\PDOException $e) {
84 $this->pdo->exec(
85 'CREATE TABLE '.$this->prefix.
86 '_tableVersion (_name VARCHAR(30) NOT NULL UNIQUE, _version INTEGER NOT NULL)'
87 );
88 return;
89 }
90
91 while (($row = $fetchTableVersion->fetch(\PDO::FETCH_ASSOC)) !== false) {
92 $this->tableVersions[$row['_name']] = (int) $row['_version'];
93 }
94 }
95
96
100 private function initKVTable()
101 {
102 if ($this->getTableVersion('kvstore') === 1) {
103 // Table initialized
104 return;
105 }
106
107 $text_t = 'TEXT';
108 if ($this->driver === 'mysql') {
109 // TEXT data type has size constraints that can be hit at some point, so we use LONGTEXT instead
110 $text_t = 'LONGTEXT';
111 }
112 $query = 'CREATE TABLE '.$this->prefix.
113 '_kvstore (_type VARCHAR(30) NOT NULL, _key VARCHAR(50) NOT NULL, _value '.$text_t.
114 ' NOT NULL, _expire TIMESTAMP, PRIMARY KEY (_key, _type))';
115 $this->pdo->exec($query);
116
117 $query = 'CREATE INDEX '.$this->prefix.'_kvstore_expire ON '.$this->prefix.'_kvstore (_expire)';
118 $this->pdo->exec($query);
119
120 $this->setTableVersion('kvstore', 1);
121 }
122
123
131 public function getTableVersion($name)
132 {
133 assert('is_string($name)');
134
135 if (!isset($this->tableVersions[$name])) {
136 return 0;
137 }
138
139 return $this->tableVersions[$name];
140 }
141
142
150 {
151 assert('is_string($name)');
152 assert('is_int($version)');
153
154 $this->insertOrUpdate(
155 $this->prefix.'_tableVersion',
156 array('_name'),
157 array('_name' => $name, '_version' => $version)
158 );
159 $this->tableVersions[$name] = $version;
160 }
161
162
172 public function insertOrUpdate($table, array $keys, array $data)
173 {
174 assert('is_string($table)');
175
176 $colNames = '('.implode(', ', array_keys($data)).')';
177 $values = 'VALUES(:'.implode(', :', array_keys($data)).')';
178
179 switch ($this->driver) {
180 case 'mysql':
181 $query = 'REPLACE INTO '.$table.' '.$colNames.' '.$values;
182 $query = $this->pdo->prepare($query);
183 $query->execute($data);
184 return;
185 case 'sqlite':
186 $query = 'INSERT OR REPLACE INTO '.$table.' '.$colNames.' '.$values;
187 $query = $this->pdo->prepare($query);
188 $query->execute($data);
189 return;
190 }
191
192 // default implementation, try INSERT, and UPDATE if that fails.
193 $insertQuery = 'INSERT INTO '.$table.' '.$colNames.' '.$values;
194 $insertQuery = $this->pdo->prepare($insertQuery);
195 try {
196 $insertQuery->execute($data);
197 return;
198 } catch (\PDOException $e) {
199 $ecode = (string) $e->getCode();
200 switch ($ecode) {
201 case '23505': // PostgreSQL
202 break;
203 default:
204 Logger::error('Error while saving data: '.$e->getMessage());
205 throw $e;
206 }
207 }
208
209 $updateCols = array();
210 $condCols = array();
211 foreach ($data as $col => $value) {
212 $tmp = $col.' = :'.$col;
213
214 if (in_array($col, $keys, true)) {
215 $condCols[] = $tmp;
216 } else {
217 $updateCols[] = $tmp;
218 }
219 }
220
221 $updateQuery = 'UPDATE '.$table.' SET '.implode(',', $updateCols).' WHERE '.implode(' AND ', $condCols);
222 $updateQuery = $this->pdo->prepare($updateQuery);
223 $updateQuery->execute($data);
224 }
225
226
230 private function cleanKVStore()
231 {
232 Logger::debug('store.sql: Cleaning key-value store.');
233
234 $query = 'DELETE FROM '.$this->prefix.'_kvstore WHERE _expire < :now';
235 $params = array('now' => gmdate('Y-m-d H:i:s'));
236
237 $query = $this->pdo->prepare($query);
238 $query->execute($params);
239 }
240
241
250 public function get($type, $key)
251 {
252 assert('is_string($type)');
253 assert('is_string($key)');
254
255 if (strlen($key) > 50) {
256 $key = sha1($key);
257 }
258
259 $query = 'SELECT _value FROM '.$this->prefix.
260 '_kvstore WHERE _type = :type AND _key = :key AND (_expire IS NULL OR _expire > :now)';
261 $params = array('type' => $type, 'key' => $key, 'now' => gmdate('Y-m-d H:i:s'));
262
263 $query = $this->pdo->prepare($query);
264 $query->execute($params);
265
266 $row = $query->fetch(\PDO::FETCH_ASSOC);
267 if ($row === false) {
268 return null;
269 }
270
271 $value = $row['_value'];
272 if (is_resource($value)) {
273 $value = stream_get_contents($value);
274 }
275 $value = urldecode($value);
276 $value = unserialize($value);
277
278 if ($value === false) {
279 return null;
280 }
281 return $value;
282 }
283
284
293 public function set($type, $key, $value, $expire = null)
294 {
295 assert('is_string($type)');
296 assert('is_string($key)');
297 assert('is_null($expire) || (is_int($expire) && $expire > 2592000)');
298
299 if (rand(0, 1000) < 10) {
300 $this->cleanKVStore();
301 }
302
303 if (strlen($key) > 50) {
304 $key = sha1($key);
305 }
306
307 if ($expire !== null) {
308 $expire = gmdate('Y-m-d H:i:s', $expire);
309 }
310
311 $value = serialize($value);
312 $value = rawurlencode($value);
313
314 $data = array(
315 '_type' => $type,
316 '_key' => $key,
317 '_value' => $value,
318 '_expire' => $expire,
319 );
320
321 $this->insertOrUpdate($this->prefix.'_kvstore', array('_type', '_key'), $data);
322 }
323
324
331 public function delete($type, $key)
332 {
333 assert('is_string($type)');
334 assert('is_string($key)');
335
336 if (strlen($key) > 50) {
337 $key = sha1($key);
338 }
339
340 $data = array(
341 '_type' => $type,
342 '_key' => $key,
343 );
344
345 $query = 'DELETE FROM '.$this->prefix.'_kvstore WHERE _type=:_type AND _key=:_key';
346 $query = $this->pdo->prepare($query);
347 $query->execute($data);
348 }
349}
An exception for terminatinating execution or to throw for unit testing.
static error($string)
Definition: Logger.php:168
static debug($string)
Definition: Logger.php:213
initTableVersionTable()
Initialize the table-version table.
Definition: SQL.php:77
setTableVersion($name, $version)
Set table version.
Definition: SQL.php:149
getTableVersion($name)
Get table version.
Definition: SQL.php:131
initKVTable()
Initialize key-value table.
Definition: SQL.php:100
insertOrUpdate($table, array $keys, array $data)
Insert or update a key-value in the store.
Definition: SQL.php:172
__construct()
Initialize the SQL data store.
Definition: SQL.php:51
cleanKVStore()
Clean the key-value table of expired entries.
Definition: SQL.php:230
$key
Definition: croninfo.php:18
if($format !==null) $name
Definition: metadata.php:146
$expire
Definition: saml2-acs.php:140
$keys
$query
$type
$password
Definition: pwgen.php:17
if(empty($password)) $table
Definition: pwgen.php:24
$params
Definition: disable.php:11