php-error-handling

PHP Error Handling: just throw it

There are many situations when error-handling code is necessary: files are misplaced, database servers are left uninitialized, URLs are changed, XML files are mangled, permissions are poorly set, disk quotas are exceeded and so on.
Here is a simple class that stores, retrieves, and sets data in an XML configuration file:

class Conf {
	private $file;
	private $xml;
	private $lastMatch;
	/**
	 * Creates a SimpleXmlElement object from the given xml file
	 * Throws either an XmlException, a FileException, or a ConfException, depending on 
	 * the kind of error it encounters
	 * @param string $file
	 */
	function __construct($file) {
		$this->file = $file;
		if (! file_exists ( $file )) {
			throw new Exception ( "The $file does not exists!" );
		}
		// The flag LIBXML_NOERROR suppresses warnings, so you can handle them  
		// with XmlException class		
		$this->xml = simplexml_load_file ( $file, null, LIBXML_NOERROR );
		if (! is_object ( $this->xml )) {
			throw new XmlException ( libxml_get_last_error () );
		}
		// display the type of $this->xml variable
		print gettype ( $this->xml ) . "\n";
		$matches = $this->xml->xpath ( "/conf" );
		if (! count ( $matches )) {
			throw new ConfException ( "Could not find root element: conf" );
		}
	}
	
	/**
	 * Writes the formated XML string to the file 
	 */
	function write() {
		if(!is_writable($this->file)){
			throw new Exception("File '{$this->file}' is not writeable!");
		}
		file_put_contents ( $this->file, $this->xml->asXML () );
	}
	
	/**
	 * Locate an item element with the given name attribute 
	 * @param string $str the name attribute value
	 * @return the value of matched item
	 */
	function get($str) {
		$matches = $this->xml->xpath ( "/conf/item[@name=\"$str\"]" );		
		if (count ( $matches )) {
			$this->lastMatch = $matches [0];
			return ( string ) $matches [0];
		}
		return NULL;
	}
	
	/**
	 * Changes the value of an existing item or creates a new one 
	 * @param string $key item's name value
	 * @param string $value item value
	 */
	function set($key, $value) {
		if (! is_null ( $this->get ( $key ) )) {
			$this->lastMatch [0] = $value;
			return;
		}
		$conf = $this->xml->conf;
		$this->xml->addChild ( 'item', $value )->addAttribute ( 'name', $key );
	}
}

class XmlException extends Exception {
	private $error;
	function __construct(LibXmlError $error) {
		$shortfile = basename ( $error->file );
		$msg = "[{$shortfile}, line {$error->line}, col {$error->column}] {$error->message}";
		$this->error = $error;
		parent::__construct ( $msg, $error->code );
	}
	
	function getLibXmlError() {
		return $this->error;
	}
}

class FileException extends Exception {}

class ConfException extends Exception {}

class RunConf {
	static function init() {
		try {
			$conf = new Conf ( dirname ( __FILE__ ) . "/conf.xml" );
			print "user: " . $conf->get ( 'user' ) . "\n";
			print "host: " . $conf->get ( 'host' ) . "\n";
			$conf->set ( "pass", "parola" );
			$conf->write ();			
			$conf->set( "phone", "07318830001" );
			$conf->write();
			print "phone: ". $conf->get('phone') . "\n";
		} catch ( FileException $e ) {
			// permissions issue or non-existent file
		} catch ( XmlException $e ) {			
			// wrong kind of XML file
		} catch ( Exception $e ) {
			// backstop: should not be called
		}
	}
}

RunConf::init();

The conf.xml looks like:

angi
mypassw
localhost	

Running the script gives the output:

object
user: angi
host: localhost
phone: 07318830001

and changes the xml file to:

angi
parola
localhost	
07318830001

Exceptions

PHP 5 introduced exceptions to PHP, a radically different way of handling error conditions. An exception is a special object instantiated from the built-in Exception class (or from a derived class). The Exception class constructor accepts two optional arguments, a message string and an error code.
The class provides some useful methods for analyzing error conditions, described bellow:

Method Description
getMessage() Get the message string that was passed to the constructor
getCode() Get the code integer that was passed to the constructor
getFile() Get the file in which the exception was generated
getLine() Get the line number at which the exception was generated
getPrevious() Get a nested Exception object
getTrace() Get a multidimensional array tracing the method calls that led to the exception, including method, class, file, and argument data
getTraceAsString() Get a string version of the data returned by getTrace()
__toString() Called automatically when the Exception object is used in string context.Returns a string describing the exception details

Throwing an Exception

The throw keyword halts execution of the current method and passes responsibility for handling the error back to the calling code.

How does client code know how to handle an exception when thrown? When you invoke a method that may throw an exception, you can wrap your call in a try clause (this must be followed by at least one catch clause in which you can handle any error).

When an exception is thrown, the catch clause in the invoking scope is called and the Exception object is automatically passed in as the argument variable.

Subclassing Exception

You can create classes that extend the Exception class as you would with any user-defined class. In fact, you can define as many catch clauses as you need for a try statement. The catch clause invoked will depend upon the type of the thrown exception and the class type hint in the argument list (see RunConf class).
The LibXmlError class is generated behind the scenes when SimpleXml encounters a broken XML file (line 20).

Remember to place the most generic error type at the end and the most specialized at the start.
For example, if you were to place the catch clause for Exception ahead of the clause for XmlException and ConfException, neither of these would ever be invoked. This is because both of these classes belong to the Exception type, and would therefore match the first clause.

The benefit of these fine-grained catch clauses is that they allow you to apply different recovery or failure mechanisms to different errors. For example, you may decide to end execution, log the error and continue, or explicitly rethrow an error:

try {
    //...
} catch ( FileException $e ) {
    throw $e;
}
email-newsletter

Dev'Letter

Professional opinion about Web Development Techniques, Tools & Productivity.
Only once a month!

Any suggestion on what to write next time ? Submit now

Opinions

avatar
550
  Subscribe  
Notify of

Related Posts