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 @@
"jaimeperez/twig-configurable-i18n": "^1.2"
},
"require-dev": {
"ext-pdo_sqlite": "*",
"phpunit/phpunit": "~4.8.35",
"mikey179/vfsStream": "~1.6",
"friendsofphp/php-cs-fixer": "^2.2"
......
......@@ -9,198 +9,209 @@
* @author Andreas Åkre Solberg <andreas@uninett.no>, UNINETT AS.
* @package SimpleSAMLphp
*/
class sspmod_core_Storage_SQLPermanentStorage {
private $db;
function __construct($name, $config = NULL) {
if (is_null($config))
$config = SimpleSAML_Configuration::getInstance();
$datadir = $config->getPathValue('datadir', 'data/');
if (!is_dir($datadir))
throw new Exception('Data directory [' . $datadir. '] does not exist');
if (!is_writable($datadir))
throw new Exception('Data directory [' . $datadir. '] is not writable');
$sqllitedir = $datadir . 'sqllite/';
if (!is_dir($sqllitedir)) {
mkdir($sqllitedir);
}
$dbfile = $sqllitedir . $name . '.sqllite';
if ($this->db = new SQLiteDatabase($dbfile)) {
$q = @$this->db->query('SELECT key1 FROM data LIMIT 1');
if ($q === false) {
$this->db->queryExec('
CREATE TABLE data (
key1 text,
key2 text,
type text,
value text,
created timestamp,
updated timestamp,
expire timestamp,
PRIMARY KEY (key1,key2,type)
);
');
}
} 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);
} else {
$this->insert($type, $key1, $key2, $value, $duration);
}
}
private function insert($type, $key1, $key2, $value, $duration = NULL) {
$setDuration = '';
if (is_null($duration)) {
$setDuration = 'NULL';
} else {
$setDuration = "'" . sqlite_escape_string(time() + $duration) . "'";
}
$query = "INSERT INTO data (key1,key2,type,created,updated,expire,value) VALUES (" .
"'" . sqlite_escape_string($key1) . "'," .
"'" . sqlite_escape_string($key2) . "'," .
"'" . sqlite_escape_string($type) . "'," .
"'" . sqlite_escape_string(time()) . "'," .
"'" . sqlite_escape_string(time()) . "'," .
$setDuration . "," .
"'" . sqlite_escape_string(serialize($value)) . "')";
$results = $this->db->queryExec($query);
return $results;
}
private function update($type, $key1, $key2, $value, $duration = NULL) {
$setDuration = '';
if (is_null($duration)) {
$setDuration = ", expire = NULL ";
} else {
$setDuration = ", expire = '" . sqlite_escape_string(time() + $duration) . "' ";
}
$query = "UPDATE data SET " .
"updated = '" . sqlite_escape_string(time()) . "'," .
"value = '" . sqlite_escape_string(serialize($value)) . "'" .
$setDuration .
"WHERE " .
"key1 = '" . sqlite_escape_string($key1) . "' AND " .
"key2 = '" . sqlite_escape_string($key2) . "' AND " .
"type = '" . sqlite_escape_string($type) . "'";
$results = $this->db->queryExec($query);
return $results;
}
public function get($type = NULL, $key1 = NULL, $key2 = NULL) {
$condition = self::getCondition($type, $key1, $key2);
$query = "SELECT * FROM data WHERE " . $condition;
$results = $this->db->arrayQuery($query, SQLITE_ASSOC);
if (count($results) !== 1) return NULL;
$res = $results[0];
$res['value'] = unserialize($res['value']);
return $res;
}
/*
* Return the value directly (not in a container)
*/
public function getValue($type = NULL, $key1 = NULL, $key2 = NULL) {
$res = $this->get($type, $key1, $key2);
if ($res === NULL) return NULL;
return $res['value'];
}
public function exists($type, $key1, $key2) {
$query = "SELECT * FROM data WHERE " .
"key1 = '" . sqlite_escape_string($key1) . "' AND " .
"key2 = '" . sqlite_escape_string($key2) . "' AND " .
"type = '" . sqlite_escape_string($type) . "' LIMIT 1";
$results = $this->db->arrayQuery($query, SQLITE_ASSOC);
return (count($results) == 1);
}
public function getList($type = NULL, $key1 = NULL, $key2 = NULL) {
$condition = self::getCondition($type, $key1, $key2);
$query = "SELECT * FROM data WHERE " . $condition;
$results = $this->db->arrayQuery($query, SQLITE_ASSOC);
if (count($results) == 0) return NULL;
foreach($results AS $key => $value) {
$results[$key]['value'] = unserialize($results[$key]['value']);
}
return $results;
}
public function getKeys($type = NULL, $key1 = NULL, $key2 = NULL, $whichKey = 'type') {
if (!in_array($whichKey, array('key1', 'key2', 'type'), true))
throw new Exception('Invalid key type');
$condition = self::getCondition($type, $key1, $key2);
$query = "SELECT DISTINCT " . $whichKey . " FROM data WHERE " . $condition;
$results = $this->db->arrayQuery($query, SQLITE_ASSOC);
if (count($results) == 0) return NULL;
$resarray = array();
foreach($results AS $key => $value) {
$resarray[] = $value[$whichKey];
}
return $resarray;
}
public function remove($type, $key1, $key2) {
$query = "DELETE FROM data WHERE " .
"key1 = '" . sqlite_escape_string($key1) . "' AND " .
"key2 = '" . sqlite_escape_string($key2) . "' AND " .
"type = '" . sqlite_escape_string($type) . "'";
$results = $this->db->arrayQuery($query, SQLITE_ASSOC);
return (count($results) == 1);
}
public function removeExpired() {
$query = "DELETE FROM data WHERE expire NOT NULL AND expire < " . time();
$this->db->arrayQuery($query, SQLITE_ASSOC);
$changes = $this->db->changes();
return $changes;
}
/**
* Create a SQL condition statement based on parameters
*/
private static function getCondition($type = NULL, $key1 = NULL, $key2 = NULL) {
$conditions = array();
if (!is_null($type)) $conditions[] = "type = '" . sqlite_escape_string($type) . "'";
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';
$condition = join(' AND ', $conditions);
return $condition;
}
class sspmod_core_Storage_SQLPermanentStorage
{
private $db;
public function __construct($name, $config = null)
{
if (is_null($config)) {
$config = SimpleSAML_Configuration::getInstance();
}
$datadir = $config->getPathValue('datadir', 'data/');
if (!is_dir($datadir)) {
throw new Exception('Data directory ['.$datadir.'] does not exist');
} else if (!is_writable($datadir)) {
throw new Exception('Data directory ['.$datadir.'] is not writable');
}
$sqllitedir = $datadir.'sqllite/';
if (!is_dir($sqllitedir)) {
mkdir($sqllitedir);
}
$dbfile = 'sqlite:'.$sqllitedir.$name.'.sqlite';
if ($this->db = new \PDO($dbfile)) {
$q = @$this->db->query('SELECT key1 FROM data LIMIT 1');
if ($q === false) {
$this->db->exec('
CREATE TABLE data (
key1 text,
key2 text,
type text,
value text,
created timestamp,
updated timestamp,
expire timestamp,
PRIMARY KEY (key1,key2,type)
);
');
}
} 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);
} else {
$this->insert($type, $key1, $key2, $value, $duration);
}
}
private function insert($type, $key1, $key2, $value, $duration = null)
{
$expire = is_null($duration) ? null : (time() + $duration);
$query = "INSERT INTO data (key1, key2, type, created, updated, expire, value)".
" VALUES(:key1, :key2, :type, :created, :updated, :expire, :value)";
$prepared = $this->db->prepare($query);
$data = array(':key1' => $key1, ':key2' => $key2,
':type' => $type, ':created' => time(),
':updated' => time(), ':expire' => $expire,
':value' => serialize($value));
$prepared->execute($data);
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
return $results;
}
private function update($type, $key1, $key2, $value, $duration = null)
{
$expire = is_null($duration) ? null : (time() + $duration);
$query = "UPDATE data SET updated = :updated, value = :value, expire = :expire WHERE key1 = :key1 AND key2 = :key2 AND type = :type";
$prepared = $this->db->prepare($query);
$data = array(':key1' => $key1, ':key2' => $key2,
':type' => $type, ':updated' => time(),
':expire' => $expire, ':value' => serialize($value));
$prepared->execute($data);
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
return $results;
}
public function get($type = null, $key1 = null, $key2 = null)
{
$conditions = self::getCondition($type, $key1, $key2);
$query = 'SELECT * FROM data WHERE '.$conditions;
$prepared = $this->db->prepare($query);
$prepared->execute();
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
if (count($results) !== 1) {
return null;
}
$res = $results[0];
$res['value'] = unserialize($res['value']);
return $res;
}
/*
* Return the value directly (not in a container)
*/
public function getValue($type = null, $key1 = null, $key2 = null)
{
$res = $this->get($type, $key1, $key2);
if ($res === null) {
return null;
}
return $res['value'];
}
public function exists($type, $key1, $key2)
{
$query = 'SELECT * FROM data WHERE type = :type AND key1 = :key1 AND key2 = :key2 LIMIT 1';
$prepared = $this->db->prepare($query);
$data = array(':type' => $type, ':key1' => $key1, ':key2' => $key2);
$prepared->execute($data);
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
return (count($results) == 1);
}
public function getList($type = null, $key1 = null, $key2 = null)
{
$conditions = self::getCondition($type, $key1, $key2);
$query = 'SELECT * FROM data WHERE '.$conditions;
$prepared = $this->db->prepare($query);
$prepared->execute();
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
if (count($results) == 0) {
return null;
}
foreach ($results as $key => $value) {
$results[$key]['value'] = unserialize($results[$key]['value']);
}
return $results;
}
public function getKeys($type = null, $key1 = null, $key2 = null, $whichKey = 'type')
{
if (!in_array($whichKey, array('key1', 'key2', 'type'), true)) {
throw new Exception('Invalid key type');
}
$conditions = self::getCondition($type, $key1, $key2);
$query = 'SELECT DISTINCT :whichKey FROM data WHERE '.$conditions;
$prepared = $this->db->prepare($query);
$data = array('whichKey' => $whichKey);
$prepared->execute($data);
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
if (count($results) == 0) {
return null;
}
$resarray = array();
foreach ($results as $key => $value) {
$resarray[] = $value[$whichKey];
}
return $resarray;
}
public function remove($type, $key1, $key2)
{
$query = 'DELETE FROM data WHERE type = :type AND key1 = :key1 AND key2 = :key2';
$prepared = $this->db->prepare($query);
$data = array(':type' => $type, ':key1' => $key1, ':key2' => $key2);
$prepared->execute($data);
$results = $prepared->fetchAll(PDO::FETCH_ASSOC);
return (count($results) == 1);
}
public function removeExpired()
{
$query = "DELETE FROM data WHERE expire NOT NULL AND expire < :expire";
$prepared = $this->db->prepare($query);
$data = array(':expire' => time());
$prepared->execute($data);
return $prepared->rowCount();
}
/**
* Create a SQL condition statement based on parameters
*/
private function getCondition($type = null, $key1 = null, $key2 = null)
{
$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