Completed
Push — master ( 91bb83...325361 )
by Nikola
05:28
created

AbstractSaxHandler::onElementStart()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 1
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) 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
    public function __construct(array $options = array())
30
    {
31
        $this->options = array_merge(array(
32
            'buffer_size' => 4096
33
        ), $options);
34
    }
35
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public final function parse(StreamInterface $stream, callable $onResult = null)
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
40
    {
41
        $parser = xml_parser_create();
42
43
        $this->onDocumentStart($parser, $stream);
44
45
        $this->attachHandlers($parser);
46
47
        $this->process($parser, $stream);
48
49
        $this->onDocumentEnd($parser, $stream);
50
51
        xml_parser_free($parser);
52
53
        $stream->close();
54
55
        $this->onResult($onResult);
56
    }
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
    protected abstract function onDocumentStart($parser, $stream);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
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
    protected abstract function onElementStart($parser, $name, $attributes);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
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
    protected abstract function onElementData($parser, $data);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
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
    protected abstract function onElementEnd($parser, $name);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
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
    protected abstract function onDocumentEnd($parser, $stream);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
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
    protected abstract function onParseError($message, $code, $lineno);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
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
    protected abstract function onResult(callable $callable = null);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
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
    private function process($parser, StreamInterface $stream)
130
    {
131
        if ($stream->eof()) {
132
            $stream->rewind();
133
        }
134
135
        while ($data = $stream->read($this->options['buffer_size'])) {
136
            xml_parse($parser, $data, $stream->eof()) or $this->onParseError(xml_error_string(xml_get_error_code($parser)), xml_get_error_code($parser), xml_get_current_line_number($parser));
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
137
        }
138
139
        return $this;
140
    }
141
142
    /**
143
     * Attach handlers.
144
     *
145
     * @param resource $parser XML parser.
146
     * @return AbstractSaxHandler $this Fluent interface.
147
     */
148
    private function attachHandlers($parser)
149
    {
150
        xml_set_element_handler(
151
            $parser,
152
            \Closure::bind(function($parser, $name, $attributes) {
153
                $this->onElementStart($parser, $name, $attributes);
154
            }, $this),
155
            \Closure::bind(function($parser, $name) {
156
                $this->onElementEnd($parser, $name);
157
            }, $this)
158
        );
159
160
        xml_set_character_data_handler(
161
            $parser,
162
            \Closure::bind(function($parser, $data) {
163
                $this->onElementData($parser, $data);
164
            }, $this));
165
166
        return $this;
167
    }
168
}
169