Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00%
0 / 1
70.00%
14 / 20
CRAP
80.77%
105 / 130
DomDocument
0.00%
0 / 1
70.00%
14 / 20
57.77
80.77%
105 / 130
 __construct($version = "1.0", $encoding = "UTF-8")
100.00%
1 / 1
2
100.00%
6 / 6
 load($filename, $options = 0)
100.00%
1 / 1
1
100.00%
8 / 8
 loadXml($source, $options = 0)
0.00%
0 / 1
2
0.00%
0 / 8
 schemaValidate($filename)
0.00%
0 / 1
2.01
88.89%
8 / 9
 schemaValidateSource($source)
0.00%
0 / 1
6
0.00%
0 / 9
 xinclude($options = 0)
100.00%
1 / 1
1
100.00%
7 / 7
 getXpath()
100.00%
1 / 1
1
100.00%
1 / 1
 getElementValue($element_name, $reference_element = null)
0.00%
0 / 1
8.02
93.75%
15 / 16
 getElement($name)
100.00%
1 / 1
6
100.00%
10 / 10
 setDefaultNamespace($prefix = self::NAMESPACE_PREFIX, $uri = self::NAMESPACE_ENVIRONAUT_1_0)
100.00%
1 / 1
1
100.00%
4 / 4
 getDefaultNamespaceUri()
100.00%
1 / 1
1
100.00%
1 / 1
 getDefaultNamespacePrefix()
100.00%
1 / 1
1
100.00%
1 / 1
 isEnvironautDocument()
100.00%
1 / 1
1
100.00%
1 / 1
 registerEnvironautNamespace(DOMDocument $doc)
0.00%
0 / 1
2
0.00%
0 / 2
 isEnvironautConfigurationDocument(DOMDocument $doc)
100.00%
1 / 1
3
100.00%
4 / 4
 refreshXpath()
100.00%
1 / 1
2
100.00%
6 / 6
 enableErrorHandling()
100.00%
1 / 1
1
100.00%
3 / 3
 handleErrors($msg_prefix = '', $msg_suffix = '', $user_error_handling = false)
100.00%
1 / 1
2
100.00%
9 / 9
 getErrorMessage(array $errors)
100.00%
1 / 1
2
100.00%
5 / 5
 parseError(\LibXMLError $error)
0.00%
0 / 1
5.20
80.00%
16 / 20
<?php
namespace Environaut\Config\Reader\Dom;
/**
 * Environaut DOM document implementation with convenience wrapper methods
 * that by default use the Environaut configuration namespace for queries.
 */
