Page 1 of 1

PHP 5 exceptions to PHP 4

Posted: Mon Feb 26, 2007 4:11 am
by fastfingertips
Hello guys

I’m in middle of a task that involves rewriting some PHP 5 classes to PHP 4 and I have somehow to simulate the PHP 5 exception system (since any error must be handled at a superior level). Any idea how I will be able to do that? I was thinking to create an observer and make all objects that are triggering error observable, but this will involve some hard work so this is my last solution.

Posted: Mon Feb 26, 2007 5:31 am
by Weirdan
other approach would be to use set_error_handler() & trigger_error()

Posted: Mon Feb 26, 2007 6:28 am
by Chris Corbyn
I've got this (it's not the best, I know):

Code: Select all

<?php

class MyException extends Swift_Exception {}

class TestOfErrors extends UnitTestCase
{
	function setUp()
	{
		Swift_Errors::reset();
	}
	
	function testErrorIsTriggeredIfItNotCaught()
	{
		$ex =& new Swift_Exception("Foo");
		Swift_Errors::trigger($ex);
		$this->assertError(); //SimpleTest's of course
		
		$ex =& new Swift_Exception("BAR");
		Swift_Errors::trigger($ex);
		$this->assertError(); //SimpleTest's of course
	}
	
	function testErrorCanBeCaught()
	{
		$ex =& new Swift_Exception("foo");
		Swift_Errors::expect($e);
		Swift_Errors::trigger($ex);
		$this->assertEqual($e, $ex);
		
		$ex =& new Swift_Exception("bar");
		Swift_Errors::expect($e);
		Swift_Errors::trigger($ex);
		$this->assertEqual($e, $ex);
	}
	
	function testOnlyOneErrorCaught()
	{
		$ex =& new Swift_Exception("foo");
		Swift_Errors::expect($e);
		Swift_Errors::trigger($ex);
		$this->assertEqual($e, $ex);
		
		
		$ex =& new Swift_Exception("bar");
		Swift_Errors::trigger($ex);
		$this->assertError();
	}
	
	function testErrorTypeCanBeSpecified()
	{
		$ex =& new MyException("test");
		Swift_Errors::trigger($ex);
		$this->assertError();
		
		$ex =& new MyException("test2");
		Swift_Errors::expect($e, "MyException");
		Swift_Errors::trigger($ex);
		$this->assertEqual($e, $ex);
	}
	
	function testErrorTypeKnowsAboutInheritance()
	{
		$ex =& new MyException("test2");
		Swift_Errors::expect($e, "MyException");
		Swift_Errors::trigger($ex);
		$this->assertEqual($e, $ex);
		
		$ex =& new MyException("test2");
		Swift_Errors::expect($e, "Swift_Exception");
		Swift_Errors::trigger($ex);
		$this->assertEqual($e, $ex);
	}
	
	function testCatchStatementsCanBeEndedWithoutThrowingException()
	{
		$ex =& new MyException("test2");
		Swift_Errors::expect($e, "MyException");
		//Swift_Errors::trigger($ex); let's not throw this one!
		Swift_Errors::clear("MyException");
		$ex =& new MyException("test3");
		Swift_Errors::trigger($ex);
		$this->assertError();
	}
	
	function testCatchStatementsCanBeNested()
	{
		$ex1 =& new MyException("ex1");
		$ex2 =& new MyException("ex2");
		Swift_Errors::expect($e1, "MyException");
		Swift_Errors::expect($e2, "MyException");
		Swift_Errors::trigger($ex1);
		Swift_Errors::trigger($ex2);
		$this->assertEqual($e1, $ex2);
		$this->assertEqual($e2, $ex1);
	}
	
	function testRealCaseScenario()
	{
		Swift_Errors::expect($e, "MyException");
		if (true) {
			Swift_Errors::trigger(new MyException("foo"));
		} else {
			Swift_Errors::clear("MyException");
		}
		$this->assertIsA($e, "MyException");
		$this->assertEqual("foo", $e->getMessage());
		
		Swift_Errors::expect($e, "MyException");
		if (false) {
			Swift_Errors::trigger(new MyException("foo"));
		} else {
			Swift_Errors::clear("MyException");
		}
		$this->assertNull($e);
		
		if (true) {
			Swift_Errors::trigger(new Swift_Exception("bar"));
		} else {
			Swift_Errors::clear("MyException");
		}
		$this->assertError();
	}
}

Code: Select all

<?php

/**
 * Swift Mailer PHP4 Exception hackaround.
 * Please read the LICENSE file
 * @author Chris Corbyn <chris@w3style.co.uk>
 * @package Swift
 * @license GNU Lesser General Public License
 */


/**
 * Swift Exception handling object for PHP4
 * Triggers and/or catches errors
 * @package Swift
 * @author Chris Corbyn <chris@w3style.co.uk>
 */
