In-depth PHP Object-Oriented, Mode and Practice - Advanced Features (3)

Keywords: xml

error handling

PHP5 introduces exceptions, which are special objects instantiated from PHP5's built-in Exception class (or its subclasses).Objects of type Exception are used to store and report error information.The construction method of the Exception class accepts two optional parameters: the information string and the error code.This class provides some Useful methods To analyze the error condition.Error notifications and debugging information provided by the Exception class (especially the getTrace() and getTraceAsString() methods returning to information) are also useful.

  • throw
    You can use the throw keyword in conjunction with the Exception object to throw exceptions.This stops executing the current method and is responsible for returning the error to the calling code.
//conf01.xml
<?xml version="1.0" ?>
<conf>
    <item name="user">bob</item>
    <item name="pass">newpass</item>
    <item name="host">localhost</item>
</conf>
class Conf
{
    private $file;
    private $xml;
    private $lastmatch;

    public function __construct($file)
    {
        $this->file = $file;
        if (!file_exists($file)) {
            throw new Exception("file 'file' does not exist");
        }
        $this->xml = simplexml_load_file($file);
    }

    public function write()
    {
        if (!is_writable($this->file)) {
            throw new Exception("file '{$this->file}' is not writeable");
        }
        file_put_contents($this->file, $this->xml->asXML());
    }

    public function get($str)
    {
        $matches = $this->xml->xpath("/conf/item[@name=\"$str\"]");
        if (count($matches)) {
            $this->lastmatch = $matches[0];
            return (string) $matches[0];
        }
        return null;
    }

    public 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);
    }
}

The u construct() and write() methods can keep checking for file errors while working, and let other, more appropriate code respond to detected errors.If you call a method that may throw an exception, you can place the call statement in the try clause.The try clause consists of the keyword try and parentheses thereafter.The try clause must be followed by at least one catch clause to handle errors:

try {
    $conf = new Conf(dirname(__FILE__) . "/conf01.xml");
    print "user: " . $conf->get('user') . "\n";
    print "host: " . $conf->get('host') . "\n";
    $conf->set("pass", "newpass");
    $conf->write();
} catch (Exception $e) {
    die($e->__toString());
}
  • Subclassification of Exceptions
    If you want to create a user-defined exception class, you can inherit it from the Exception class.
    In fact, when defining multiple catch clauses, only one try clause is required.Which catch clause is called depends on the type of exception thrown and the type hint of the class in the parameter.Below we define some simple subclasses of Exception:
class XmlException extends Exception
{
    private $error;

    public 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);
    }

    public function getLibXmlError()
    {
        return $this->error;
    }
}

class FileException extends Exception
{
}

class ConfException extends Exception
{
}

When SimpleXml scans a corrupted XML file, a LibXmlError object is generated.Now use these exception classes in your code and modify u construct() and write():

class Conf
{
    //...
    public function __construct($file)
    {
        $this->file = $file;
        if (!file_exists($file)) {
            throw new Exception("file 'file' does not exist");
        }
        $this->xml = simplexml_load_file($file,null,LIBXML_NOERROR);
        if (!is_object($this->xml)) {
            throw new XmlException(libxml_get_last_error());
        }
        print gettype($this->xml);
        $matches=$this->xml->xpath("/conf");
        if (!count($matches)) {
            throw new ConfException("could not find root element: conf");   
        }
    }

    public function write()
    {
        if (!is_writable($this->file)) {
            throw new FileException("file '{$this->file}' is not writeable");
        }
        file_put_contents($this->file, $this->xml->asXML());
    }
    //...

_u construct() throws an XmlException, FileException, or ConfException depending on the type of error encountered.The optional parameter LIBXML_NOERROR is given to simplexml_load_file(), which is used for direct output of persistent error warnings and is left to the XmlException class to process after the warnings occur.When I encounter an irregular XML file, simplexml_load_file() does not return an object, so I know an error has occurred and then access the error with libxml_get_last_error().

If the $file property points to a file that is not writable, your write() method will throw a FileException.

class Runner
{

    public function init()
    {
        try {
            $conf = new Conf(dirname(__FILE__) . "/conf01.xml");
            print "user: " . $conf->get('user') . "\n";
            print "host: " . $conf->get('host') . "\n";
            $conf->set("pass", "newpass");
            $conf->write();
        } catch (FileException $e) {
            //File permission problem or file does not exist
        } catch (XmlException $e) {
            //Damaged XML file
        } catch (ConfException $e) {
            //Wrong XML file format
        } catch (Exception $e) {
            //Backup trapper, normally should not be called
        }
    }
}

The call to the catch clause depends on the type of exception thrown.The first matching type clause will be executed, so remember to put the generic type last and the specific type first.Also, it is often a good idea to have a "backstop" clause in case new exceptions are added to your code during development.If the exception is not caught at the end, a fatal error will occur.

Posted by Visualant on Wed, 17 Jul 2019 11:07:35 -0700