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; 24*8792b649SGreg 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*8792b649SGreg Roach /** @var resource|XMLParser 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