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