<?php
/**
 * patErrorManager main error management class used by pat tools for the
 * application-internal error management. Creates patError objects for
 * any errors for precise error management.
 *
 *	$Id: patErrorManager.php,v 1.2 2004/02/17 21:57:22 argh Exp $
 *
 * @package	patError
 */

/**
 * error definition: illegal options.
 */									
define( "PATERRORMANAGER_ERROR_ILLEGAL_OPTIONS", 1 );

/**
 * error definition: callback function does not exist.
 */									
define( "PATERRORMANAGER_ERROR_CALLBACK_NOT_CALLABLE", 2 );
 
/**
 * global definitions needed to keep track of things when calling the patErrorManager
 * static methods.
 */
$GLOBALS["_pat_errorHandling"]	=	array( 
											E_NOTICE	=> array( "mode" => "echo" ),
											E_WARNING	=> array( "mode" => "echo" ),
											E_ERROR		=> array( "mode" => "die" )
										);

/**
 * available error levels
 * Stored in a variable to keep them flexible
 */
$GLOBALS["_pat_errorLevels"]	=	array(
											E_NOTICE	=> "Notice",
											E_WARNING	=> "Warning",
											E_ERROR		=> "Error"
										);
/**
 * error class names
 * Stored in a variable allows to change during runtime
 */
$GLOBALS["_pat_errorClass"]	=	"patError";
 
/**
 * patErrorManager main error management class used by pat tools for the
 * application-internal error management. Creates patError objects for
 * any errors for precise error management.
 *
 * @static
 * @package	patErrorManager
 * @version	0.2
 * @author	gERD Schaufelberger <gerd@php-tools.net>
 * @author	Stephan Schmidt <schst@php-tools.net>
 * @license	LGPL
 * @link	http://www.php-tools.net
 * @todo	implement ignoreError() to ignore errrors with a certain code
 * @todo	implement expectError() to ignore an error with a certain code only once.
 * @todo	replace is_a with PHP function (allow patErrorManager to work with PHP < 4.2)
 */
class patErrorManager
{
   /**
	* method for checking whether the return value of a pat application method is a pat
	* error object.
	*
	* @static
	* @access	public
	* @param	mixed	&$object
	* @return	boolean $result	True if argument is a patError-object, false otherwise.
	*/
    function isError( &$object )
    {
		if( !is_object( $object ) )
		{
			return false;
		}
		
		if( !is_a( $object, $GLOBALS["_pat_errorClass"] ) )
		{
			return false;
		}
		
        return  true;
    }
	
   /**
	* wrapper for the {@link raise()} method where you do not have to specify the
	* error level - a {@link patError} object with error level E_ERROR will be returned.
	*
	* @static
	* @access	public
	* @param	int		$code	The application-internal error code for this error
	* @param	string	$msg	The error message, which may also be shown the user if need be.
	* @param	mixed	$info	Optional: Additional error information (usually only developer-relevant information that the user should never see, like a database DSN).
	* @return	object	$error	The configured patError object
	* @see		raise()
	* @see		patError
	*/
	function &raiseError( $code, $msg, $info = null )
	{
		$e	=	patErrorManager::raise( E_ERROR, $code, $msg, $info );
		return $e;
	}
	
   /**
	* wrapper for the {@link raise()} method where you do not have to specify the
	* error level - a {@link patError} object with error level E_WARNING will be returned.
	*
	* @static
	* @access	public
	* @param	int		$code	The application-internal error code for this error
	* @param	string	$msg	The error message, which may also be shown the user if need be.
	* @param	mixed	$info	Optional: Additional error information (usually only developer-relevant information that the user should never see, like a database DSN).
	* @return	object	$error	The configured patError object
	* @see		raise()
	* @see		patError
	*/
	function &raiseWarning( $code, $msg, $info = null )
	{
		$e	=	patErrorManager::raise( E_WARNING, $code, $msg, $info );
		return $e;
	}
	
