Completed
Push — master ( 58d0b6...f4d8f3 )
by Nikola
01:55
created

AbstractSaxHandler::onElementData()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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