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:
Josh Sherman 2010-10-12 01:44:10 -04:00
parent 9fe5ce72d4
commit 630a1276aa
10 changed files with 437 additions and 331 deletions

View file

@ -44,7 +44,7 @@ class Config extends Object
*
* @param string $filename optional Filename of the config
*/
public function __construct($filename = '../config.ini')
public function __construct($filename = '../config.php')
{
parent::__construct();
@ -54,46 +54,87 @@ class Config extends Object
/**
* Loads a configuration file
*
* Handles the potential loading of the configuration file and sanitizing
* the boolean strings into actual boolean values.
*
* @param string $filename filename of the config file
* @param boolean $merge whether or not the data should be merged
* @return boolean success of the load process
*/
public function load($filename, $merge = false)
public function load($filename)
{
$environments = false;
$environment = false;
// Sanity checks the config file
if (file_exists($filename) && is_file($filename) && is_readable($filename))
{
$config = parse_ini_file($filename, true);
require_once $filename;
if ($merge == false)
// Determines the environment
if (isset($config['environment']))
{
$this->data = $config;
$environment = $config['environment'];
}
else
{
foreach ($config as $section => $variables)
if (isset($config['environments']) && is_array($config['environments']))
{
if (!isset($this->data[$section]))
$environments = $config['environments'];
// Loops through the environments and tries to match on IP or name
foreach ($config['environments'] as $name => $host)
{
$this->data[$section] = array();
}
foreach ($variables as $variable => $value)
{
$this->data[$section][$variable] = $value;
if ((preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $host) && $_SERVER['SERVER_ADDR'] == $host) || $_SERVER['SERVER_NAME'] == $host)
{
// Sets the environment and makes a run for it
$environment = $name;
break;
}
}
}
}
// Flattens the array based on the environment
$this->data = $this->flatten($environment, $config);
// Restore environments value
if ($environments != false)
{
$this->data['environments'] = $environments;
}
// Sets the environment if it's not set already
if (!isset($this->data['environment']))
{
$this->data['environment'] = $environment;
}
return true;
}
else
return false;
}
private function flatten($environment, $array)
{
if (is_array($array))
{
Error::fatal(basename($filename) . ' is either missing or unreadable');
foreach ($array as $key => $value)
{
if (is_array($value))
{
if (isset($value[$environment]))
{
$value = $value[$environment];
}
else
{
$value = $this->flatten($environment, $value);
}
}
$array[$key] = $value;
}
}
return $array;
}
/**

View file

@ -37,17 +37,12 @@ class Controller extends Object
{
parent::__construct();
// Generate a generic "site down" message
if ($this->config->pickles['disabled'])
// Generate a generic "site down" message if the site is set to be disabled
if (isset($this->config->pickles['disabled']) && $this->config->pickles['disabled'] == true)
{
throw new Exception($_SERVER['SERVER_NAME'] . ' is currently<br />down for maintenance');
}
// Ack, not sure what page to load, throw an error
if (!isset($_REQUEST['request']) && (empty($this->config->pickles['default']) || $this->config->pickles['default'] == null))
{
throw new Exception('Unable complete this request because no URI was provided and there is no default module specified in config.ini');
}
// Loads the requested module's information
if (isset($_REQUEST['request']) && trim($_REQUEST['request']) != '')
{
@ -78,7 +73,9 @@ class Controller extends Object
// Loads the default module information (if any)
else
{
list($basename, $module_class, $module_filename, $template_basename, $css_class, $js_basename) = $this->prepareVariables($this->config->pickles['default']);
$default_module = isset($this->config->pickles['module']) ? $this->config->pickles['module'] : 'home';
list($basename, $module_class, $module_filename, $template_basename, $css_class, $js_basename) = $this->prepareVariables($default_module);
}
unset($basename);

View file

@ -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;
}
}

View file

@ -0,0 +1,31 @@
<?php
/**
* Mongo Interface Class File for PICKLES
*
* PHP version 5
*
* Licensed under the GNU General Public License Version 3
* Redistribution of these files must retain the above copyright notice.
*
* @package PICKLES
* @author Josh Sherman <josh@phpwithpickles.org>
* @copyright Copyright 2007-2010, Gravity Boulevard, LLC
* @license http://www.gnu.org/licenses/gpl.html GPL v3
* @link http://phpwithpickles.org
*/
/**
* Mongo Database Abstraction Layer
*
* Requires PECL mongo >= 1.0.10
* Note: PECL mongo 1.0.10 is included /vendors/pecl
*
* @link http://pecl.php.net/package/mongo
* @link http://us2.php.net/manual/en/book.mongo.php
*/
class Database_Mongo extends Object
{
}
?>

