Passed
Push — master ( add283...7568bf )
by Carlos C
02:10 queued 11s
created

LibXmlException::useInternalErrors()   A

Complexity

Conditions 3
Paths 8

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 14
c 1
b 0
f 0
nc 8
nop 1
dl 0
loc 25
ccs 13
cts 13
cp 1
crap 3
rs 9.7998
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
    /** @var LibXMLError[] */
14
    private $errors;
15
16
    /**
17
     * LibXmlException constructor.
18
     *
19
     * @param string $message
20
     * @param LibXMLError[] $errors
21
     * @throws InvalidArgumentException if errors array is empty
22
     */
23 2
    private function __construct(string $message, array $errors)
24
    {
25 2
        if ([] === $errors) {
26 1
            throw new InvalidArgumentException('Errors array of LibXmlError is empty');
27
        }
28 1
        parent::__construct($message);
29 1
        $this->errors = $errors;
30 1
    }
31
32
    /**
33
     * Create an instance with the provided information
34
     *
35
     * @param string $message
36
     * @param LibXMLError[] $errors
37
     * @return LibXmlException
38
     * @throws InvalidArgumentException when an error item is not a LibXmlError
39
     */
40 2
    public static function create(string $message, array $errors): self
41
    {
42
        /** @var mixed $error psalm found this validation contradictory since $errors is defined as LibXMLError[] */
43 2
        foreach ($errors as $index => $error) {
44 1
            if (! $error instanceof LibXMLError) {
45 1
                throw new InvalidArgumentException("Error index $index is not a LibXmlError");
46
            }
47
        }
48
49 1
        return new self($message, $errors);
50
    }
51
52
    /**
53
     * List of libxml errors
54
     *
55
     * @return LibXMLError[]
56
     */
57 1
    public function getErrors(): array
58
    {
59 1
        return $this->errors;
60
    }
61
62
    /**
63
     * Create a LibXmlException based on errors in libxml.
64
     * If found, clear the errors and chain all the error messages.
65
     *
66
     * @return LibXmlException|null
67
     */
68 4
    public static function createFromLibXml(): ?self
69
    {
70 4
        $errors = libxml_get_errors();
71 4
        if (! count($errors)) {
72 3
            return null;
73
        }
74 1
        libxml_clear_errors();
75
        /** @var LibXMLError $error */
76 1
        $error = end($errors);
77 1
        return new self($error->message, $errors);
78
    }
79
80
    /**
81
     * Execute a callable ensuring that the execution will occur inside an environment
82
     * where libxml use internal errors is true.
83
     *
84
     * After executing the callable the value of libxml use internal errors is set to
85
     * previous value.
86
     *
87
     * @param callable $callable
88
     * @return mixed
89
     *
90
     * @throws LibXmlException if some error inside libxml was found
91
     */
92 3
    public static function useInternalErrors(callable $callable)
93
    {
94
        // capture current error reporting level and set to no-errors
95 3
        $previousErrorReporting = error_reporting();
96 3
        error_reporting(0);
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