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