• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

BenMorel / XMLStreamer / 19268317324

11 Nov 2025 02:12PM UTC coverage: 91.935%. Remained the same
19268317324

push

github

BenMorel
Require PHP 8.1

57 of 62 relevant lines covered (91.94%)

15.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.88
/src/XMLReader.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace BenMorel\XMLStreamer;
6

7
/**
8
 * Wrapper for the native XMLReader, that throws exceptions instead of triggering PHP errors.
9
 */
10
final class XMLReader
11
{
12
    /**
13
     * The wrapped native XMLReader instance.
14
     */
15
    private \XMLReader $xmlReader;
16

17
    /**
18
     * The transient error handler used to catch PHP errors triggered in the native XMLReader class.
19
     *
20
     * @var \Closure(int, string): never
21
     */
22
    private \Closure $errorHandler;
23

24
    /**
25
     * XMLReader constructor.
26
     */
27
    public function __construct()
28
    {
29
        $this->xmlReader = new \XMLReader();
26✔
30

31
        /** @psalm-suppress UnusedClosureParam */
32
        $this->errorHandler = static function(int $severity, string $message) : void {
26✔
33
            throw new XMLReaderException($message);
8✔
34
        };
26✔
35
    }
36

37
    /**
38
     * Returns the depth of the node in the tree, starting at zero.
39
     */
40
    public function depth() : int
41
    {
42
        return $this->xmlReader->depth;
21✔
43
    }
44

45
    /**
46
     * Returns the qualified name of the current node.
47
     *
48
     * Returns an empty string if the current node does not have a name.
49
     */
50
    public function name() : string
51
    {
52
        return $this->xmlReader->name;
21✔
53
    }
54

55
    /**
56
     * Returns the type of the current node, as an XMLReader::* constant.
57
     */
58
    public function nodeType() : int
59
    {
60
        return $this->xmlReader->nodeType;
25✔
61
    }
62

63
    /**
64
     * Opens a URI containing an XML document to parse.
65
     *
66
     * @param string      $uri      URI pointing to the document.
67
     * @param string|null $encoding The document encoding or NULL.
68
     * @param int         $options  A bitmask of the LIBXML_* constants.
69
     *
70
     * @throws XMLReaderException
71
     */
72
    public function open(string $uri, ?string $encoding = null, int $options = 0) : void
73
    {
74
        set_error_handler($this->errorHandler);
26✔
75

76
        try {
77
            $result = $this->xmlReader->open($uri, $encoding, $options);
26✔
78
        } finally {
79
            restore_error_handler();
26✔
80
        }
81

82
        if ($result !== true) {
25✔
83
            throw XMLReaderException::unknownError(__FUNCTION__);
×
84
        }
85
    }
86

87
    /**
88
     * Closes the input the XMLReader object is currently parsing.
89
     *
90
     * @throws XMLReaderException
91
     */
92
    public function close() : void
93
    {
94
        set_error_handler($this->errorHandler);
18✔
95

96
        try {
97
            $result = $this->xmlReader->close();
18✔
98
        } finally {
99
            restore_error_handler();
18✔
100
        }
101

102
        if ($result !== true) {
18✔
103
            throw XMLReaderException::unknownError(__FUNCTION__);
×
104
        }
105
    }
106

107
    /**
108
     * Returns a copy of the current node as a DOM object.
109
     *
110
     * @throws XMLReaderException
111
     */
112
    public function expand(?\DOMNode $baseNode = null) : \DOMNode
113
    {
114
        set_error_handler($this->errorHandler);
11✔
115

116
        try {
117
            /** @psalm-suppress PossiblyNullArgument https://github.com/vimeo/psalm/pull/8641 */
118
            $result = $this->xmlReader->expand($baseNode);
11✔
119
        } finally {
120
            restore_error_handler();
11✔
121
        }
122

123
        if ($result === false) {
11✔
124
            throw XMLReaderException::unknownError(__FUNCTION__);
×
125
        }
126

127
        return $result;
11✔
128
    }
129

130
    /**
131
     * Moves the cursor to the next node in the document.
132
     *
133
     * @return bool True if successful, false is there are no more nodes.
134
     *
135
     * @throws XMLReaderException
136
     */
137
    public function read() : bool
138
    {
139
        set_error_handler($this->errorHandler);
25✔
140

141
        try {
142
            $result = $this->xmlReader->read();
25✔
143
        } finally {
144
            restore_error_handler();
25✔
145
        }
146

147
        return $result;
21✔
148
    }
149

150
    /**
151
     * Moves the cursor to the next node in the document, skipping all subtrees.
152
     *
153
     * @return bool True if successful, false is there are no more nodes.
154
     *
155
     * @throws XMLReaderException
156
     */
157
    public function next(?string $localName = null) : bool
158
    {
159
        set_error_handler($this->errorHandler);
20✔
160

161
        try {
162
            if ($localName !== null) {
20✔
163
                $result = $this->xmlReader->next($localName);
×
164
            } else {
165
                $result = $this->xmlReader->next();
20✔
166
            }
167
        } finally {
168
            restore_error_handler();
20✔
169
        }
170

171
        return $result;
17✔
172
    }
173
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc