Skip to content
Snippets Groups Projects
Unverified Commit 67c29a61 authored by Thijs Kinkhorst's avatar Thijs Kinkhorst Committed by GitHub
Browse files

Merge pull request #746 from tvdijen/patch-sqlite

 Replace deprecated Sqlite-calls with PDO-calls 
parents cea12b94 c21c031b
No related branches found
No related tags found
No related merge requests found
...@@ -44,7 +44,6 @@ ...@@ -44,7 +44,6 @@
"jaimeperez/twig-configurable-i18n": "^1.2" "jaimeperez/twig-configurable-i18n": "^1.2"
}, },
"require-dev": { "require-dev": {
"ext-pdo_sqlite": "*",
"phpunit/phpunit": "~4.8.35", "phpunit/phpunit": "~4.8.35",
"mikey179/vfsStream": "~1.6", "mikey179/vfsStream": "~1.6",
"friendsofphp/php-cs-fixer": "^2.2" "friendsofphp/php-cs-fixer": "^2.2"
......
...@@ -9,198 +9,209 @@ ...@@ -9,198 +9,209 @@
* @author Andreas Åkre Solberg <andreas@uninett.no>, UNINETT AS. * @author Andreas Åkre Solberg <andreas@uninett.no>, UNINETT AS.
* @package SimpleSAMLphp * @package SimpleSAMLphp
*/ */
class sspmod_core_Storage_SQLPermanentStorage { class sspmod_core_Storage_SQLPermanentStorage
{
private $db; private $db;
function __construct($name, $config = NULL) { public function __construct($name, $config = null)
if (is_null($config)) {
$config = SimpleSAML_Configuration::getInstance(); if (is_null($config)) {
$config = SimpleSAML_Configuration::getInstance();
$datadir = $config->getPathValue('datadir', 'data/'); }
if (!is_dir($datadir)) $datadir = $config->getPathValue('datadir', 'data/');
throw new Exception('Data directory [' . $datadir. '] does not exist');
if (!is_writable($datadir)) if (!is_dir($datadir)) {
throw new Exception('Data directory [' . $datadir. '] is not writable'); throw new Exception('Data directory ['.$datadir.'] does not exist');
} else if (!is_writable($datadir)) {
$sqllitedir = $datadir . 'sqllite/'; throw new Exception('Data directory ['.$datadir.'] is not writable');
if (!is_dir($sqllitedir)) { }
mkdir($sqllitedir);
} $sqllitedir = $datadir.'sqllite/';
if (!is_dir($sqllitedir)) {
$dbfile = $sqllitedir . $name . '.sqllite'; mkdir($sqllitedir);
}
if ($this->db = new SQLiteDatabase($dbfile)) {
$q = @$this->db->query('SELECT key1 FROM data LIMIT 1'); $dbfile = 'sqlite:'.$sqllitedir.$name.'.sqlite';
if ($q === false) { if ($this->db = new \PDO($dbfile)) {
$this->db->queryExec(' $q = @$this->db->query('SELECT key1 FROM data LIMIT 1');
CREATE TABLE data ( if ($q === false) {
key1 text, $this->db->exec('
key2 text, CREATE TABLE data (
type text, key1 text,
value text, key2 text,
created timestamp, type text,
updated timestamp, value text,
expire timestamp, created timestamp,
PRIMARY KEY (key1,key2,type) updated timestamp,
); expire timestamp,
'); PRIMARY KEY (key1,key2,type)
} );
} else { ');
throw new Exception('Error creating SQL lite database [' . $dbfile . '].'); }
} } else {
} throw new Exception('Error creating SQL lite database ['.$dbfile.'].');
}
public function set($type, $key1, $key2, $value, $duration = NULL) { }
if ($this->exists($type, $key1, $key2)) {
$this->update($type, $key1, $key2, $value, $duration); public function set($type, $key1, $key2, $value, $duration = null)
} else { {
$this->insert($type, $key1, $key2, $value, $duration); if ($this->exists($type, $key1, $key2)) {
} $this->update($type, $key1, $key2, $value, $duration);
} } else {
$this->insert($type, $key1, $key2, $value, $duration);
private function insert($type, $key1, $key2, $value, $duration = NULL) { }
}
$setDuration = '';
if (is_null($duration)) { private function insert($type, $key1, $key2, $value, $duration = null)
$setDuration = 'NULL'; {
} else { $expire = is_null($duration) ? null : (time() + $duration);
$setDuration = "'" . sqlite_escape_string(time() + $duration) . "'";
} $query = "INSERT INTO data (key1, key2, type, created, updated, expire, value)".
" VALUES(:key1, :key2, :type, :created, :updated, :expire, :value)";
$query = "INSERT INTO data (key1,key2,type,created,updated,expire,value) VALUES (" . $prepared = $this->db->prepare($query);
"'" . sqlite_escape_string($key1) . "'," . $data = array(':key1' => $key1, ':key2' => $key2,
"'" . sqlite_escape_string($key2) . "'," . ':type' => $type, ':created' => time(),
"'" . sqlite_escape_string($type) . "'," . ':updated' => time(), ':expire' => $expire,
"'" . sqlite_escape_string(time()) . "'," . ':value' => serialize($value));
"'" . sqlite_escape_string(time()) . "'," . $prepared->execute($data);
$setDuration . "," . $results = $prepared->fetchAll(PDO::FETCH_ASSOC);
"'" . sqlite_escape_string(serialize($value)) . "')"; return $results;
$results = $this->db->queryExec($query); }
return $results;
} private function update($type, $key1, $key2, $value, $duration = null)
{
private function update($type, $key1, $key2, $value, $duration = NULL) { $expire = is_null($duration) ? null : (time() + $duration);
$setDuration = ''; $query = "UPDATE data SET updated = :updated, value = :value, expire = :expire WHERE key1 = :key1 AND key2 = :key2 AND type = :type";
if (is_null($duration)) { $prepared = $this->db->prepare($query);
$setDuration = ", expire = NULL "; $data = array(':key1' => $key1, ':key2' => $key2,
} else { ':type' => $type, ':updated' => time(),
$setDuration = ", expire = '" . sqlite_escape_string(time() + $duration) . "' "; ':expire' => $expire, ':value' => serialize($value));
} $prepared->execute($data);
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
$query = "UPDATE data SET " . return $results;
"updated = '" . sqlite_escape_string(time()) . "'," . }
"value = '" . sqlite_escape_string(serialize($value)) . "'" .
$setDuration . public function get($type = null, $key1 = null, $key2 = null)
"WHERE " . {
"key1 = '" . sqlite_escape_string($key1) . "' AND " . $conditions = self::getCondition($type, $key1, $key2);
"key2 = '" . sqlite_escape_string($key2) . "' AND " . $query = 'SELECT * FROM data WHERE '.$conditions;
"type = '" . sqlite_escape_string($type) . "'";
$results = $this->db->queryExec($query); $prepared = $this->db->prepare($query);
return $results; $prepared->execute();
} $results = $prepared->fetchAll(PDO::FETCH_ASSOC);
if (count($results) !== 1) {
public function get($type = NULL, $key1 = NULL, $key2 = NULL) { return null;
}
$condition = self::getCondition($type, $key1, $key2);
$query = "SELECT * FROM data WHERE " . $condition; $res = $results[0];
$results = $this->db->arrayQuery($query, SQLITE_ASSOC); $res['value'] = unserialize($res['value']);
return $res;
if (count($results) !== 1) return NULL; }
$res = $results[0]; /*
$res['value'] = unserialize($res['value']); * Return the value directly (not in a container)
return $res; */
} public function getValue($type = null, $key1 = null, $key2 = null)
{
/* $res = $this->get($type, $key1, $key2);
* Return the value directly (not in a container) if ($res === null) {
*/ return null;
public function getValue($type = NULL, $key1 = NULL, $key2 = NULL) { }
$res = $this->get($type, $key1, $key2); return $res['value'];
if ($res === NULL) return NULL; }
return $res['value'];
} public function exists($type, $key1, $key2)
{
public function exists($type, $key1, $key2) { $query = 'SELECT * FROM data WHERE type = :type AND key1 = :key1 AND key2 = :key2 LIMIT 1';
$query = "SELECT * FROM data WHERE " . $prepared = $this->db->prepare($query);
"key1 = '" . sqlite_escape_string($key1) . "' AND " . $data = array(':type' => $type, ':key1' => $key1, ':key2' => $key2);
"key2 = '" . sqlite_escape_string($key2) . "' AND " . $prepared->execute($data);
"type = '" . sqlite_escape_string($type) . "' LIMIT 1"; $results = $prepared->fetchAll(PDO::FETCH_ASSOC);
$results = $this->db->arrayQuery($query, SQLITE_ASSOC); return (count($results) == 1);
return (count($results) == 1); }
}
public function getList($type = null, $key1 = null, $key2 = null)
public function getList($type = NULL, $key1 = NULL, $key2 = NULL) { {
$conditions = self::getCondition($type, $key1, $key2);
$condition = self::getCondition($type, $key1, $key2); $query = 'SELECT * FROM data WHERE '.$conditions;
$query = "SELECT * FROM data WHERE " . $condition; $prepared = $this->db->prepare($query);
$results = $this->db->arrayQuery($query, SQLITE_ASSOC); $prepared->execute();
if (count($results) == 0) return NULL;
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
foreach($results AS $key => $value) { if (count($results) == 0) {
$results[$key]['value'] = unserialize($results[$key]['value']); return null;
} }
return $results;
} foreach ($results as $key => $value) {
$results[$key]['value'] = unserialize($results[$key]['value']);
public function getKeys($type = NULL, $key1 = NULL, $key2 = NULL, $whichKey = 'type') { }
return $results;
if (!in_array($whichKey, array('key1', 'key2', 'type'), true)) }
throw new Exception('Invalid key type');
public function getKeys($type = null, $key1 = null, $key2 = null, $whichKey = 'type')
$condition = self::getCondition($type, $key1, $key2); {
if (!in_array($whichKey, array('key1', 'key2', 'type'), true)) {
$query = "SELECT DISTINCT " . $whichKey . " FROM data WHERE " . $condition; throw new Exception('Invalid key type');
$results = $this->db->arrayQuery($query, SQLITE_ASSOC); }
if (count($results) == 0) return NULL; $conditions = self::getCondition($type, $key1, $key2);
$query = 'SELECT DISTINCT :whichKey FROM data WHERE '.$conditions;
$resarray = array(); $prepared = $this->db->prepare($query);
foreach($results AS $key => $value) { $data = array('whichKey' => $whichKey);
$resarray[] = $value[$whichKey]; $prepared->execute($data);
} $results = $prepared->fetchAll(PDO::FETCH_ASSOC);
return $resarray; if (count($results) == 0) {
} return null;
}
public function remove($type, $key1, $key2) { $resarray = array();
$query = "DELETE FROM data WHERE " . foreach ($results as $key => $value) {
"key1 = '" . sqlite_escape_string($key1) . "' AND " . $resarray[] = $value[$whichKey];
"key2 = '" . sqlite_escape_string($key2) . "' AND " . }
"type = '" . sqlite_escape_string($type) . "'"; return $resarray;
$results = $this->db->arrayQuery($query, SQLITE_ASSOC); }
return (count($results) == 1);
} public function remove($type, $key1, $key2)
{
public function removeExpired() { $query = 'DELETE FROM data WHERE type = :type AND key1 = :key1 AND key2 = :key2';
$query = "DELETE FROM data WHERE expire NOT NULL AND expire < " . time(); $prepared = $this->db->prepare($query);
$this->db->arrayQuery($query, SQLITE_ASSOC); $data = array(':type' => $type, ':key1' => $key1, ':key2' => $key2);
$changes = $this->db->changes(); $prepared->execute($data);
return $changes; $results = $prepared->fetchAll(PDO::FETCH_ASSOC);
} return (count($results) == 1);
}
/** public function removeExpired()
* Create a SQL condition statement based on parameters {
*/ $query = "DELETE FROM data WHERE expire NOT NULL AND expire < :expire";
private static function getCondition($type = NULL, $key1 = NULL, $key2 = NULL) { $prepared = $this->db->prepare($query);
$conditions = array(); $data = array(':expire' => time());
$prepared->execute($data);
if (!is_null($type)) $conditions[] = "type = '" . sqlite_escape_string($type) . "'"; return $prepared->rowCount();
if (!is_null($key1)) $conditions[] = "key1 = '" . sqlite_escape_string($key1) . "'"; }
if (!is_null($key2)) $conditions[] = "key2 = '" . sqlite_escape_string($key2) . "'";
/**
if (count($conditions) === 0) return '1'; * Create a SQL condition statement based on parameters
*/
$condition = join(' AND ', $conditions); private function getCondition($type = null, $key1 = null, $key2 = null)
{
return $condition; $conditions = array();
} if (!is_null($type)) {
$conditions[] = "type = ".$this->db->quote($type);
}
if (!is_null($key1)) {
$conditions[] = "key1 = ".$this->db->quote($key1);
}
if (!is_null($key2)) {
$conditions[] = "key2 = ".$this->db->quote($key2);
}
if (count($conditions) === 0) {
return '1';
}
return join(' AND ', $conditions);
}
} }
<?php
use PHPUnit\Framework\TestCase;
/**
* Test for the SQLPermanentStorage class.
*/
class Test_Core_Storage_SQLPermanentStorage extends TestCase
{
private static $sql;
public static function setUpBeforeClass()
{
// Create instance
$config = \SimpleSAML_Configuration::loadFromArray([
'datadir' => sys_get_temp_dir(),
]);
self::$sql = new sspmod_core_Storage_SQLPermanentStorage('test', $config);
}
public static function tearDownAfterClass()
{
self::$sql = null;
unlink(sys_get_temp_dir().'/sqllite/test.sqlite');
}
public function testSet()
{
// Set a new value
self::$sql->set('testtype', 'testkey1', 'testkey2', 'testvalue', 0);
// Test getCondition
$result = self::$sql->get();
$this->assertEquals('testvalue', $result['value']);
}
public function testSetOverwrite()
{
// Overwrite existing value
self::$sql->set('testtype', 'testkey1', 'testkey2', 'testvaluemodified', 0);
// Test that the value was actually overwriten
$result = self::$sql->getValue('testtype', 'testkey1', 'testkey2');
$this->assertEquals('testvaluemodified', $result);
$result = self::$sql->getList('testtype', 'testkey1', 'testkey2');
$this->assertEquals('testvaluemodified', $result[0]['value']);
}
public function testNonexistentKey()
{
// Test that getting some non-existing key will return null
$result = self::$sql->getValue('testtype_nonexistent', 'testkey1_nonexistent', 'testkey2_nonexistent');
$this->assertNull($result);
$result = self::$sql->getList('testtype_nonexistent', 'testkey1_nonexistent', 'testkey2_nonexistent');
$this->assertNull($result);
$result = self::$sql->get('testtype_nonexistent', 'testkey1_nonexistent', 'testkey2_nonexistent');
$this->assertNull($result);
}
public function testExpiration()
{
// Make sure the earlier created entry has expired now
sleep(1);
// Now add a second entry that never expires
self::$sql->set('testtype', 'testkey1_nonexpiring', 'testkey2_nonexpiring', 'testvalue_nonexpiring', null);
// Expire entries and verify that only the second one is left
self::$sql->removeExpired();
$result = self::$sql->getValue('testtype', 'testkey1', 'testkey2');
$this->assertNull($result);
$result = self::$sql->getValue('testtype', 'testkey1_nonexpiring', 'testkey2_nonexpiring');
$this->assertEquals('testvalue_nonexpiring', $result);
}
public function testRemove()
{
// Now remove the nonexpiring entry and make sure it's gone
self::$sql->remove('testtype', 'testkey1_nonexpiring', 'testkey2_nonexpiring');
$result = self::$sql->getValue('testtype', 'testkey1_nonexpiring', 'testkey2_nonexpiring');
$this->assertNull($result);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment