Gutted config system to use a PHP array instead of 1980's INI files. Broke down the database layer to support multiple types of interfaces and started laying out the Mongo interface class.
This commit is contained in:
parent
9fe5ce72d4
commit
630a1276aa
10 changed files with 437 additions and 331 deletions
|
@ -16,317 +16,81 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Database Abstraction Layer for MySQL
|
||||
* Database Factory
|
||||
*
|
||||
* All database usage inside PICKLES-based sites should be done via the
|
||||
* database object that is a part of every model ($this->db). Because the
|
||||
* database object can execute raw SQL, there should be no limitations. Within
|
||||
* the PICKLES system, the database instance is shared, but the Database
|
||||
* constructor is not private, just in case someone wants to create new
|
||||
* Database objects all willy nilly like.
|
||||
* Generic class to simplify connecting to a database. All database objects
|
||||
* should be created by this class to future proof against any internal changes
|
||||
* to PICKLES.
|
||||
*/
|
||||
class Database extends Object
|
||||
{
|
||||
/**
|
||||
* Hostname for the MySQL Server
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $hostname = 'localhost';
|
||||
|
||||
/**
|
||||
* Username for the MySQL Server
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $username = null;
|
||||
|
||||
/**
|
||||
* Password for the MySQL Server
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $password = null;
|
||||
|
||||
/**
|
||||
* Database name for the MySQL Server
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $database = null;
|
||||
|
||||
/**
|
||||
* Connection resource to MySQL
|
||||
*
|
||||
* @access private
|
||||
* @var object
|
||||
*/
|
||||
private $connection = null;
|
||||
|
||||
/**
|
||||
* Results object for the executed statement
|
||||
*
|
||||
* @access private
|
||||
* @var object
|
||||
*/
|
||||
private $results = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Sets up our connection variables
|
||||
* Attempts to get an instance of the passed database type or attempts to
|
||||
* use a default specified in the config.
|
||||
*
|
||||
* @param string $hostname optional hostname to connect to
|
||||
* @param string $username optional username to use
|
||||
* @param string $password optional password to use
|
||||
* @param string $database optional database to connect to
|
||||
* @param string $name optional name of the connection to use
|
||||
*/
|
||||
public function __construct($hostname = null, $username = null, $password = null, $database = null)
|
||||
public function __construct(String $name = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$config = $this->config->database;
|
||||
|
||||
// Loads the credentials and database, arguments first
|
||||
if (isset($username, $password, $database))
|
||||
{
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->database = $database;
|
||||
}
|
||||
elseif (isset($config['username'], $config['password'], $config['database']))
|
||||
{
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
$this->database = $config['database'];
|
||||
}
|
||||
|
||||
// Loads or defaults the host name
|
||||
if (isset($hostname))
|
||||
{
|
||||
$this->hostname = $hostname;
|
||||
}
|
||||
elseif (isset($config['hostname']))
|
||||
{
|
||||
$this->hostname = $config['hostname'];
|
||||
}
|
||||
return Database::getInstance($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instance of the object
|
||||
* Get instance
|
||||
*
|
||||
* Let's the parent class do all the work
|
||||
* Looks up the datasource using the passed name and gets an instance of
|
||||
* it. Allows for easy sharing of certain classes within the system to
|
||||
* avoid the extra overhead of creating new objects each time. Also avoids
|
||||
* the hassle of passing around variables (yeah I know, very global-ish)
|
||||
*
|
||||
* @static
|
||||
* @param string $class name of the class to instantiate
|
||||
* @return object self::$instance instance of the Config class
|
||||
* @param string $name name of the datasource
|
||||
* @return object instance of the class
|
||||
*/
|
||||
public static function getInstance($class = 'Database')
|
||||
public static function getInstance($name = null)
|
||||
{
|
||||
return parent::getInstance($class);
|
||||
}
|
||||
$config = Config::getInstance();
|
||||
|
||||
/**
|
||||
* Opens database connection
|
||||
*
|
||||
* Establishes a connection to the MySQL database based on the
|
||||
* configuration options that are available in the Config object.
|
||||
*
|
||||
* @return boolean true on success, throws an exception overwise
|
||||
*/
|
||||
public function open()
|
||||
{
|
||||
if ($this->connection === null)
|
||||
// Checks if we have a default
|
||||
if ($name == null)
|
||||
{
|
||||
if (isset($this->username, $this->password, $this->database))
|
||||
if (is_array($config->datasources))
|
||||
{
|
||||
// Creates a new PDO database object (persistent)
|
||||
try
|
||||
{
|
||||
$pdo_attributes = array(
|
||||
PDO::ATTR_PERSISTENT => true,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::NULL_EMPTY_STRING => true
|
||||
);
|
||||
|
||||
$this->connection = new PDO('mysql:host=' . $this->hostname . ';dbname=' . $this->database, $this->username, $this->password, $pdo_attributes);
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new Exception($e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('There was an error loading the database configuration');
|
||||
$datasources = $config->datasources;
|
||||
$name = key($datasources);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes database connection
|
||||
*
|
||||
* Sets the connection to null regardless of state.
|
||||
*
|
||||
* @return boolean always true
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->connection = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an SQL Statement
|
||||
*
|
||||
* Executes a standard or prepared query based on passed parameters. All
|
||||
* queries are logged to a file as well as timed and logged in the
|
||||
* execution time is over 1 second.
|
||||
*
|
||||
* @param string $sql statement to execute
|
||||
* @param array $input_parameters optional key/values to be bound
|
||||
* @return integer ID of the last inserted row or sequence number
|
||||
*/
|
||||
public function execute($sql, $input_parameters = null)
|
||||
{
|
||||
$this->open();
|
||||
|
||||
$loggable_query = $sql;
|
||||
|
||||
if ($input_parameters != null)
|
||||
if ($name != null)
|
||||
{
|
||||
$loggable_query .= ' -- ' . (JSON_AVAILABLE ? json_encode($input_parameters) : serialize($input_parameters));
|
||||
}
|
||||
|
||||
Log::query($loggable_query);
|
||||
|
||||
// Checks if the query is blank
|
||||
if (trim($sql) != '')
|
||||
{
|
||||
try
|
||||
if (isset($config->datasources[$name]))
|
||||
{
|
||||
// Establishes if the profiler is enabled
|
||||
$profiler = (isset($this->config->pickles['profiler']) && $this->config->pickles['profiler'] == true && preg_match('/^EXPLAIN /i', $sql) == false);
|
||||
$datasource = $config->datasources[$name];
|
||||
$datasource['type'] = strtolower($datasource['type']);
|
||||
|
||||
// Executes a standard query
|
||||
if ($input_parameters === null)
|
||||
switch ($datasource['type'])
|
||||
{
|
||||
// Explains the query
|
||||
if ($profiler == true)
|
||||
{
|
||||
$explain = $this->fetchAll('EXPLAIN ' . $sql);
|
||||
}
|
||||
case 'mysql':
|
||||
if (!isset(self::$instances['Database'][$name]))
|
||||
{
|
||||
self::$instances['Database'][$name] = new Database_MySQL($datasource['hostname'], $datasource['username'], $datasource['password'], $datasource['database']);
|
||||
}
|
||||
|
||||
$start_time = microtime(true);
|
||||
$this->results = $this->connection->query($sql);
|
||||
}
|
||||
// Executes a prepared statement
|
||||
else
|
||||
{
|
||||
// Explains the query
|
||||
if ($profiler == true)
|
||||
{
|
||||
$explain = $this->fetchAll('EXPLAIN ' . $sql, $input_parameters);
|
||||
}
|
||||
|
||||
$start_time = microtime(true);
|
||||
$this->results = $this->connection->prepare($sql);
|
||||
$this->results->execute($input_parameters);
|
||||
}
|
||||
|
||||
$end_time = microtime(true);
|
||||
$duration = $end_time - $start_time;
|
||||
|
||||
if ($duration >= 1)
|
||||
{
|
||||
Log::slowQuery($duration . ' seconds: ' . $loggable_query);
|
||||
}
|
||||
|
||||
// Logs the information to the profiler
|
||||
if ($profiler == true)
|
||||
{
|
||||
Profiler::logQuery($sql, $input_parameters, isset($explain) ? $explain : false, $duration);
|
||||
return self::$instances['Database'][$name];
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Datasource type "' . $datasource['type'] . '" is invalid');
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (PDOException $e)
|
||||
{
|
||||
throw new Exception($e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('No query to execute');
|
||||
}
|
||||
|
||||
return $this->connection->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single row from the database
|
||||
*
|
||||
* @param string $sql statement to be executed
|
||||
* @param array $input_parameters optional key/values to be bound
|
||||
* @param string $return_type optional type of return set
|
||||
* @return mixed based on return type
|
||||
*/
|
||||
public function fetch($sql = null, $input_parameters = null, $return_type = null)
|
||||
{
|
||||
$this->open();
|
||||
|
||||
if ($sql !== null)
|
||||
{
|
||||
$this->execute($sql, $input_parameters);
|
||||
}
|
||||
|
||||
// Pulls the results based on the type
|
||||
$results = false;
|
||||
switch ($return_type)
|
||||
{
|
||||
case 'column':
|
||||
$results = $this->results->fetchColumn(0);
|
||||
break;
|
||||
case 'all':
|
||||
$results = $this->results->fetchAll(PDO::FETCH_ASSOC);
|
||||
break;
|
||||
default:
|
||||
$results = $this->results->fetch(PDO::FETCH_ASSOC);
|
||||
break;
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a single column from the database
|
||||
*
|
||||
* This method assumes you want the first column in your select. If you
|
||||
* need 2 or more columns you should simply use fetch().
|
||||
*
|
||||
* @param string $sql statement to be executed
|
||||
* @param array $input_parameters optional key/values to be bound
|
||||
* @return string
|
||||
*/
|
||||
public function fetchColumn($sql = null, $input_parameters = null)
|
||||
{
|
||||
return $this->fetch($sql, $input_parameters, 'column');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all rows as an array
|
||||
*
|
||||
* @param string $sql statement to be executed
|
||||
* @param array $input_parameters optional key/values to be bound
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll($sql = null, $input_parameters = null)
|
||||
{
|
||||
return $this->fetch($sql, $input_parameters, 'all');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue