xref: /webtrees/app/Report/ReportParserBase.php (revision 59597b37d69e8147c3f4a27643e9c8edaa2a0592)
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 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Report;
21
22use DomainException;
23use Exception;
24
25/**
26 * Class ReportParserBase
27 */
28class ReportParserBase
29{
30    /** @var resource The XML parser */
31    protected $xml_parser;
32
33    /** @var string Text contents of tags */
34    protected $text = '';
35
36    /**
37     * Create a parser for a report
38     *
39     * @param string $report The XML filename
40     *
41     * @throws Exception
42     */
43    public function __construct(string $report)
44    {
45        $this->xml_parser = xml_parser_create();
46
47        xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, false);
48
49        xml_set_element_handler(
50            $this->xml_parser,
51            function ($parser, string $name, array $attrs): void {
52                $this->startElement($parser, $name, $attrs);
53            },
54            function ($parser, string $name): void {
55                $this->endElement($parser, $name);
56            }
57        );
58
59        xml_set_character_data_handler(
60            $this->xml_parser,
61            function ($parser, string $data): void {
62                $this->characterData($parser, $data);
63            }
64        );
65
66        $fp = fopen($report, 'rb');
67
68        if ($fp === false) {
69            throw new Exception('Cannot open ' . $report);
70        }
71
72        while ($data = fread($fp, 4096)) {
73            if (!xml_parse($this->xml_parser, $data, feof($fp))) {
74                throw new DomainException(sprintf(
75                    'XML error: %s at line %d',
76                    xml_error_string(xml_get_error_code($this->xml_parser)),
77                    xml_get_current_line_number($this->xml_parser)
78                ));
79            }
80        }
81
82        fclose($fp);
83
84        xml_parser_free($this->xml_parser);
85    }
86
87    /**
88     * XML handler for an opening (or self-closing) tag.
89     *
90     * @param resource $parser The resource handler for the xml parser
91     * @param string   $name   The name of the xml element parsed
92     * @param string[] $attrs  An array of key value pairs for the attributes
93     *
94     * @return void
95     */
96    protected function startElement($parser, string $name, array $attrs): void
97    {
98        $method = $name . 'StartHandler';
99
100        if (method_exists($this, $method)) {
101            $this->$method($attrs);
102        }
103    }
104
105    /**
106     * XML handler for a closing tag.
107     *
108     * @param resource $parser the resource handler for the xml parser
109     * @param string   $name   the name of the xml element parsed
110     *
111     * @return void
112     */
113    protected function endElement($parser, string $name): void
114    {
115        $method = $name . 'EndHandler';
116
117        if (method_exists($this, $method)) {
118            $this->$method();
119        }
120    }
121
122    /**
123     * XML handler for character data.
124     *
125     * @param resource $parser The resource handler for the xml parser
126     * @param string   $data   The name of the xml element parsed
127     *
128     * @return void
129     */
130    protected function characterData($parser, $data): void
131    {
132        $this->text .= $data;
133    }
134}
135