   /**
	* wrapper for the {@link raise()} method where you do not have to specify the
	* error level - a {@link patError} object with error level E_NOTICE will be returned.
	*
	* @static
	* @access	public
	* @param	int		$code	The application-internal error code for this error
	* @param	string	$msg	The error message, which may also be shown the user if need be.
	* @param	mixed	$info	Optional: Additional error information (usually only developer-relevant information that the user should never see, like a database DSN).
	* @return	object	$error	The configured patError object
	* @see		raise()
	* @see		patError
	*/
	function &raiseNotice( $code, $msg, $info = null )
	{
		$e	=	patErrorManager::raise( E_NOTICE, $code, $msg, $info );
		return $e;
	}
	
   /**
	* creates a new patError object given the specified information.
	*
	* @access	public
	* @param	int		$level	The error level - use any of PHP's own error levels for this: E_ERROR, E_WARNING, E_NOTICE, E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE.
	* @param	int		$code	The application-internal error code for this error
	* @param	string	$msg	The error message, which may also be shown the user if need be.
	* @param	mixed	$info	Optional: Additional error information (usually only developer-relevant information that the user should never see, like a database DSN).
	* @return	object	$error	The configured patError object
	* @see		patError
	* @todo		implement 'simple' mode that returns just false (BC for patConfiguration)
	* @todo		either remove HTML tags and entities from output or test for enviroment!!! <b></b> in shell is ugly!
	*/
    function &raise( $level, $code, $msg, $info = null )
    {
		// need patError
		$class	=	$GLOBALS["_pat_errorClass"];
		if( !class_exists( $class ) )
		{
			include_once dirname( __FILE__ ) . "/". $class .".php";
		}
		
		// build error object
		$error			=&	new	$class( $level, $code, $msg, $info );
		
		$level_human	=	patErrorManager::translateErrorLevel( $level );

		// see what to do with this kind of error		
		$handling	=	patErrorManager::getErrorHandling( $level );
		switch( $handling["mode"] )
		{
			// nothing else
			case "ignore":
				break;
		
			// trigger a PHP error
			case "trigger":
				switch( $error->getLevel() )
				{
					case	E_NOTICE:
						$level	=	E_USER_NOTICE;
						break;
					case	E_WARNING:
						$level	=	E_USER_WARNING;
						break;
					case	E_NOTICE:
					default:
						$level	=	E_USER_ERROR;
						break;
				}
			
				trigger_error( $error->getMessage(), $level );
				break;
		
			// print debugging information for developing purpose
			case "verbose":
				echo "<br /><b>pat-$level_human</b>: " . $error->getMessage() . "<br />\n";
				if( $info != null )
				{
					echo "&nbsp;&nbsp;&nbsp;" . $error->getInfo() . "<br />\n";
				}
				break;
				
			// print user information
			case "echo":
				echo "<br /><b>pat-$level_human</b>: " . $error->getMessage() . "<br />\n";
				break;
				
			// call external handler
			case "callback";
				$opt	=	$handling["options"];
				$error	=	&call_user_func( $opt, $error );
				break;
				
			// die with error message
			case "die":
				die( "<br /><b>pat-$level_human</b> " . $error->getMessage() ) . "<br />\n";
				break;
		}
		
        return  $error;
    }

   /**
	* register a new error level
	*
	* This allows you to add custom error levels to the built-in
	* - E_NOTICE
	* - E_WARNING
	* - E_NOTICE
	*
	* You may use this level in subsequent calls to raise().
	* Error handling will be set to 'ignore' for the new level, you
	* may change it by using setErrorHandling().
	*
	* You could be using PHP's predefined constants for error levels
	* or any other integer value.
	*
	* @access	public
	* @param	integer	error level
	* @param	string	human-readable name
	* @return	boolean	true on success; false if the level already has been registered
	* @see		raise(), setErrorHandling()
	* @link		http://www.php.net/manual/en/function.error-reporting.php
	*/
	function registerErrorLevel( $level, $name )
	{
		if( isset( $GLOBALS["_pat_errorLevels"][$level] ) )
		{
			return false;
		}
		$GLOBALS["_pat_errorLevels"][$level]	=	$name;
		patErrorManager::setErrorHandling( $level, 'ignore' );
		return	true;
	}
	
