Completed
Push — master ( fa5569...0fb0f7 )
by Nikola
03:21
created

AbstractSaxHandler::parse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
ccs 10
cts 10
cp 1
rs 9.4286
cc 1
eloc 9
nc 1
nop 2
crap 1
1
<?php
2
/*
3
 * This file is part of the runopencode/sax, an RunOpenCode project.
4
 *
5
 * (c) 2016 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 8
    public function __construct(array $options = array())
30
    {
31 8
        $this->options = array_merge(array(
32
            'buffer_size' => 4096
33 8
        ), $options);
34 8
    }
35
36
    /**
37
     * {@inheritdoc}
38
     */
39 8
    final public function parse(StreamInterface $stream, callable $onResult = null)
40
    {
41 8
        $parser = xml_parser_create();
42
43 8
        $this->onDocumentStart($parser, $stream);
44
45 8
        $this->attachHandlers($parser);
46
47 8
        $this->process($parser, $stream);
48
49 6
        $this->onDocumentEnd($parser, $stream);
50
51 6
        xml_parser_free($parser);
52
53 6
        $stream->close();
54
55 6
        $this->onResult($onResult);
56 6
    }
57
58
    /**
59
     * Document start handler, executed when parsing process started.
60
     *
61
     * @param resource $parser Parser handler.
62
     * @param StreamInterface $stream XML stream.
63
     */
64
    abstract protected function onDocumentStart($parser, $stream);
65
66
    /**
67
     * Element start handler, executed when XML tag is entered.
68
     *
69
     * @param resource $parser Parser handler.
70
     * @param string $name Tag name.
71
     * @param array $attributes Element attributes.
72
     */
73
    abstract protected function onElementStart($parser, $name, $attributes);
74
75
    /**
76
     * Element CDATA handler, executed when XML tag CDATA is parsed.
77
     *
78
     * @param resource $parser Parser handler.
79
     * @param string $data Element CDATA.
80
     */
81
    abstract protected function onElementData($parser, $data);
82
83
    /**
84
     * Element end handler, executed when XML tag is leaved.
85
     *
86
     * @param resource $parser Parser handler.
87
     * @param string $name Tag name.
88
     */
89
    abstract protected function onElementEnd($parser, $name);
90
91
    /**
92
     * Document end handler, executed when parsing process ended.
93
     *
94
     * @param resource $parser Parser handler.
95
     * @param StreamInterface $stream XML stream.
96
     */
97
    abstract protected function onDocumentEnd($parser, $stream);
98
99
    /**
100
     * Parsing error handler.
101
     *
102
     * @param string $message Parsing error message.
103
     * @param int $code Error code.
104
     * @param int $lineno XML line number which caused error.
105
     */
106
    abstract protected function onParseError($message, $code, $lineno);
107
108
    /**
109
     * Result callable handler.
110
     *
111
     * Considering that your handler processed XML document, this method will collect
112
     * parsing result. This method is called last and it will provide parsing result to callable.
113
     *
114
     * Callable parameters are user defined and depends on defined handler API and user requirements.
115
     *
116
     * @param callable $callable Callable to execute when parsing is completed.
117
     */
118
    abstract protected function onResult(callable $callable = null);
119
120
    /**
121
     * Parse path to XML document/string content.
122
     *
123
     * @param resource $parser Parser.
124
     * @param StreamInterface $stream XML document stream.
125
     * @return AbstractSaxHandler $this Fluent interface.
126
     *
127
     * @throws \RuntimeException
128
     */
129 8
    private function process($parser, StreamInterface $stream)
130
    {
131 8
        if ($stream->eof()) {
132
            $stream->rewind();
133
        }
134
135 8
        while ($data = $stream->read($this->options['buffer_size'])) {
136 8
            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));
137 6
        }
138
139 6
        return $this;
140
    }
141
142
    /**
143
     * Attach handlers.
144
     *
145
     * @param resource $parser XML parser.
146
     * @return AbstractSaxHandler $this Fluent interface.
147
     */
148 8
    private function attachHandlers($parser)
149
    {
150 8
        xml_set_element_handler(
151 8
            $parser,
152
            \Closure::bind(function($parser, $name, $attributes) {
153 8
                $this->onElementStart($parser, $name, $attributes);
154 8
            }, $this),
155
            \Closure::bind(function($parser, $name) {
156 8
                $this->onElementEnd($parser, $name);
157 8
            }, $this)
158 8
        );
159
160 8
        xml_set_character_data_handler(
161 8
            $parser,
162 8
            \Closure::bind(function($parser, $data) {
163 8
                $this->onElementData($parser, $data);
164 8
            }, $this));
165
166 8
        return $this;
167
    }
168
}
169