class DomDocument extends \DOMDocument
{
    /**
     * Default namespace of Environaut configuration files.
     */
    const NAMESPACE_ENVIRONAUT_1_0 = 'http://mivesto.de/environaut/config/1.0';
    /**
     * Default namespace prefix for Environaut configuration files.
     */
    const NAMESPACE_PREFIX = 'ec';
    /**
     * @var \DOMXpath xpath instance for this document
     */
    protected $xpath = null;
    /**
     * @var string default URI used as namespace for the document
     */
    protected $default_namespace_uri;
    /**
     * @var string default prefix for the default namespace of this document
     */
    protected $default_namespace_prefix;
    /**
     * @var array map of DOM classes and our implementations of them
     */
    protected $class_map = array(
        'DOMAttr' => 'Environaut\Config\Reader\Dom\DomAttribute',
        'DOMDocument' => 'Environaut\Config\Reader\Dom\DomDocument',
        'DOMElement' => 'Environaut\Config\Reader\Dom\DomElement'
    );
    /**
     * Creates a new DomDocument instance.
     *
     * @param string $version
     * @param string $encoding
     */
    public function __construct($version = "1.0", $encoding = "UTF-8")
    {
        parent::__construct($version, $encoding);
        foreach ($this->class_map as $dom_class => $environaut_class) {
            $this->registerNodeClass($dom_class, $environaut_class);
        }
        $this->xpath = new \DOMXPath($this);
    }
    /**
     * Loads XML from a file and registers the Environaut configuration
     * namespace if the XML document is an Environaut configuration file.
     *
     * @param string $filename xml file to load
     * @param int $options libXml flags to use for loading (like LIBXML_NOCDATA etc.)
     *
     * @return bool true on success; false on error
     *
     * @throws \DOMException in case of errors loading the document from the given file
     */
    public function load($filename, $options = 0)
    {
        $user_error_handling = $this->enableErrorHandling();
        $success = parent::load($filename, $options);
        $this->handleErrors(
            'Loading the document failed. Details are:' . PHP_EOL . PHP_EOL,
            PHP_EOL . 'Please fix the mentioned errors.',
            $user_error_handling
        );
        $this->refreshXpath();
        return $success;
    }
    /**
     * Loads XML from a string and registers the Environaut configuration
     * namespace if the XML document is an Environaut configuration file.
     *
     * @param string $source xml string to load
     * @param int $options libXml flags to use for loading (like LIBXML_NOCDATA etc.)
     *
     * @return bool true on success; false on error
     *
     * @throws \DOMException in case of errors loading the document from the given string
     */
    public function loadXml($source, $options = 0)
    {
        $user_error_handling = $this->enableErrorHandling();
        $success = parent::loadXML($source, $options);
        $this->handleErrors(
            'Loading the document failed. Details are:' . PHP_EOL . PHP_EOL,
            PHP_EOL . 'Please fix the mentioned errors.',
            $user_error_handling
        );
        $this->refreshXpath();
        return $success;
    }
    /**
     * Validates the current document according to the given schema file.
     *
     * @param string $filename path to the schema file
     *
     * @return bool true on success
     *
     * @throws \DomException in case of validation errors or non-readable file
     */
    public function schemaValidate($filename)
    {
        if (!is_readable($filename)) {
            throw new \DOMException("Schema file is not readable: $filename");
        }
        $user_error_handling = $this->enableErrorHandling();
        $success = parent::schemaValidate($filename);
        $this->handleErrors(
            'Validating the document failed. Details are:' . PHP_EOL . PHP_EOL,
            PHP_EOL . 'Please fix the mentioned errors or use another schema file.',
            $user_error_handling
        );
        return $success;
    }
    /**
     * Validates the current document according to the given schema file content.
     *
     * @param string $source content of a schema file
     *
     * @return bool true on success
     *
     * @throws \DomException in case of validation errors or empty schema
     */
    public function schemaValidateSource($source)
    {
        if (empty($source)) {
            throw new \DOMException('Schema is empty.');
        }
        $user_error_handling = $this->enableErrorHandling();
        $success = parent::schemaValidateSource($source);
        $this->handleErrors(
            'Validating the document failed. Details are:' . PHP_EOL . PHP_EOL,
            PHP_EOL . 'Please fix the mentioned errors or use another schema.',
            $user_error_handling
        );
        return $success;
    }
    /**
     * Substitutes XIncludes present in the current document.
     *
     * @param int $options libXml flags to use for loading (like LIBXML_NOCDATA etc.)
     *
     * @return int number of XIncludes or false if no xincludes were found
     *
     * @throws \DOMException in case of errors xincluding content
     */
    public function xinclude($options = 0)
    {
        $user_error_handling = $this->enableErrorHandling();
        $number_of_xincludes = parent::xinclude($options);
        $this->handleErrors(
            'Resolving XInclude directives in the current document failed. Details are:' . PHP_EOL . PHP_EOL,
            PHP_EOL . 'Please fix the XInclude directives according to the mentioned errors.',
            $user_error_handling
        );
        return $number_of_xincludes;
    }
    /**
     * Returns the xpath instance that handles this document.
     *
     * @return \DOMXpath instance
     */
    public function getXpath()
    {
        return $this->xpath;
    }
    /**
     * Returns the nodeValue of the child element with the given name.
     *
     * @param string $element_name name of child element
     * @param \DomElement $reference_element reference element for child node iteration, default to documentElement
     *
     * @return mixed nodeValue or null if Environaut element with that name does not exist
     *
     * @throws \InvalidArgumentException if name is empty
     */
    public function getElementValue($element_name, $reference_element = null)
    {
        $element_name = trim($element_name);
        if (empty($element_name)) {
            throw new \InvalidArgumentException('Element name must not be empty.');
        }
        if (null === $reference_element) {
            $reference_element = $this->documentElement;
        }
        if ($this->isEnvironautDocument()) {
            foreach ($reference_element->childNodes as $node) {
                if ($node->nodeType == XML_ELEMENT_NODE &&
                    $node->localName == $element_name &&
                    $node->namespaceURI == $this->documentElement->namespaceURI
                ) {
                    return $node->nodeValue;
                }
            }
        }
        return null;
    }
    /**
     * Returns the DomElement with the given name.
     *
     * @param string $name of the element to get
     *
     * @return DomElement of that name or null if does not exist in the Environaut namespace
     */
    public function getElement($name)
    {
        if ($this->isEnvironautDocument()) {
            foreach ($this->documentElement->childNodes as $node) {
                if ($node->nodeType == XML_ELEMENT_NODE &&
                    $node->localName == $name &&
                    $node->namespaceURI == $this->documentElement->namespaceURI
                ) {
                    return $node;
                }
            }
        }
        return null;
    }
    /**
     * Set the default namespace that should be used when accessing elements via
     * convenience methods and bind it to the internal xpath instance.
     *
     * @param string $prefix default prefix to use
     * @param string $uri namespace URI
     */
    public function setDefaultNamespace($prefix = self::NAMESPACE_PREFIX, $uri = self::NAMESPACE_ENVIRONAUT_1_0)
    {
        $this->default_namespace_uri = $uri;
        $this->default_namespace_prefix = $prefix;
        $this->xpath->registerNamespace($prefix, $uri);
    }
    /**
     * Returns the default namespace URI used by Environaut.
     *
     * @return string default namespace URI
     */
    public function getDefaultNamespaceUri()
    {
        return $this->default_namespace_uri;
    }
    /**
     * Returns the default namespace prefix used by Environaut
     *
     * @return string default namespace prefix
     */
    public function getDefaultNamespacePrefix()
    {
        return $this->default_namespace_prefix;
    }
    /**
     * Returns whether the current document is an Environaut configuration file.
     *
     * @return bool true if it's an Environaut document; false otherwise
     */
    public function isEnvironautDocument()
    {
        return self::isEnvironautConfigurationDocument($this);
    }
    /**
     * Registers the default Environaut namespace on the internal xpath
     * instance of the given DomDocument instance.
     *
     * @param DOMDocument $doc document instance
     */
    public static function registerEnvironautNamespace(DOMDocument $doc)
    {
        $doc->getXpath()->registerNamespace(self::NAMESPACE_PREFIX, self::NAMESPACE_ENVIRONAUT_1_0);
    }
    /**
     * Returns whether the given document instance is an Environaut configuration file.
     *
     * @param DOMDocument $doc document to check
     *
     * @return bool true if it's an Environaut document; false otherwise
     */
    public static function isEnvironautConfigurationDocument(DOMDocument $doc)
    {
        return (
            $doc->documentElement &&
            $doc->documentElement->localName === 'environaut' &&
            $doc->documentElement->namespaceURI === self::NAMESPACE_ENVIRONAUT_1_0
        );
    }
    /**
     * Re-instantiates the internal xpath instance and then
     * registers the Environaut config namespace if necessary.
     */
    protected function refreshXpath()
    {
        unset($this->xpath);
        $this->xpath = new \DOMXPath($this);
        if ($this->isEnvironautDocument()) {
            $this->setDefaultNamespace(self::NAMESPACE_PREFIX, self::NAMESPACE_ENVIRONAUT_1_0);
        }
    }
    /**
     * Disables libXml errors to let us handle errors by ourselves.
     *
     * @return bool whether or not the internal error handling was enabled before
     */
    protected function enableErrorHandling()
    {
        $user_error_handling = libxml_use_internal_errors(true);
        libxml_clear_errors();
        return $user_error_handling;
    }
    /**
     * Evaluates the last libXml operation and throws an exception
     * with a verbose multiline error message as text.
     *
     * @param string $msg_prefix prefix for the error message of the exception
     * @param string $msg_suffix suffix for the error message of the exception
     * @param bool $user_error_handling
     *
     * @throws \DOMException when libXml errors occurred
     */
    protected function handleErrors($msg_prefix = '', $msg_suffix = '', $user_error_handling = false)
    {
        if (libxml_get_last_error() !== false) {
            $errors = libxml_get_errors();
            libxml_clear_errors();
            libxml_use_internal_errors($user_error_handling);
            throw new \DOMException(
                $msg_prefix .
                $this->getErrorMessage($errors) .
                $msg_suffix
            );
        }
        libxml_use_internal_errors($user_error_handling);
    }
    /**
     * Returns a formatted error message from the given libXml errors.
     *
     * @param array $errors array of \LibXMLError instances
     *
     * @return string formatted error message
     */
    protected function getErrorMessage(array $errors)
    {
        $error_message = '';
        foreach ($errors as $error) {
            $error_message .= $this->parseError($error) . PHP_EOL . PHP_EOL;
        }
        return $error_message;
    }
    /**
     * Formats the given libXml error as a multiline string.
     *
     * @param \LibXMLError $error error to get as formatted string
     *
     * @return string formatted error message
     */
    protected function parseError(\LibXMLError $error)
    {
        $msg = '';
        switch ($error->level) {
            case LIBXML_ERR_WARNING:
                $msg .= 'Warning ' . $error->code . ': ';
                break;
            case LIBXML_ERR_FATAL:
                $msg .= 'Fatal error: ' . $error->code . ': ';
                break;
            case LIBXML_ERR_ERROR:
            default:
                $msg .= 'Error ' . $error->code . ': ';
                break;
        }
        $msg .= trim($error->message) . PHP_EOL .
                '  Line: ' . $error->line . PHP_EOL .
                'Column: ' . $error->column;
        if ($error->file) {
            $msg .= PHP_EOL . '  File: ' . $error->file;
        }
        return $msg;
    }
}