Page 2 of 2

Posted: Mon Jul 24, 2006 5:15 pm
by Chris Corbyn
Daedalus- wrote:You guys succeeded in doing something that hundreds of tutorials and web pages could not do:

You made me understand a design pattern. :p
Glad you learned something new :)

Posted: Mon Jul 24, 2006 5:59 pm
by jamiel
Back to the original topic, We use Exceptions for most of our error handling ... so use a Log_Exception, Mail_Exception, STD_Exception etc. which all extend Exception.

Posted: Mon Jul 24, 2006 6:00 pm
by daedalus__
try...catch stuff?

i haven't figured it out

Posted: Mon Jul 24, 2006 7:35 pm
by Jenk
try + catch is simple, and is my preference as you are raising individual exceptions, over categorised errors (e.g. E_USER_ERROR etc.)

Code: Select all

class DummyException extends Exception {}

class MyClass
{
    public function __construct ()
    {
        throw new DummyException ('This is a dummy Exception!');
    }
}

try {
    $foo = new MyClass;
} catch (DummyException $e) {
    echo 'DummyException caught.. continuing..';
} catch (Exception $e) {
    echo 'Uknown Exception caught. Terminating Script.';
    echo $e->getMessage();
    die;
}
In that example.. the MyClass object will, of course, always throw a DummyException, but in the catch block specific for catching the DummyException we can set a routine for what to do when that type of Exception is thrown. In this case, nothing. The example also shows that you can use multiple catch blocks. All that one does is catch any exception that is not a DummyException, because the script does not know the exact type of exception, or rather, the developer has not created a catch() block to accomodate for it, it prints the message and terminates the script.

A more complex example of try catch from one of the systems I've used in the past. (bit OTT for my liking but meh..):

Code: Select all

/*
* user login..
*/

try {

    /*
    * create User object
    */    
    $user = new User($db);
    /*
    * login
    */    
    $user->login($$METHOD['uname'], $$METHOD['pass']);

/*
* ConnectionException thrown when Database, or connection to Database, not available.
*/
} catch (ConnectionException $e) {

    /* log the exception for investigation */ 
    Error::logException($e);
    /* raise incident for support */
    Error::raiseIncident($e);
    /* generate user friendly message */ 
    Error::setMessage('There has been an error connecting to the Database.' 
    . 'An incident report has been raised. Please try again later.');
    /* terminate */
    Error::terminate();

/*
* AuthenticationException thrown when user credentials are incorrect/invalid.
*/
} catch (AuthenticationException $e) {

    /* generate user friendly message */
    Error::setMessage('Username and/or Password incorrect. Please try again.');
    /* terminate */
    Error::terminate();

/*
* UserProhibitedException thrown when user has been banned.
*/ 
} catch (UserProhibitedException $e) {

    /* generate user frindly exception */
    Error::setMessage('Sorry, that account has been banned. Please contact the administrator for further details.');
    /* terminate */
    Error::terminate();

/*
* Unknown Exception - log and set for highest priority.
*/
} catch (Exception $e) {

    /* log the error for investigation */
    Error::logException($e);
    /* raise incident and set high priority */
    Error::raiseIncident($e);
    Error::setPriority(1);
    /* generate user friendly message */
    Error::setMessage('There has been an error.'
    . 'An incident has been raised and will be investigated. Please try again later.');
    /* terminate */
    Error::terminate();    

}

Posted: Tue Jul 25, 2006 3:37 pm
by daedalus__
Did you hear that sound?

It sounded like... a large gust of wind above my head!

lol!

I'll understand everything someday but until then..

whoosh!

Posted: Tue Jul 25, 2006 4:00 pm
by jamiel
Try something, something goes wrong, throw an exception, catch it ... fix it or decide how to go from here to stop a blank screen for the user. Exceptions are an important step forward in error handling and Enterprise development in PHP. Definately worth getting your head around.

Posted: Tue Jul 25, 2006 5:18 pm
by daedalus__
Thoughts, opinions, questions, comments, praises, flames, and money all appreciated and accepted!

I fixed it up, and used that observer pattern, which I completely understand now, thanks everyone!

Code: Select all

<?php
class ErrorHandler
{
	// Properties
	private $observers = array();	

	// Constructor and Destructor (if needed)
	public function __construct()
	{
		set_error_handler(array($this, 'CallObservers'));
	}

	// Load Observers
	public function LoadObserver($object)
	{
		$this->observers[] =& $object;
	}

	// Unload Observers
	public function UnloadObserver($id)
	{
		unset($this->observers[$id]);
	}

	// Call Observers
	public function CallObservers($level, $message, $file, $line)
	{
		for ($i = 0; $i < $this->CountObservers(); $i++)
		{
			$this->observers[$i]->HandleError($level, $message, $file, $line, $this->CountObservers());
		}
	}

	// Methods
	private function CountObservers()
	{
		return count($this->observers);
	}
}

class ErrorHandlerToBrowser
{
	// Properties
	private $friendly;

	// Constructor
	public function __construct($friendly)
	{
		$this->SetFriendlyErrors($friendly);
	}