class Swift_Errors
{
	/**
	 * Caught errors
	 * @var array,Swift_Error
	 */
	var $errors = array();
	/**
	 * If an error has been thrown previously and not caught (hack)
	 * @var boolean
	 */
	var $halt = false;
	/**
	 * Errors we're expecting, so don't trigger them
	 * @var array
	 */
	var $try = array();
	
	/**
	 * Get an instance of this class as a singleton - needed internally
	 * @return Swift_Errors
	 */
	function &getInstance()
	{
		static $instance = null;
		if (!$instance) $instance = array(new Swift_Errors());
		return $instance[0];
	}
	/**
	 * Check if things are supposed to have stopped processing because of an
	 * uncaught excpetion
	 * @return boolean
	 */
	function halted()
	{
		$me =& Swift_Errors::getInstance();
		return $me->halt;
	}
	/**
	 * Reset everything logged so far
	 */
	function reset()
	{
		$me =& Swift_Errors::getInstance();
		$me->errors = array();
		$me->halt = false;
		$me->try = array();
	}
	/**
	 * Throw a new exception - it will either be caught or triggered
	 * @param Swift_Exception
	 */
	function trigger(&$e)
	{
		$me =& Swift_Errors::getInstance();
		$me->errors[] =& $e;
		$me->halt = true;
		foreach (array_reverse(array_keys($me->try)) as $type)
		{
			if (is_a($e, $type))
			{
				foreach (array_reverse(array_keys($me->try[$type])) as $i)
				{
					$me->try[$type][$i] = $e;
					unset($me->try[$type][$i]);
					$me->halt = false;
					return;
				}
			}
		}
		//If here, then it wasn't caught
		$me->dumpError($e);
	}
	/**
	 * Dump the error if it was not caught
	 * @param Swift_Exception
	 */
	function dumpError(&$e)
	{
		$output = "<br /><strong>Uncaught Error</strong> of type [" . get_class($e) . "] with message [" . $e->getMessage() . "]";
		$output .= "<br />" . $e->getBacktraceDump() . "<br />";
		trigger_error($output, E_USER_ERROR);
	}
	/**
	 * Tell the error handler we're expecting an error of type $type and assign it to $e
	 * @param &$e
	 * @param string The type of expection - optional
	 */
	function expect(&$e, $type="Swift_Exception")
	{
		$me =& Swift_Errors::getInstance();
		$e = null;
		$me->try[$type][] =& $e;
	}
	/**
	 * Clear anything that may have been expected matching $type
	 * @param string The type
	 */
	function clear($type)
	{
		$me =& Swift_Errors::getInstance();
		if (isset($me->try[$type]))
		{
			foreach (array_reverse(array_keys($me->try[$type])) as $i)
			{
				unset($me->try[$type][$i]);
				break;
			}
		}
	}
	/**
	 * The last error message as a string
	 * @return string
	 */
	function getLast()
	{
		$me =& Swift_Errors::getInstance();
		if (count($me->errors))
		{
			$last =& $me->errors[(count($me->errors)-1)];
			return $last->getMessage();
		}
	}
	/**
	 * Get all logged errors as an array
	 * @return array,Swift_Exception
	 */
	function &getAll()
	{
		return $this->errors;
	}
}

Code: Select all

<?php

/**
 * Swift Mailer PHP4 Exception.
 * Please read the LICENSE file
 * @author Chris Corbyn <chris@w3style.co.uk>
 * @package Swift
 * @license GNU Lesser General Public License
 */


/**
 * Swift Exception for PHP4.
 * @package Swift
 * @author Chris Corbyn <chris@w3style.co.uk>
 */
class Swift_Exception
{
	/**
	 * The error message in this exception
	 * @var string
	 */
	var $message;
	/**
	 * A backtrace to show
	 * @var array
	 */
	var $trace;
	
	/**
	 * Constructor
	 * @param string The error message
	 */
	function Swift_Exception($message)
	{
		$this->message = $message;
		$this->trace = debug_backtrace();
	}
	/**
	 * Get the error message
	 * @return string
	 */
	function getMessage()
	{
		return $this->message;
	}
	/**
	 * Get the backtrace
	 * @return array
	 */
	function getTrace()
	{
		return $this->trace;
	}
	/**
	 * Get a summarised backtrace as a string
	 * @return string
	 */
	function getBacktraceDump()
	{
		$trace = $this->getTrace();
		$ret = "";
		for ($i = 0; $i < count($trace); $i++)
		{
			$end = array_pop($trace);
			if (!empty($end["class"])) $class = $end["class"] . "::";
			else $class = "";
			
			$file_info = " @$i " . $class . $end["function"] .
			"() in " . $end["file"] . " on line " .
			$end["line"] . "<br />";
			
			$ret .= $file_info;
		}
		return $ret;
	}
}