LibXmlException   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 106
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 31
c 1
b 0
f 0
dl 0
loc 106
ccs 31
cts 31
cp 1
rs 10
wmc 11

5 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 10 3
A __construct() 0 7 2
A createFromLibXml() 0 9 2
A useInternalErrors() 0 24 3
A getErrors() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Eclipxe\XmlSchemaValidator\Internal;
6
7
use Exception;
8
use InvalidArgumentException;
9
use LibXMLError;
10
11
class LibXmlException extends Exception
12
{
13
    private const ERROR_LEVEL_SUPPRESS_ALL = 0;
14
15
    /** @var LibXMLError[] */
16
    private $errors;
17
18
    /**
19
     * LibXmlException constructor.
20
     *
21
     * @param string $message
22
     * @param LibXMLError[] $errors
23
     * @throws InvalidArgumentException if errors array is empty
24
     */
25 3
    private function __construct(string $message, array $errors)
26
    {
27 3
        if ([] === $errors) {
28 1
            throw new InvalidArgumentException('Errors array of LibXmlError is empty');
29
        }
30 2
        parent::__construct($message);
31 2
        $this->errors = $errors;
32
    }
33
34
    /**
35
     * Create an instance with the provided information
36
     *
37
     * @param string $message
38
     * @param LibXMLError[] $errors
39
     * @return LibXmlException
40
     * @throws InvalidArgumentException when an error item is not a LibXmlError
41
     */
42 4
    public static function create(string $message, array $errors): self
43
    {
44
        /** @var mixed $error psalm found this validation contradictory since $errors is defined as LibXMLError[] */
45 4
        foreach ($errors as $index => $error) {
46 3
            if (! $error instanceof LibXMLError) {
47 1
                throw new InvalidArgumentException("Error index $index is not a LibXmlError");
48
            }
49
        }
50
51 3
        return new self($message, $errors);
52
    }
53
54
    /**
55
     * List of libxml errors
56
     *
57
     * @return LibXMLError[]
58
     */
59 2
    public function getErrors(): array
60
    {
61 2
        return $this->errors;
62
    }
63
64
    /**
65
     * Create a LibXmlException based on errors in libxml.
66
     * If found, clear the errors and chain all the error messages.
67
     *
68
     * @return LibXmlException|null
69
     */
70 5
    public static function createFromLibXml(): ?self
71
    {
72 5
        $errors = libxml_get_errors();
73 5
        if (! count($errors)) {
74 3
            return null;
75
        }
76 2
        libxml_clear_errors();
77 2
        $error = end($errors);
78 2
        return self::create($error->message, $errors);
79
    }
80
81
    /**
82
     * Execute a callable ensuring that the execution will occur inside an environment
83
     * where libxml use internal errors is true.
84
     *
85
     * After executing the callable the value of libxml use internal errors is set to
86
     * previous value.
87
     *
88
     * @param callable $callable
89
     * @return mixed
90
     *
91
     * @throws LibXmlException if some error inside libxml was found
92
     */
93 3
    public static function useInternalErrors(callable $callable)
94
    {
95
        // capture current error reporting level and set to no-errors
96 3
        $previousErrorReporting = error_reporting(self::ERROR_LEVEL_SUPPRESS_ALL);
97
98
        // capture current libxml use internal errors and set to true
99 3
        $previousLibXmlUseInternalErrors = libxml_use_internal_errors(true);
100 3
        if ($previousLibXmlUseInternalErrors) {
101 1
            libxml_clear_errors();
102
        }
103
104
        // run callable and throw libxml error as exception and always restore previous status
105
        try {
106
            /** @psalm-var mixed $return */
107 3
            $return = $callable();
108 3
            $exception = static::createFromLibXml();
109 3
            if (null !== $exception) {
110 1
                throw $exception;
111
            }
112 2
            return $return;
113
        } finally {
114
            // restore error reporting level and libxml use internal errors
115 3
            error_reporting($previousErrorReporting);
116 3
            libxml_use_internal_errors($previousLibXmlUseInternalErrors);
117
        }
118
    }
119
}
120