Updated the Error class from a static class to an instantiatable one.

git-svn-id: http://svn.cleancode.org/svn/pickles@68 4d10bc64-7434-11dc-a737-d2d0f8310089
This commit is contained in:
Josh Sherman 2008-10-11 20:16:47 +00:00
parent ecf86b9818
commit 20fd236cb0
9 changed files with 177 additions and 255 deletions

View file

@ -30,15 +30,9 @@
* Handles loading a configuration file and parsing the data for
* any public nodes that need to be made available to the viewer.
*
* @usage <code>$config = Config::getInstance();
*$config->node; // Returns SimpleXML object</code>
* @usage <code>$config = new Config($filename); // $filename is optional, default = ../config.xml</code>
*/
class Config {
/**
* Private instance of the Config class
*/
private static $_instance;
class Config extends Object {
/**
* Private collection of public config data
@ -46,32 +40,12 @@ class Config {
private $_public;
/**
* Private constructor
* Constructor
*/
private function __construct() { }
public function __construct($file = '../config.xml') {
parent::__construct($this);
/**
* __clone
*/
public function __clone() {
trigger_error('Cloning is not available on a Singleton (that would defeat the purpose wouldn\'t it?)', E_USER_ERROR);
}
/**
* Gets an instance of the requested class object
*
* Determines if a requested class object has already
* been instantiated, if so it will use it. If not,
* it will create one.
*
* @return An instace of the requested class
*/
public static function getInstance() {
if (!self::$_instance instanceof Config) {
self::$_instance = new Config();
}
return self::$_instance;
$this->load($file);
}
/**
@ -84,9 +58,7 @@ class Config {
* @return boolean Success of the load process
* @todo Add the ability to load in multiple configuration files.
*/
public static function load($file) {
$config = Config::getInstance();
public function load($file) {
if (file_exists($file)) {
/**
@ -103,10 +75,10 @@ class Config {
if (is_array($variables)) {
foreach ($variables as $key => $value) {
if (is_object($value) && isset($value->attributes()->public) && $value->attributes()->public == true) {
$config->_public[$key] = $value;
$this->_public[$key] = $value;
}
$config->$key = $value;
$this->$key = $value;
}
}
@ -212,6 +184,35 @@ class Config {
return 'home';
}
/**
* Gets the mapping for a table
*
* @param string $requested_table The table being requested
* @return array The mapping for the table
*/
public function getTableMapping($requested_table, $default_mapping) {
$table_mapping = $default_mapping;
if (isset($this->mappings->table)) {
foreach ($this->mappings->table as $table) {
if (isset($table->name) && trim($table->name) != '' && $table->name == $requested_table) {
$table_mapping['name'] = (string)(isset($table->alias) && trim($table->alias) != '' ? $table->alias : $table->name);
if (isset($table->fields->field)) {
foreach ($table->fields->field as $field) {
if (isset($field->name) && trim($field->name) != '') {
$table_mapping['fields'][(string)$field->name] = (string)(isset($field->alias) && trim($field->alias) != '' ? $field->alias : $field->name);
}
}
}
}
}
}
return $table_mapping;
}
/**
* Gets the viewer value
*

View file

@ -32,7 +32,7 @@
* the model asks for it, and loads the viewer that the model has requested.
* Default values are present to make things easier on the user.
*
* @usage <code>new Controller();</code>
* @usage <code>new Controller($config);</code>
*/
class Controller extends Object {
@ -41,14 +41,15 @@ class Controller extends Object {
*
* To make life a bit easier when using PICKLES, the Controller logic is
* executed automatically via use of a constructor.
*
* @param object Config object
*/
public function __construct() {
parent::__construct();
public function __construct(Config $config) {
parent::__construct($config);
// Load the config for the site passed in
$config = Config::getInstance();
$db = new DB($config->database->hostname, $config->database->username, $config->database->password, $config->database->database);
$logger = new Logger();
$error = new Error($config, $logger);
$db = new DB($config, $error);
// Generate a generic "site down" message
if ($config->getDisabled()) {
@ -64,7 +65,8 @@ class Controller extends Object {
* yet, they want to next it, like /users/logout?
*/
if ($model_name == 'logout') {
Security::logout();
$security = new Security($config, $db);
$security->logout();
}
else {
// Loads the requested model's information
@ -126,7 +128,10 @@ class Controller extends Object {
// Potentially requests use authentication
if ($model->getAuthentication() === true) {
Security::authenticate();
if (!isset($security)) {
$security = new Security($config, $db);
}
$security->authenticate();
}
// Potentially executes the model's logic
@ -135,7 +140,8 @@ class Controller extends Object {
}
// Creates a new viewer object
$viewer = Viewer::create($model->getViewer());
$viewer_class = 'Viewer_' . $model->getViewer();
$viewer = new $viewer_class($config, null);
// Sets the viewers properties
$viewer->model_name = $model_name;

View file

@ -38,7 +38,9 @@
* @todo Eventually finish adding in my ActiveRecord class, even
* though I feel active record dumbs people down since it's a
* crutch for actually being able to write SQL.
* @todo Rename me to Database (maybe)
* @todo Rename DB to Database (maybe, as to not conflict with the
* PEAR class or any other generically named class... until
* we have namespaces))
*/
class DB extends Object {
@ -47,17 +49,23 @@ class DB extends Object {
private $password;
private $database;
private $error;
/**
* Private MySQL resources
*/
private $connection;
private $results;
public function __construct($hostname, $username, $password, $database) {
$this->hostname = $hostname;
$this->username = $username;
$this->password = $password;
$this->database = $database;
public function __construct(Config $config, Error $error = null) {
parent::__construct($config);
$this->error = isset($error) ? $error : new Error($config);
$this->hostname = $config->database->hostname;
$this->username = $config->database->username;
$this->password = $config->database->password;
$this->database = $config->database->database;
}
/**
@ -75,14 +83,11 @@ class DB extends Object {
}
if (isset($this->username, $this->password, $this->database)) {
/**
* @todo I removed the @ and changed to be pconnect... let's see
*/
$this->connection = mysql_pconnect($this->hostname, $this->username, $this->password);
if (is_resource($this->connection)) {
if (!mysql_select_db($this->database, $this->connection)) {
Error::addWarning("There was an error selecting the '" . $this->database , "' database");
$this->error->addWarning('There was an error selecting the "' . $this->database . '" database');
return false;
}
else {
@ -90,13 +95,13 @@ class DB extends Object {
}
}
else {
Error::addError('There was an error connecting to the database server');
$this->error->addError('There was an error connecting to the database server');
}
return false;
}
else {
Error::addError('There was an error loading the configuration');
$this->error->addError('There was an error loading the database configuration');
}
return false;
@ -136,15 +141,15 @@ class DB extends Object {
if (trim($sql) != '') {
$this->results = mysql_query($sql, $this->connection);
if (empty($this->results)) {
Error::addError('There was an error executing the SQL');
Error::addError(mysql_error());
$this->error->addError('There was an error executing the SQL');
$this->error->addError(mysql_error());
}
else {
return true;
}
}
else {
Error::addWarning('There was no SQL to execute');
$this->error->addWarning('There was no SQL to execute');
}
return false;
@ -180,11 +185,11 @@ class DB extends Object {
return $results[0];
}
else {
Error::addWarning('There is nothing to return');
$this->error->addWarning('There is nothing to return');
}
}
else {
Error::addError('There is no valid MySQL result resource');
$this->error->addError('There is no valid MySQL result resource');
}
return null;
@ -225,11 +230,11 @@ class DB extends Object {
return $results;
}
else {
Error::addWarning('There is nothing to return');
$this->error->addWarning('There is nothing to return');
}
}
else {
Error::addError('There is no valid MySQL result resource');
$this->error->addError('There is no valid MySQL result resource');
}
return null;
@ -265,7 +270,7 @@ class DB extends Object {
return $return;
}
else {
Error::addError('There is no valid MySQL result resource');
$this->error->addError('There is no valid MySQL result resource');
}
return null;
@ -281,35 +286,34 @@ class DB extends Object {
* @param array $columnValues Associative array of name value pairs
* (Corresponds with the column names for the table)
* @return boolean Returns the status of the execution
* @todo Convert from camel case to underscores
* @todo Check that the table exists, and possibly check that the
* columns exist as well
*/
public function insert($table, $columnValues) {
public function insert($table, $values) {
$this->open();
if (trim($table) != '') {
if (is_array($columnValues)) {
foreach ($columnValues as $key => $value) {
$columnValues[$key] = $value == null ? 'NULL' : "'" . mysql_real_escape_string(stripslashes($value), $this->connection) . "'";
if (is_array($values)) {
foreach ($values as $key => $value) {
$values[$key] = $value == null ? 'NULL' : "'" . mysql_real_escape_string(stripslashes($value), $this->connection) . "'";
}
$this->execute("
INSERT INTO {$table} (
" . implode(array_keys($columnValues), ', ') . "
" . implode(array_keys($values), ', ') . "
) VALUES (
" . implode($columnValues, ", ") . "
" . implode($values, ", ") . "
);
");
return mysql_insert_id($this->connection);
}
else {
Error::addError('No data was specified');
$this->error->addError('No data was specified');
}
}
else {
Error::addError('No database table was specified');
$this->error->addError('No database table was specified');
}
return false;
@ -327,17 +331,16 @@ class DB extends Object {
* @params array $conditions Associative array of name value pairs that
will be used to create a WHERE clause in the SQL.
* @return boolean Returns the status of the execution
* @todo Convert from camel case to underscores
* @todo Check that the table exists, and possibly check that the
* columns exist and conditional columns exist as well
*/
public function update($table, $columnValues, $conditions) {
public function update($table, $values, $conditions) {
$this->open();
if (trim($table) != '') {
$fields = $where = null;
if (is_array($columnValues)) {
foreach ($columnValues as $key => $value) {
if (is_array($values)) {
foreach ($values as $key => $value) {
$fields .= ($fields ? ', ' : null) . $key . " = '" . mysql_real_escape_string(stripslashes($value), $this->connection) . "'";
}
@ -359,15 +362,15 @@ class DB extends Object {
}
}
else {
Error::addError('No conditions were specified');
$this->error->addError('No conditions were specified');
}
}
else {
Error::addError('No data was specified');
$this->error->addError('No data was specified');
}
}
else {
Error::addError('No database table was specified');
$this->error->addError('No database table was specified');
}
return false;
@ -387,9 +390,8 @@ class DB extends Object {
* will be used to create a WHERE clause in the SQL.
* @return boolean Returns the status of the execution
* @todo This function doesn't exist yet
* @todo Convert from camel case to underscores
*/
public function delete($table, $columnValues, $conditions) {
public function delete($table, $values, $conditions) {
}
}

View file

@ -27,135 +27,78 @@
/**
* Error handling class
*
* Handles (for the most part) all the errors and warnings that are encountered
* by the PICKLES core classes. Unfortunately this was written, but never really
* utilized correctly, so I'm thinking it may be better to remove it entirely or
* add logic to appropriately report the errors, perhaps as a variable in the
* model data array.
*
* @todo Internally document the functions better.
* @todo Convert the class to extend Singleton.
* @todo Quite possibly revamp the class entirely as it is still left over
* from the previous iteration of PICKLES (the system received a kick
* in the ass Q2 of 2008 to move in a more object-oriented direction,
* previously models were procedural files and not instantiated
* classes. I want to thank Joe Stump for the direction I took.
* Handles (for the most part) all the errors and warnings that
* are encountered by the PICKLES core classes. Usage is optional
* for site level code. Errors are logged but it's up to the
* developer to interact with and/or display the errors to their
* end-users.
*/
class Error {
class Error extends Object {
/**
* Private message arrays
*/
private static $errors;
private static $warnings;
private $errors = null;
private $warnings = null;
protected $logger;
/**
* Gets an instance of the object
*
* Determines if the Error object has been instantiated, and if not, it will
* go ahead and instantiate it.
*
* @return object An instance of the Error class
* Constructor
*/
public static function instance() {
static $object;
if (!is_object($object)) {
$object = new Error();
}
return $object;
public function __construct(Config $config, Logger $logger = null) {
$this->logger = isset($logger) ? $logger : new Logger();
}
/**
* Adds an error message
*
* Takes the passed error message and loads it into the private array of
* error messages.
*
* @param string Error message
* @return boolean true
*/
public static function addError($message) {
self::$errors[] = $message;
public function addError($message) {
$this->errors[] = $message;
$this->logger->write('error', '[error] ' . $message);
return true;
}
/**
* Adds a warning message
*
* Takes the passed warning message and loads it into the private array of
* warning messages.
*
* @param string Warning message
* @return boolean true
*/
public static function addWarning($message) {
self::$warnings[] = $message;
public function addWarning($message) {
$this->warnings[] = $message;
$this->logger->write('error', '[warning] ' . $message);
return true;
}
/**
* Gets the stored errors
*
* Returns the errors that have been stored in the private array of error
* messages.
*
* @return array Error messages indexed by the order they were stored
* @return mixed Messages in sequential order or false
*/
public static function getError() {
return self::$errors;
public function getErrors() {
return $this->errors;
}
/**
* Gets the stored warnings
*
* Returns the warnings that have been stored in the private array of warning
* messages.
*
* @return array Warning messages indexed by the order they were stored
*/
public static function getWarning() {
return self::$warnings;
public function getWarnings() {
return $this->warnings;
}
/**
* Determines if there are any stored errors or warnings
*
* Checks the private error and warning arrays and returns the status.
*
* @return boolean Whether or not there are any errors or warnings.
*/
public static function isError() {
if (is_array(self::getError()) || is_array(self::getWarning())) {
return true;
}
return false;
}
/**
* Display errors and warnings
*
* If any errors or warnings are set they are echo'd out separated by XHTML
* compliant line breaks. Also clears out the private arrays upon displaying
* their contents.
*
* @return boolean Whether or not there are any errors or warnings.
*/
public function display() {
if (self::isError()) {
if (self::getError()) {
foreach (self::getError() as $error) {
echo "{$error}<br />";
}
}
if (self::getWarning()) {
foreach (self::getWarning() as $error) {
echo "{$warning}<br />";
}
}
self::$errors = self::$warnings = null;
public function isError() {
if (isset($this->errors, $this->warnings)) {
return true;
}

View file

@ -65,7 +65,7 @@ class Model extends Object {
* internal config and database object
*/
public function __construct($config, $db) {
parent::__construct();
parent::__construct($config);
$this->config = $config;
$this->db = $db;

View file

@ -39,14 +39,34 @@ class Object {
* Protected instance of the Config class
*/
protected $config = null;
/**
* Protected instance of the DB class
*/
protected $db = null;
protected $logger = null;
/**
* Constructor
*
* Handles getting an instance of the Config class.
*
* @param object Config object
*/
public function __construct() {
$this->config = Config::getInstance();
public function __construct(Config $config, DB $db = null) {
if (isset($config)) {
$this->config = $config;
}
if (isset($db)) {
$this->db = $db;
}
$parents = class_parents($this);
$logger = new Logger();
$logger->write('object', get_class($this) . (is_array($parents) ? ' -> ' . implode(' -> ', $parents) : ''));
}
/**
@ -54,6 +74,9 @@ class Object {
*/
public function __destruct() { }
/**
*
*/
public function __get($variable) {
if (isset($this->$variable)) {
return $this->$variable;

View file

@ -50,21 +50,29 @@ class Security extends Object {
* add in the ability for someone to add a custom message and/or
* landing page in the configuration as well.
*/
static function authenticate() {
$db = DB::getInstance();
public function authenticate() {
$table = array(
'name' => 'users',
'fields' => array(
'id' => 'id',
'username' => 'username',
'password' => 'password'
)
);
$table = $this->config->getTableMapping('users', $table);
if (isset($_SERVER['PHP_AUTH_USER'])) {
$from = '
FROM users
WHERE email = "' . $_SERVER['PHP_AUTH_USER'] . '"
AND password = "' . md5($_SERVER['PHP_AUTH_PW']) . '"
AND admin = 1;
FROM ' . $table['name'] . '
WHERE ' . $table['fields']['username'] . ' = "' . $_SERVER['PHP_AUTH_USER'] . '"
AND ' . $table['fields']['password'] . ' = "' . md5($_SERVER['PHP_AUTH_PW']) . '";
';
$db->execute('SELECT COUNT(id) ' . $from);
if ($db->getField() != 0) {
$db->execute('SELECT id ' . $from);
$_SESSION['user_id'] = $db->getField();
$this->db->execute('SELECT COUNT(' . $table['fields']['id'] . ') ' . $from);
if ($this->db->getField() != 0) {
$this->db->execute('SELECT ' . $table['fields']['id'] . ' ' . $from);
$_SESSION['user_id'] = $this->db->getField();
}
else {
$_SESSION['user_id'] = null;
@ -72,12 +80,14 @@ class Security extends Object {
}
if (!isset($_SESSION['user_id'])) {
header('WWW-Authenticate: Basic realm="Site Admin"');
header('WWW-Authenticate: Basic realm="' . $_SERVER['SERVER_NAME'] . ' Secured Page"');
header('HTTP/1.0 401 Unauthorized');
exit('No shirt, no shoes, no salvation. Access denied.');
exit('Invalid login credentials, access denied.');
}
else {
// Commented out to allow navigation to the page intended
/**
* @todo add logic to allow the site owner to force a redirect when a user logs in
*/
//header('Location: /');
//exit();
}
@ -86,16 +96,10 @@ class Security extends Object {
/**
* Logs the user out
*
* Destroys the session, clears out the authentication variables in the
* $_SERVER super global and redirects the user to the root of the site.
* Destroys the session, and redirects the user to the root of the site.
*/
static function logout() {
$session = Session::getInstance();
$session->destroy();
unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']);
public function logout() {
session_destroy();
header('Location: /');
}
}

View file

@ -1,57 +0,0 @@
<?php
/**
* Viewer Class File for PICKLES
*
* PICKLES is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* PICKLES is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with PICKLES. If not, see
* <http://www.gnu.org/licenses/>.
*
* @author Joshua John Sherman <josh@phpwithpickles.org>
* @copyright Copyright 2007, 2008 Joshua John Sherman
* @link http://phpwithpickles.org
* @license http://www.gnu.org/copyleft/lesser.html
* @package PICKLES
*/
/**
* Viewer Class
*
* Uses the factory design pattern to create a new Viewer object
* based on what viewer the model says it wants to use.
*/
class Viewer {
/**
* Private constructor
*/
private function __construct() { }
/**
* Viewer Factory
*
* Creates an instance of the Viewer type that the model requests.
*
* @param string $tye The type of viewer to produce
* @return object An instance of the viewer, loaded with the passed model
* @todo Create constants to correspond with each viewer type so it's
* potentially easier to reference from the model (since the
* constants would each be uppercase instead of mixedcase.
*/
public static function create($type) {
$class = 'Viewer_' . $type;
return new $class();
}
}
?>

View file

@ -38,8 +38,8 @@ abstract class Viewer_Common extends Object {
*
* @param object $model Object for the model we're loading
*/
public function __construct() {
parent::__construct();
public function __construct(Config $config) {
parent::__construct($config);
/**
* @todo This may need to be flipped on only for Smarty and PHP templates