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