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