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).
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;
}
Opinions