libxml error handler with OOP

2019-07-01 12:43发布

问题:

I need catch libxml errors. But I want to use my class for this. I know about libxml_get_errors and other function. But I need something like libxml_set_erroc_class("myclass") and in all case for error will call my class. I don't want in each case after use $dom->load(...) create some construction like foreach(libxml_get_errors as $error) {....}. Can you help me?

回答1:

libxml errors are mostly generated when reading or writing xml document because automatic validation is done.

So this is where you should concentrate and you don't need to overwrite the set_error_handler .. Here is a prove of concept

Use Internal Errors

libxml_use_internal_errors ( true );

Sample XML

$xmlstr = <<< XML
<?xml version='1.0' standalone='yes'?>
<movies>
 <movie>
  <titles>PHP: Behind the Parser</title>
 </movie>
</movies>
XML;

echo "<pre>" ;

I guess this is an example of the kind of what you want to achieve

try {
    $loader = new XmlLoader ( $xmlstr );
} catch ( XmlException $e ) {
    echo $e->getMessage();
}

XMLLoader Class

class XmlLoader {
    private $xml;
    private $doc;
    function __construct($xmlstr) {
        $doc = simplexml_load_string ( $xmlstr );

        if (! $doc) {
            throw new XmlException ( libxml_get_errors () );
        }
    }
}

XmlException Class

class XmlException extends Exception {

    private $errorMessage = "";
    function __construct(Array $errors) {

        $x = 0;
        foreach ( $errors as $error ) {
            if ($error instanceof LibXMLError) {
                $this->parseError ( $error );
                $x ++;
            }
        }
        if ($x > 0) {
            parent::__construct ( $this->errorMessage );
        } else {
            parent::__construct ( "Unknown Error XmlException" );
        }
    }

    function parseError(LibXMLError $error) {
        switch ($error->level) {
            case LIBXML_ERR_WARNING :
                $this->errorMessage .= "Warning $error->code: ";
                break;
            case LIBXML_ERR_ERROR :
                $this->errorMessage .= "Error $error->code: ";
                break;
            case LIBXML_ERR_FATAL :
                $this->errorMessage .= "Fatal Error $error->code: ";
                break;
        }

        $this->errorMessage .= trim ( $error->message ) . "\n  Line: $error->line" . "\n  Column: $error->column";

        if ($error->file) {
            $this->errorMessage .= "\n  File: $error->file";
        }
    }

}

Sample Output

Fatal Error 76: Opening and ending tag mismatch: titles line 4 and title
  Line: 4
  Column: 46

I hope this helps

Thanks



回答2:

There is no facility to do this directly. Your options would be:

  1. extend the PHP class that uses libxml and wrap your custom error handling logic around the stock implementation (not that good), or
  2. write your own class that aggregates an instance of that PHP class and create your own public interface around it (better, because you are in control of the public interface and you don't risk problems if the PHP class is extended in the future), or
  3. replace the global error handler for the duration of your parsing and restore it afterwards (not as powerful, may be problematic if your code calls into other code, however easier to do)

Solutions 1 and 2 have the advantage that they do not modify the behavior of any other code in your application no matter what.



回答3:

edit (confused exceptions with errors):

  1. Use set_exception_handler to catch global exceptions.
  2. Have your code throw these exceptions in cases like $dom->load(). Because libxml doesn't seem to throw exceptions on its own, your other option is to create a wrapper around it, use the wrapper in your code and have it check libxml for errors and throw them in necessary cases.
  3. Handle the exceptions inside "myclass".

Be wary though that the set_exception_handler will be catching all your exceptions.

Here's an example of what you could do:

//inheritance example (composition, though, would be better)
class MyDOMWrapper extends DOMDocument{
    public function load($filename, $options = 0){
        $bool = parent::load($filename, $options);
        if (!$bool){
            throw new MyDOMException('Shit happens. Feeling lucky.', 777);
        }
    }
}
class MyDOMException extends DOMException{
    public $libxml;

    public function __construct($message, $code){
        parent::__construct($message, $code);
        $this->libxml = libxml_get_errors();
    }
}
class MyDOMExceptionHandler{
    public static function handle($e){
        //handle exception globally
    }
}
libxml_use_internal_errors(true);
set_exception_handler(array('MyDOMErrorHandler', 'handle'));

//global exception handler
$myDom = new MyDOMWrapper();
$myDom->load('main.xml');

//when special handling is necessary
try {
    $myDom = new MyDOMWrapper();
    $myDom->load('main.xml');
} catch (MyDOMException $e) {   
    //handle or
    throw $e; //rethrow
}