xref: /webtrees/app/Report/ReportParserBase.php (revision 4e69366a139deb61a3f41ad48d0068262443eb94)
1ef0d468bSGreg Roach<?php
23976b470SGreg Roach
3ef0d468bSGreg Roach/**
4ef0d468bSGreg Roach * webtrees: online genealogy
589f7189bSGreg Roach * Copyright (C) 2021 webtrees development team
6ef0d468bSGreg Roach * This program is free software: you can redistribute it and/or modify
7ef0d468bSGreg Roach * it under the terms of the GNU General Public License as published by
8ef0d468bSGreg Roach * the Free Software Foundation, either version 3 of the License, or
9ef0d468bSGreg Roach * (at your option) any later version.
10ef0d468bSGreg Roach * This program is distributed in the hope that it will be useful,
11ef0d468bSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12ef0d468bSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13ef0d468bSGreg Roach * GNU General Public License for more details.
14ef0d468bSGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
16ef0d468bSGreg Roach */
17fcfa147eSGreg Roach
18e7f56f2aSGreg Roachdeclare(strict_types=1);
19e7f56f2aSGreg Roach
2076692c8bSGreg Roachnamespace Fisharebest\Webtrees\Report;
21ef0d468bSGreg Roach
22208e9f76SGreg Roachuse DomainException;
237774ce41SGreg Roachuse Exception;
248792b649SGreg Roachuse XMLParser;
25299d100dSGreg Roach
26b19e047dSGreg Roachuse function call_user_func;
27b6f35a76SGreg Roachuse function fclose;
28b6f35a76SGreg Roachuse function feof;
29b6f35a76SGreg Roachuse function fopen;
30b6f35a76SGreg Roachuse function fread;
31b19e047dSGreg Roachuse function method_exists;
32b6f35a76SGreg Roachuse function sprintf;
33b6f35a76SGreg Roachuse function xml_error_string;
34b6f35a76SGreg Roachuse function xml_get_current_line_number;
35b6f35a76SGreg Roachuse function xml_get_error_code;
36b6f35a76SGreg Roachuse function xml_parse;
37b6f35a76SGreg Roachuse function xml_parser_create;
38b6f35a76SGreg Roachuse function xml_parser_free;
39b6f35a76SGreg Roachuse function xml_parser_set_option;
40b6f35a76SGreg Roachuse function xml_set_character_data_handler;
41b6f35a76SGreg Roachuse function xml_set_element_handler;
42b6f35a76SGreg Roach
43b6f35a76SGreg Roachuse const XML_OPTION_CASE_FOLDING;
44b19e047dSGreg Roach
45ef0d468bSGreg Roach/**
46ef0d468bSGreg Roach * Class ReportParserBase
47ef0d468bSGreg Roach */
48c1010edaSGreg Roachclass ReportParserBase
49c1010edaSGreg Roach{
50*4e69366aSGreg Roach    /** @var XMLParser (resource before PHP 8.0) The XML parser */
51a6f13a4aSGreg Roach    protected $xml_parser;
52ef0d468bSGreg Roach
53ef0d468bSGreg Roach    /** @var string Text contents of tags */
54ef0d468bSGreg Roach    protected $text = '';
55ef0d468bSGreg Roach
56ef0d468bSGreg Roach    /**
57ef0d468bSGreg Roach     * Create a parser for a report
58ef0d468bSGreg Roach     *
59ef0d468bSGreg Roach     * @param string $report The XML filename
607774ce41SGreg Roach     *
617774ce41SGreg Roach     * @throws Exception
62ef0d468bSGreg Roach     */
6376f666f4SGreg Roach    public function __construct(string $report)
64c1010edaSGreg Roach    {
65ef0d468bSGreg Roach        $this->xml_parser = xml_parser_create();
668a4ee39cSGreg Roach
67ef0d468bSGreg Roach        xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, false);
688a4ee39cSGreg Roach
698a4ee39cSGreg Roach        xml_set_element_handler(
708a4ee39cSGreg Roach            $this->xml_parser,
71af14d238SGreg Roach            function ($parser, string $name, array $attrs): void {
728a4ee39cSGreg Roach                $this->startElement($parser, $name, $attrs);
738a4ee39cSGreg Roach            },
74af14d238SGreg Roach            function ($parser, string $name): void {
758a4ee39cSGreg Roach                $this->endElement($parser, $name);
768a4ee39cSGreg Roach            }
778a4ee39cSGreg Roach        );
788a4ee39cSGreg Roach
798a4ee39cSGreg Roach        xml_set_character_data_handler(
808a4ee39cSGreg Roach            $this->xml_parser,
819d454b6bSGreg Roach            function ($parser, string $data): void {
828a4ee39cSGreg Roach                $this->characterData($parser, $data);
838a4ee39cSGreg Roach            }
848a4ee39cSGreg Roach        );
85ef0d468bSGreg Roach
86e364afe4SGreg Roach        $fp = fopen($report, 'rb');
878a4ee39cSGreg Roach
887774ce41SGreg Roach        if ($fp === false) {
897774ce41SGreg Roach            throw new Exception('Cannot open ' . $report);
907774ce41SGreg Roach        }
917774ce41SGreg Roach
92e364afe4SGreg Roach        while ($data = fread($fp, 4096)) {
93ef0d468bSGreg Roach            if (!xml_parse($this->xml_parser, $data, feof($fp))) {
94208e9f76SGreg Roach                throw new DomainException(sprintf(
95ef0d468bSGreg Roach                    'XML error: %s at line %d',
96ef0d468bSGreg Roach                    xml_error_string(xml_get_error_code($this->xml_parser)),
97ef0d468bSGreg Roach                    xml_get_current_line_number($this->xml_parser)
98ef0d468bSGreg Roach                ));
99ef0d468bSGreg Roach            }
100ef0d468bSGreg Roach        }
101ef0d468bSGreg Roach
1027774ce41SGreg Roach        fclose($fp);
1037774ce41SGreg Roach
104ef0d468bSGreg Roach        xml_parser_free($this->xml_parser);
105ef0d468bSGreg Roach    }
106ef0d468bSGreg Roach
107ef0d468bSGreg Roach    /**
108ef0d468bSGreg Roach     * XML handler for an opening (or self-closing) tag.
109ef0d468bSGreg Roach     *
110ef0d468bSGreg Roach     * @param resource $parser The resource handler for the xml parser
111ef0d468bSGreg Roach     * @param string   $name   The name of the xml element parsed
112ef0d468bSGreg Roach     * @param string[] $attrs  An array of key value pairs for the attributes
11318d7a90dSGreg Roach     *
11418d7a90dSGreg Roach     * @return void
115ef0d468bSGreg Roach     */
116af14d238SGreg Roach    protected function startElement($parser, string $name, array $attrs): void
117c1010edaSGreg Roach    {
118a6f13a4aSGreg Roach        $method = $name . 'StartHandler';
119208e9f76SGreg Roach
120ef0d468bSGreg Roach        if (method_exists($this, $method)) {
121b19e047dSGreg Roach            call_user_func([$this, $method], $attrs);
122ef0d468bSGreg Roach        }
123ef0d468bSGreg Roach    }
124ef0d468bSGreg Roach
125ef0d468bSGreg Roach    /**
126ef0d468bSGreg Roach     * XML handler for a closing tag.
127ef0d468bSGreg Roach     *
128ef0d468bSGreg Roach     * @param resource $parser the resource handler for the xml parser
129ef0d468bSGreg Roach     * @param string   $name   the name of the xml element parsed
13018d7a90dSGreg Roach     *
13118d7a90dSGreg Roach     * @return void
132ef0d468bSGreg Roach     */
133af14d238SGreg Roach    protected function endElement($parser, string $name): void
134c1010edaSGreg Roach    {
135a6f13a4aSGreg Roach        $method = $name . 'EndHandler';
136208e9f76SGreg Roach
137ef0d468bSGreg Roach        if (method_exists($this, $method)) {
138b19e047dSGreg Roach            call_user_func([$this, $method]);
139ef0d468bSGreg Roach        }
140ef0d468bSGreg Roach    }
141ef0d468bSGreg Roach
142ef0d468bSGreg Roach    /**
143ef0d468bSGreg Roach     * XML handler for character data.
144ef0d468bSGreg Roach     *
145ef0d468bSGreg Roach     * @param resource $parser The resource handler for the xml parser
146ef0d468bSGreg Roach     * @param string   $data   The name of the xml element parsed
14718d7a90dSGreg Roach     *
14818d7a90dSGreg Roach     * @return void
149ef0d468bSGreg Roach     */
15024f2a3afSGreg Roach    protected function characterData($parser, string $data): void
151c1010edaSGreg Roach    {
152ef0d468bSGreg Roach        $this->text .= $data;
153ef0d468bSGreg Roach    }
154ef0d468bSGreg Roach}
155