Completed
Push — master ( 57e351...776438 )
by Nikola
02:30
created

AbstractSaxHandler::getDeclaredNamespaces()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/*
3
 * This file is part of the runopencode/sax, an RunOpenCode project.
4
 *
5
 * (c) 2017 RunOpenCode
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace RunOpenCode\Sax\Handler;
11
12
use Psr\Http\Message\StreamInterface;
13
use RunOpenCode\Sax\Contract\SaxHandlerInterface;
14
15
/**
16
 * Class AbstractSaxHandler
17
 *
18
 * Sax handler prototype.
19
 *
20
 * @package RunOpenCode\Sax
21
 */
22
abstract class AbstractSaxHandler implements SaxHandlerInterface
23
{
24
    /**
25
     * @var array
26
     */
27
    protected $options;
28
29
    /**
30
     * @var string|null
31
     */
32
    private $currentElement = null;
33
34
    /**
35
     * @var int
36
     */
37
    private $stackSize = 0;
38
39
    /**
40
     * @var string|null
41
     */
42
    private $dataBuffer = null;
43
44
    /**
45
     * @var array
46
     */
47
    private $namespaces = [];
48
49
    /**
50
     * AbstractSaxHandler constructor.
51
     *
52
     * @param array $options
53
     */
54 2
    public function __construct(array $options = array())
55
    {
56 2
        $this->options = array_merge(array(
57 2
            'buffer_size'   => 4096,
58
            'case_folding'  => true,
59
            'separator'     => ':',
60
            'encoding'      => 'UTF-8',
61
            'skip_tagstart' => null,
62
            'skip_white'    => null,
63 2
        ), $options);
64 2
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 1
    final public function parse(StreamInterface $stream)
70
    {
71 1
        $parser = (true === $this->options['namespaces'])
72
            ?
73
            xml_parser_create_ns($this->options['encoding'], $this->options['separator'])
74
            :
75
            xml_parser_create($this->options['encoding']);
76
77
        if (false === $this->options['case_folding']) {
78
            xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
79
        }
80
81
        if (null === $this->options['skip_tagstart']) {
82
            xml_parser_set_option($parser, XML_OPTION_SKIP_TAGSTART, $this->options['skip_tagstart']);
83
        }
84
85
        if (null === $this->options['skip_white']) {
86
            xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, $this->options['skip_white']);
87
        }
88
89
        $this->onDocumentStart($parser, $stream);
90
91
        $this->attachHandlers($parser);
92
93
        $this->process($parser, $stream);
94
95
        $this->onDocumentEnd($parser, $stream);
96
97
        xml_parser_free($parser);
98
99
        $stream->close();
100
101
        return $this->getResult();
102
    }
103
104
    /**
105
     * Document start handler, executed when parsing process started.
106
     *
107
     * @param resource $parser Parser handler.
108
     * @param StreamInterface $stream XML stream.
109
     */
110
    abstract protected function onDocumentStart($parser, $stream);
111
112
    /**
113
     * Element start handler, executed when XML tag is entered.
114
     *
115
     * @param resource $parser Parser handler.
116
     * @param string $name Tag name.
117
     * @param array $attributes Element attributes.
118
     */
119
    abstract protected function onElementStart($parser, $name, $attributes);
120
121
    /**
122
     * Element CDATA handler, executed when XML tag CDATA is parsed.
123
     *
124
     * @param resource $parser Parser handler.
125
     * @param string $data Element CDATA.
126
     */
127
    abstract protected function onElementData($parser, $data);
128
129
    /**
130
     * Element end handler, executed when XML tag is leaved.
131
     *
132
     * @param resource $parser Parser handler.
133
     * @param string $name Tag name.
134
     */
135
    abstract protected function onElementEnd($parser, $name);
136
137
    /**
138
     * Document end handler, executed when parsing process ended.
139
     *
140
     * @param resource $parser Parser handler.
141
     * @param StreamInterface $stream XML stream.
142
     */
143
    abstract protected function onDocumentEnd($parser, $stream);
144
145
    /**
146
     * Parsing error handler.
147
     *
148
     * @param string $message Parsing error message.
149
     * @param int $code Error code.
150
     * @param int $lineno XML line number which caused error.
151
     */
152
    abstract protected function onParseError($message, $code, $lineno);
153
154
    /**
155
     * Get parsing result.
156
     *
157
     * Considering that your handler processed XML document, this method will collect
158
     * parsing result. This method is called last and it will provide parsing result to invoker.
159
     *
160
     * @return mixed Parsing result
161
     */
162
    abstract protected function getResult();
163
164
    /**
165
     * Start namespace declaration handler, executed when namespace declaration started.
166
     *
167
     * @param resource $parser Parser handler.
168
     * @param string $prefix Namespace reference within an XML object.
169
     * @param string $uri Uniform Resource Identifier (URI) of namespace.
170
     */
171
    protected function onNamespaceDeclarationStart($parser, $prefix, $uri)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $prefix is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $uri is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
172
    {
173
        // noop
174
    }
175
176
    /**
177
     * End namespace declaration handler, executed when namespace declaration ended.
178
     *
179
     * @param resource $parser Parser handler.
180
     * @param string $prefix Namespace reference within an XML object.
181
     */
182
    protected function onNamespaceDeclarationEnd($parser, $prefix)
0 ignored issues
show
Unused Code introduced by
The parameter $parser is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $prefix is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
183
    {
184
        // noop
185
    }
186
187
    /**
188
     * Get declared namespaces.
189
     *
190
     * Retrieve declared namespaces as associative array where keys are
191
     * used prefixes within XML document. Note that only processed namespace
192
     * declarations will be provided.
193
     *
194
     * @return array
195
     */
196
    final protected function getDeclaredNamespaces()
197
    {
198
        return $this->namespaces;
199
    }
200
201
    /**
202
     * Parse path to XML document/string content.
203
     *
204
     * @param resource $parser Parser.
205
     * @param StreamInterface $stream XML document stream.
206
     * @return AbstractSaxHandler $this Fluent interface.
207
     *
208
     * @throws \RuntimeException
209
     */
210
    private function process($parser, StreamInterface $stream)
211
    {
212
        if ($stream->eof()) {
213
            $stream->rewind();
214
        }
215
216
        while ($data = $stream->read($this->options['buffer_size'])) {
217
            xml_parse($parser, $data, $stream->eof()) || $this->onParseError(xml_error_string(xml_get_error_code($parser)), xml_get_error_code($parser), xml_get_current_line_number($parser));
218
        }
219
220
        return $this;
221
    }
222
223
    /**
224
     * Attach handlers.
225
     *
226
     * @param resource $parser XML parser.
227
     * @return AbstractSaxHandler $this Fluent interface.
228
     */
229
    private function attachHandlers($parser)
230
    {
231
        $onElementStart = \Closure::bind(function ($parser, $name, $attributes) {
232
            $this->currentElement = $name;
233
            $this->stackSize++;
234
            $this->dataBuffer = null;
235
236
            $this->onElementStart($parser, $name, $attributes);
237
        }, $this);
238
239
        $onElementEnd   = \Closure::bind(function ($parser, $name) {
240
            $this->currentElement = null;
241
            $this->stackSize--;
242
243
            if (null !== $this->dataBuffer) {
244
                $this->onElementData($parser, $this->dataBuffer);
245
            }
246
247
            $this->dataBuffer = null;
248
249
            $this->onElementEnd($parser, $name);
250
        }, $this);
251
252
        $onElementData  =  \Closure::bind(function ($parser, $data) {
253
            $this->dataBuffer .= $data;
254
        }, $this);
255
256
        $onNamespaceDeclarationStart = \Closure::bind(function ($parser, $prefix, $uri) {
257
            $this->namespaces[$prefix] = $uri;
258
            $this->onNamespaceDeclarationStart($parser, $prefix, $uri);
259
        }, $this);
260
261
        $onNamespaceDeclarationEnd = \Closure::bind(function ($parser, $prefix) {
262
            $this->onNamespaceDeclarationEnd($parser, $prefix);
263
        }, $this);
264
265
        xml_set_element_handler($parser, $onElementStart, $onElementEnd);
266
267
        xml_set_character_data_handler($parser, $onElementData);
268
269
        xml_set_start_namespace_decl_handler($parser, $onNamespaceDeclarationStart);
270
271
        xml_set_end_namespace_decl_handler($parser, $onNamespaceDeclarationEnd);
272
273
        return $this;
274
    }
275
}
276