298
classes/Database/MySQL.php Normal file
View file

@ -0,0 +1,298 @@
<?php
/**
* MySQL Class File for PICKLES
*
* PHP version 5
*
* Licensed under the GNU General Public License Version 3
* Redistribution of these files must retain the above copyright notice.
*
* @package PICKLES
* @author Josh Sherman <josh@phpwithpickles.org>
* @copyright Copyright 2007-2010, Gravity Boulevard, LLC
* @license http://www.gnu.org/licenses/gpl.html GPL v3
* @link http://phpwithpickles.org
*/
/**
* MySQL Database Abstraction Layer
*
* Should not be instantiating this class directly, but instead using the
* Database class as a factory for this class.
*
* @todo Add in a check if the class is being called directly and except.
*/
class Database_MySQL 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
*
* @param string $hostname hostname to connect to
* @param string $username username to use
* @param string $password password to use
* @param string $database database to connect to
*/
public function __construct($hostname, $username, $password, $database)
{
parent::__construct();
$this->hostname = $hostname;
$this->username = $username;
$this->password = $password;
$this->database = $database;
return true;
}
/**
* 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)
{
if (isset($this->username, $this->password, $this->database))
{
// 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');
}
}
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)
{
$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
{
// Establishes if the profiler is enabled
$profiler = (isset($this->config->pickles['profiler']) && $this->config->pickles['profiler'] == true && preg_match('/^EXPLAIN /i', $sql) == false);
// Executes a standard query
if ($input_parameters === null)
{
// Explains the query
if ($profiler == true)
{
$explain = $this->fetchAll('EXPLAIN ' . $sql);
}
$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);
}
}
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');
}
}
?>

View file

@ -61,7 +61,7 @@ class Display_PHP extends Display_Common
ob_start();
// Puts the class variables in local scope for the template
$form_class = (isset($this->config->pickles['form']) ? $this->config->pickles['form'] : 'Form');
$form_class = (isset($this->config->pickles['classes']['Form']) ? $this->config->pickles['classes']['Form'] : 'Form');
$__config = $this->config;
$__meta = $this->meta_data;

View file

@ -31,7 +31,7 @@ class Object
* @access private
* @var mixed
*/
private static $instances = array();
protected static $instances = array();
/**
* Instance of the Config object

View file

@ -72,36 +72,11 @@ if (ini_get('date.timezone') == '')
// }}}
// {{{ Loads the configuration file(1)
// {{{ Loads the configuration file and sets any configuration options
// Loads the base config
$config = Config::getInstance();
// Determines the environment
if ($config->environment != false && is_array($config->environment))
{
$environment = false;
// Loops through the environments and tries to match on IP or name
foreach ($config->environment as $name => $host)
{
if ((preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $host) && $_SERVER['SERVER_ADDR'] == $host) || $_SERVER['SERVER_NAME'] == $host)
{
// Tries to load the environment config
$environment_config = SITE_PATH . $name . '.ini';
if (file_exists($environment_config))
{
$config->load($environment_config, true);
}
break;
}
}
}
// }}}
// {{{ Sets any configurable options
// Configures any available PHP configuration options
if (isset($config->php['display_error']))
{

BIN
vendors/pecl/mongo-1.0.10.tgz vendored Normal file

Binary file not shown.