   /**
	* sets the way the patErrorManager will handle teh different error levels. Use this
	* if you want to override the default settings.
	*
	* Error handling modes:
	* - ignore
	* - trigger
	* - verbose
	* - echo
	* - callback
	* - die
	*
	* You may also set the error handling for several modes at once using PHP's bit operations.
	* Examples:
	* - E_ALL = Set the handling for all levels
	* - E_ERROR | E_WARNING = Set the handling for errors and warnings
	* - E_ALL ^ E_ERROR = Set the handling for all levels except errors
	*
	* @static
	* @access	public
	* @param	int		$level		The error level for which to set the error handling
	* @param	string	$mode		The mode to use for the error handling.
	* @param	mixed	$options	Optional: Any options needed for the given mode.
	* @return	mixed	$result		True on success, or a patError object if failed.
	* @see		getErrorHandling()
	*/
    function setErrorHandling( $level, $mode, $options = null )
    {
		$levels	=	$GLOBALS["_pat_errorLevels"];

		foreach( $levels as $eLevel => $eTitle )
		{
			if( ( $level & $eLevel ) != $eLevel )
			{
				continue;
			}

			// set callback options
			if( $mode == "callback" )
			{
				if( !is_array( $options ) )
				{
					return patErrorManager::raiseError( E_ERROR, 
														PATERRORMANAGER_ERROR_ILLEGAL_OPTIONS, 
														"Options for callback not valid" 
														);
				}
				
				if( !is_callable( $options ) )
				{
					$tmp	=	array( "GLOBAL" );
					if( is_array( $options ) )
					{
						$tmp[0]	=	$options[0];
						$tmp[1]	=	$options[1];
					}
					else
					{
						$tmp[1]	=	$options;
					}
					
					return patErrorManager::raiseError(	E_ERROR, 
														PATERRORMANAGER_ERROR_CALLBACK_NOT_CALLABLE, 
														"Function is not callable", 
														"Function:" . $tmp[1]  . " scope " . $tmp[0] . "." 
														);
				}
			}
				
		
			// save settings
			$GLOBALS["_pat_errorHandling"][$eLevel]	=	array( "mode" => $mode );
			if( $options	!= null )
			{
				$GLOBALS["_pat_errorHandling"][$eLevel]["options"]	=	$options;
			}
		}
			
        return  true;
    }

   /**
	* retrieves the current error handling settings for the specified error level.
	*
	* @access	public
	* @param	int		$level		The error level to retrieve. This can be any of PHP's own error levels, e.g. E_ALL, E_NOTICE...
	* @return	array	$handling	All error handling details
	*/
    function getErrorHandling( $level )
    {
		return $GLOBALS["_pat_errorHandling"][$level];
    }

   /**
	* translate an error level
	*
	* returns the human-readable name for an error level,
	* e.g. E_ERROR will be translated to 'Error'.
	*
	* @access	public
	* @param	integer		error level
	* @return	string		human-readable representation
	*/
	function translateErrorLevel( $level )
	{
		if( isset( $GLOBALS["_pat_errorLevels"][$level] ) )
		{
			return	$GLOBALS["_pat_errorLevels"][$level];
		}
		return	"Unknown error level";
	}
	
   /**
	* setErrorClass
	*
	* In order to autoload this class, the filename containing that class must be
	* named like the class itself; with an appending ".php". Although the file must be stored
	* in the same directory as patErrorManager.php (this file)
	*
	* @access public
	* @param string $name classname
	* @return boolean $result true on success
	*/
    function setErrorClass( $name )
    {
		$GLOBALS["_pat_errorClass"]	=	$name;
		return true;
    }
}
?>