runff 1.0 commit
This commit is contained in:
293
lib/SimpleSAML/Database.php
Executable file
293
lib/SimpleSAML/Database.php
Executable file
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
namespace SimpleSAML;
|
||||
|
||||
/**
|
||||
* This file implements functions to read and write to a group of database servers.
|
||||
*
|
||||
* This database class supports a single database, or a master/slave configuration with as many defined slaves as a
|
||||
* user would like.
|
||||
*
|
||||
* The goal of this class is to provide a single mechanism to connect to a database that can be reused by any component
|
||||
* within SimpleSAMLphp including modules. When using this class, the global configuration should be passed here, but in
|
||||
* the case of a module that has a good reason to use a different database, such as sqlauth, an alternative config file
|
||||
* can be provided.
|
||||
*
|
||||
* @author Tyler Antonio, University of Alberta. <tantonio@ualberta.ca>
|
||||
* @package SimpleSAMLphp
|
||||
*/
|
||||
|
||||
class Database
|
||||
{
|
||||
|
||||
/**
|
||||
* This variable holds the instance of the session - Singleton approach.
|
||||
*/
|
||||
private static $instance = array();
|
||||
|
||||
/**
|
||||
* PDO Object for the Master database server
|
||||
*/
|
||||
private $dbMaster;
|
||||
|
||||
/**
|
||||
* Array of PDO Objects for configured database slaves
|
||||
*/
|
||||
private $dbSlaves = array();
|
||||
|
||||
/**
|
||||
* Prefix to apply to the tables
|
||||
*/
|
||||
private $tablePrefix;
|
||||
|
||||
/**
|
||||
* Array with information on the last error occurred.
|
||||
*/
|
||||
private $lastError;
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the current database instance. Will create a new one if there isn't an existing connection.
|
||||
*
|
||||
* @param \SimpleSAML_Configuration $altConfig Optional: Instance of a SimpleSAML_Configuration class
|
||||
*
|
||||
* @return \SimpleSAML\Database The shared database connection.
|
||||
*/
|
||||
public static function getInstance($altConfig = null)
|
||||
{
|
||||
$config = ($altConfig) ? $altConfig : \SimpleSAML_Configuration::getInstance();
|
||||
$instanceId = self::generateInstanceId($config);
|
||||
|
||||
// check if we already have initialized the session
|
||||
if (isset(self::$instance[$instanceId])) {
|
||||
return self::$instance[$instanceId];
|
||||
}
|
||||
|
||||
// create a new session
|
||||
self::$instance[$instanceId] = new Database($config);
|
||||
return self::$instance[$instanceId];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor that restricts instantiation to getInstance().
|
||||
*
|
||||
* @param \SimpleSAML_Configuration $config Instance of the SimpleSAML_Configuration class
|
||||
*/
|
||||
private function __construct($config)
|
||||
{
|
||||
$driverOptions = $config->getArray('database.driver_options', array());
|
||||
if ($config->getBoolean('database.persistent', true)) {
|
||||
$driverOptions = array(\PDO::ATTR_PERSISTENT => true);
|
||||
}
|
||||
|
||||
// connect to the master
|
||||
$this->dbMaster = $this->connect(
|
||||
$config->getString('database.dsn'),
|
||||
$config->getString('database.username', null),
|
||||
$config->getString('database.password', null),
|
||||
$driverOptions
|
||||
);
|
||||
|
||||
// connect to any configured slaves
|
||||
$slaves = $config->getArray('database.slaves', array());
|
||||
foreach ($slaves as $slave) {
|
||||
array_push(
|
||||
$this->dbSlaves,
|
||||
$this->connect(
|
||||
$slave['dsn'],
|
||||
$slave['username'],
|
||||
$slave['password'],
|
||||
$driverOptions
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->tablePrefix = $config->getString('database.prefix', '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate an Instance ID based on the database configuration.
|
||||
*
|
||||
* @param \SimpleSAML_Configuration $config Configuration class
|
||||
*
|
||||
* @return string $instanceId
|
||||
*/
|
||||
private static function generateInstanceId($config)
|
||||
{
|
||||
$assembledConfig = array(
|
||||
'master' => array(
|
||||
'database.dsn' => $config->getString('database.dsn'),
|
||||
'database.username' => $config->getString('database.username', null),
|
||||
'database.password' => $config->getString('database.password', null),
|
||||
'database.prefix' => $config->getString('database.prefix', ''),
|
||||
'database.persistent' => $config->getBoolean('database.persistent', false),
|
||||
),
|
||||
'slaves' => $config->getArray('database.slaves', array()),
|
||||
);
|
||||
|
||||
return sha1(serialize($assembledConfig));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function connects to a database.
|
||||
*
|
||||
* @param string $dsn Database connection string
|
||||
* @param string $username SQL user
|
||||
* @param string $password SQL password
|
||||
* @param array $options PDO options
|
||||
*
|
||||
* @throws \Exception If an error happens while trying to connect to the database.
|
||||
* @return \PDO object
|
||||
*/
|
||||
private function connect($dsn, $username, $password, $options)
|
||||
{
|
||||
try {
|
||||
$db = new \PDO($dsn, $username, $password, $options);
|
||||
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
return $db;
|
||||
} catch (\PDOException $e) {
|
||||
throw new \Exception("Database error: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function randomly selects a slave database server to query. In the event no slaves are configured, it will
|
||||
* return the master.
|
||||
*
|
||||
* @return \PDO object
|
||||
*/
|
||||
private function getSlave()
|
||||
{
|
||||
if (count($this->dbSlaves) > 0) {
|
||||
$slaveId = rand(0, count($this->dbSlaves) - 1);
|
||||
return $this->dbSlaves[$slaveId];
|
||||
} else {
|
||||
return $this->dbMaster;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function simply applies the table prefix to a supplied table name.
|
||||
*
|
||||
* @param string $table Table to apply prefix to, if configured
|
||||
*
|
||||
* @return string Table with configured prefix
|
||||
*/
|
||||
public function applyPrefix($table)
|
||||
{
|
||||
return $this->tablePrefix.$table;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function queries the database
|
||||
*
|
||||
* @param \PDO $db PDO object to use
|
||||
* @param string $stmt Prepared SQL statement
|
||||
* @param array $params Parameters
|
||||
*
|
||||
* @throws \Exception If an error happens while trying to execute the query.
|
||||
* @return \PDOStatement object
|
||||
*/
|
||||
private function query($db, $stmt, $params)
|
||||
{
|
||||
assert(is_object($db));
|
||||
assert(is_string($stmt));
|
||||
assert(is_array($params));
|
||||
|
||||
try {
|
||||
$query = $db->prepare($stmt);
|
||||
|
||||
foreach ($params as $param => $value) {
|
||||
if (is_array($value)) {
|
||||
$query->bindValue(":$param", $value[0], ($value[1]) ? $value[1] : \PDO::PARAM_STR);
|
||||
} else {
|
||||
$query->bindValue(":$param", $value, \PDO::PARAM_STR);
|
||||
}
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
|
||||
return $query;
|
||||
} catch (\PDOException $e) {
|
||||
$this->lastError = $db->errorInfo();
|
||||
throw new \Exception("Database error: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function queries the database without using a prepared statement.
|
||||
*
|
||||
* @param \PDO $db PDO object to use
|
||||
* @param string $stmt An SQL statement to execute, previously escaped.
|
||||
*
|
||||
* @throws \Exception If an error happens while trying to execute the query.
|
||||
* @return int The number of rows affected.
|
||||
*/
|
||||
private function exec($db, $stmt)
|
||||
{
|
||||
assert(is_object($db));
|
||||
assert(is_string($stmt));
|
||||
|
||||
try {
|
||||
return $db->exec($stmt);
|
||||
} catch (\PDOException $e) {
|
||||
$this->lastError = $db->errorInfo();
|
||||
throw new \Exception("Database error: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This executes queries directly on the master.
|
||||
*
|
||||
* @param string $stmt Prepared SQL statement
|
||||
* @param array $params Parameters
|
||||
*
|
||||
* @return int The number of rows affected by the query.
|
||||
*/
|
||||
public function write($stmt, $params = array())
|
||||
{
|
||||
$db = $this->dbMaster;
|
||||
|
||||
if (is_array($params)) {
|
||||
$obj = $this->query($db, $stmt, $params);
|
||||
return $obj->rowCount();
|
||||
} else {
|
||||
return $this->exec($db, $stmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This executes queries on a database server that is determined by this::getSlave().
|
||||
*
|
||||
* @param string $stmt Prepared SQL statement
|
||||
* @param array $params Parameters
|
||||
*
|
||||
* @return \PDOStatement object
|
||||
*/
|
||||
public function read($stmt, $params = array())
|
||||
{
|
||||
$db = $this->getSlave();
|
||||
|
||||
return $this->query($db, $stmt, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return an array with information about the last operation performed in the database.
|
||||
*
|
||||
* @return array The array with error information.
|
||||
*/
|
||||
public function getLastError()
|
||||
{
|
||||
return $this->lastError;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user