	// Setters
	private function SetFriendlyErrors($int)
	{
		if (is_int($int) != FALSE)
		{
			$this->friendly = (($int > 0) != FALSE) ? 1 : 0;
		}
	}

	// Getters
	private function GetFriendlyErrors()
	{
		if (isset($this->friendly))
		{
			return $this->friendly;
		}
	}

	// Methods
	public function HandleError($level, $message, $file, $line, $observers)
	{
		// First check to see if friendly messages are on. 
		if (1 == $this->GetFriendlyErrors())
		{
			print "We're sorry, an error has occured.<br />\n";
		}
		else
		{
			print "<pre>";
			print "<b>$level</b>\n$message\n<i>$file</i> on line <b>$line</b>";
			print "</pre>";
		}
		// Check to see if this is the only observer
		if (1 == $observers)
		{
			print "This Error has <b>not</b> been logged, please report it to an administrator.";
		}
		else
		{
			print "This Error has been logged, you may refresh the page and re-try the operation, or you may go <a href=\"/\">back</a> to the home page.";
		}
	}
}

class ErrorHandlerToDatabase
{
	// Properties
	private $table;

	// Constructor and Destructor
	public function __construct($table)
	{
		$this->SetTableName($table);
	}

	// Setters
	private function SetTableName($table)
	{
		if (is_string($table) != FALSE)
		{
			$this->table == $table;
		}
	}

	// Getters
	private function GetTableName()
	{
		return $this->table;
	}

	// Methods
	public function HandleError($level, $message, $file, $line, $count = '')
	{
		$sql = sprintf("INSERT INTO %s (id, level, message, file, line) 
						VALUES (NULL, '%s', '%s', '%s', '%d')",
						$this->GetTableName(), $level, $message, $file, $line);
		mysql_query($sql);
		if (mysql_affected_rows() > 0)
		{
			print "The error was logged into the database successfully!";
		}
	}
}

class ErrorHandlerToLog
{
	// Properties
	private $log_file_path;
	private $log_file_name;
	private $log_file_frequency;	// This is how often to create a new log file.
									// Daily, Weekly, Monthly, Yearly, Never
	// Constructor
	public function __construct($log_file_path, $log_file_name, $log_file_frequency)
	{
		$this->SetLogFrequency($log_file_frequency);
		$this->SetLogPath($log_file_path);
		$this->SetLogName($log_file_name);
	}

	// Setters
	private function SetLogPath($log_file_path)
	{
		$this->log_file_path = $log_file_path;
		if (substr($log_file_path, strlen($log_file_path)-1, 1) != '/')
		{
			$this->log_file_path .= '/';
		}
	}

	private function SetLogName($log_file_name)
	{
		switch ($this->GetLogFrequency())
		{
			case 'daily':
				$this->log_file_name = $log_file_name.date('\_M\_d\_Y');
			break;
			case 'weekly':
				$this->log_file_name = $log_file_name.date('\_W\_Y');
			break;
			case 'monthly':
				$this->log_file_name = $log_file_name.date('\_M\_Y');
			break;
			case 'yearly':
				$this->log_file_name = $log_file_name.date('\_Y');
			break;
			default:
				$this->log_file_name = $log_file_name;
			break;
		}
		$this->log_file_name .= '.log';
	}

	private function SetLogFrequency($log_file_frequency)
	{
		$valid_args = array('daily', 'weekly', 'monthly', 'yearly', 0);	
		if (in_array($log_file_frequency, $args) != FALSE)
		{
			$this->log_file_frequency = $log_file_frequency;
		}
	}

	// Getters
	private function GetLogPath()
	{
		return $this->log_file_path;
	}

	private function GetLogName()
	{
		return $this->log_file_name;
	}

	private function GetLogFrequency()
	{
		return $this->log_file_frequency;
	}

	// Methods
	public function HandleError($level, $message, $file, $line, $count = '')
	{
		$error_str = $level.', '.$message.', '.$file.', '.$line;
		$file = fopen($this->GetLogPath().$this->GetLogName(), 'a') or die('could not open');
		fwrite($file, $error_str);
		fclose($file);		
	}
}


function __autoload($class_name)
{
	include_once("inc/$class_name.class.php");
}

$ErrorHandler = new ErrorHandler();

$ErrorHandler->LoadObserver(new ErrorHandlerToBrowser(1));

getimagesize('bleat.jpg');
I realise that it lacks error handling of most or any kind but I will fix that later.

I want to try out this try...catch stuff but don't completely understand it yet..

EDIT: I realise there are problems with the ToLogFile class or whatever I called it, but at the end of a 20 hour day, I am too tired to finish it. :(

updated, look up

Posted: Thu Jul 27, 2006 1:43 am
by daedalus__
updated, look up

Posted: Thu Jul 27, 2006 4:41 am
by Benjamin
That class seems rather complicated for an error handling class. I wrote one last night because it's something I've been putting off. I didn't post it here though cause I don't want to hijack your thread.

Posted: Thu Jul 27, 2006 12:07 pm
by daedalus__
hijack away!