1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
<?php
/**
* webtrees: online genealogy
* Copyright (C) 2023 webtrees development team
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace Fisharebest\Webtrees\Report;
use DomainException;
use Exception;
use XMLParser;
use function fclose;
use function feof;
use function fread;
use function method_exists;
use function sprintf;
use function xml_error_string;
use function xml_get_current_line_number;
use function xml_get_error_code;
use function xml_parse;
use function xml_parser_create;
use function xml_parser_free;
use function xml_parser_set_option;
use function xml_set_character_data_handler;
use function xml_set_element_handler;
use const XML_OPTION_CASE_FOLDING;
class ReportParserBase
{
// The XML parser
protected XMLParser $xml_parser;
/** @var string Text contents of tags */
protected string $text = '';
/**
* Create a parser for a report
*
* @param string $report The XML filename
*
* @throws Exception
*/
public function __construct(string $report)
{
$this->xml_parser = xml_parser_create();
xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, 0);
xml_set_element_handler(
$this->xml_parser,
function ($parser, string $name, array $attrs): void {
$this->startElement($parser, $name, $attrs);
},
function ($parser, string $name): void {
$this->endElement($parser, $name);
}
);
xml_set_character_data_handler(
$this->xml_parser,
function ($parser, string $data): void {
$this->characterData($parser, $data);
}
);
$fp = fopen($report, 'rb');
while ($data = fread($fp, 4096)) {
if (!xml_parse($this->xml_parser, $data, feof($fp))) {
throw new DomainException(sprintf(
'XML error: %s at line %d',
xml_error_string(xml_get_error_code($this->xml_parser)),
xml_get_current_line_number($this->xml_parser)
));
}
}
fclose($fp);
xml_parser_free($this->xml_parser);
}
/**
* XML handler for an opening (or self-closing) tag.
*
* @param resource $parser The resource handler for the xml parser
* @param string $name The name of the xml element parsed
* @param array<string> $attrs An array of key value pairs for the attributes
*
* @return void
*/
protected function startElement($parser, string $name, array $attrs): void
{
$method = $name . 'StartHandler';
if (method_exists($this, $method)) {
$this->$method($attrs);
}
}
/**
* XML handler for a closing tag.
*
* @param resource $parser the resource handler for the xml parser
* @param string $name the name of the xml element parsed
*
* @return void
*/
protected function endElement($parser, string $name): void
{
$method = $name . 'EndHandler';
if (method_exists($this, $method)) {
$this->$method();
}
}
/**
* XML handler for character data.
*
* @param resource $parser The resource handler for the xml parser
* @param string $data The name of the xml element parsed
*
* @return void
*/
protected function characterData($parser, string $data): void
{
$this->text .= $data;
}
}
|