. */ declare(strict_types=1); namespace Fisharebest\Webtrees\Report; use DomainException; use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Carbon; use Fisharebest\Webtrees\Date; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Family; use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\Functions\Functions; use Fisharebest\Webtrees\Gedcom; use Fisharebest\Webtrees\GedcomRecord; use Fisharebest\Webtrees\GedcomTag; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Individual; use Fisharebest\Webtrees\Log; use Fisharebest\Webtrees\MediaFile; use Fisharebest\Webtrees\Note; use Fisharebest\Webtrees\Place; use Fisharebest\Webtrees\Tree; use Illuminate\Database\Capsule\Manager as DB; use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\Expression; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Str; use League\Flysystem\FilesystemOperator; use LogicException; use stdClass; use Symfony\Component\Cache\Adapter\NullAdapter; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use function addcslashes; use function addslashes; use function array_pop; use function array_shift; use function assert; use function call_user_func; use function count; use function end; use function explode; use function file; use function file_exists; use function getimagesize; use function imagecreatefromstring; use function imagesx; use function imagesy; use function in_array; use function method_exists; use function preg_match; use function preg_match_all; use function preg_replace; use function preg_replace_callback; use function preg_split; use function reset; use function round; use function sprintf; use function str_contains; use function str_replace; use function str_starts_with; use function strip_tags; use function strlen; use function strtoupper; use function substr; use function trim; use function uasort; use function xml_error_string; use function xml_get_current_line_number; use function xml_get_error_code; use function xml_parse; use function xml_parser_create; use function xml_parser_free; use function xml_parser_set_option; use function xml_set_character_data_handler; use function xml_set_element_handler; use const PREG_SET_ORDER; use const XML_OPTION_CASE_FOLDING; /** * Class ReportParserGenerate - parse a report.xml file and generate the report. */ class ReportParserGenerate extends ReportParserBase { /** @var bool Are we collecting data from elements */ private $process_footnote = true; /** @var bool Are we currently outputing data? */ private $print_data = false; /** @var bool[] Push-down stack of $print_data */ private $print_data_stack = []; /** @var int Are we processing GEDCOM data */ private $process_gedcoms = 0; /** @var int Are we processing conditionals */ private $process_ifs = 0; /** @var int Are we processing repeats */ private $process_repeats = 0; /** @var int Quantity of data to repeat during loops */ private $repeat_bytes = 0; /** @var string[] Repeated data when iterating over loops */ private $repeats = []; /** @var array[] Nested repeating data */ private $repeats_stack = []; /** @var AbstractRenderer[] Nested repeating data */ private $wt_report_stack = []; /** @var resource Nested repeating data */ private $parser; /** @var resource[] Nested repeating data */ private $parser_stack = []; /** @var string The current GEDCOM record */ private $gedrec = ''; /** @var string[] Nested GEDCOM records */ private $gedrec_stack = []; /** @var ReportBaseElement The currently processed element */ private $current_element; /** @var ReportBaseElement The currently processed element */ private $footnote_element; /** @var string The GEDCOM fact currently being processed */ private $fact = ''; /** @var string The GEDCOM value currently being processed */ private $desc = ''; /** @var string The GEDCOM type currently being processed */ private $type = ''; /** @var int The current generational level */ private $generation = 1; /** @var array Source data for processing lists */ private $list = []; /** @var int Number of items in lists */ private $list_total = 0; /** @var int Number of items filtered from lists */ private $list_private = 0; /** @var string The filename of the XML report */ protected $report; /** @var AbstractRenderer A factory for creating report elements */ private $report_root; /** @var AbstractRenderer Nested report elements */ private $wt_report; /** @var string[][] Variables defined in the report at run-time */ private $vars; /** @var Tree The current tree */ private $tree; /** @var FilesystemOperator */ private $data_filesystem; /** * Create a parser for a report * * @param string $report The XML filename * @param AbstractRenderer $report_root * @param string[][] $vars * @param Tree $tree * @param FilesystemOperator $data_filesystem */ public function __construct( string $report, AbstractRenderer $report_root, array $vars, Tree $tree, FilesystemOperator $data_filesystem ) { $this->report = $report; $this->report_root = $report_root; $this->wt_report = $report_root; $this->current_element = new ReportBaseElement(); $this->vars = $vars; $this->tree = $tree; $this->data_filesystem = $data_filesystem; parent::__construct($report); } /** * XML start element handler * This function is called whenever a starting element is reached * The element handler will be called if found, otherwise it must be HTML * * @param resource $parser the resource handler for the XML parser * @param string $name the name of the XML element parsed * @param string[] $attrs an array of key value pairs for the attributes * * @return void */ protected function startElement($parser, string $name, array $attrs): void { $newattrs = []; foreach ($attrs as $key => $value) { if (preg_match("/^\\$(\w+)$/", $value, $match)) { if (isset($this->vars[$match[1]]['id']) && !isset($this->vars[$match[1]]['gedcom'])) { $value = $this->vars[$match[1]]['id']; } } $newattrs[$key] = $value; } $attrs = $newattrs; if ($this->process_footnote && ($this->process_ifs === 0 || $name === 'if') && ($this->process_gedcoms === 0 || $name === 'Gedcom') && ($this->process_repeats === 0 || $name === 'Facts' || $name === 'RepeatTag')) { $method = $name . 'StartHandler'; if (method_exists($this, $method)) { call_user_func([$this, $method], $attrs); } } } /** * XML end element handler * This function is called whenever an ending element is reached * The element handler will be called if found, otherwise it must be HTML * * @param resource $parser the resource handler for the XML parser * @param string $name the name of the XML element parsed * * @return void */ protected function endElement($parser, string $name): void { if (($this->process_footnote || $name === 'Footnote') && ($this->process_ifs === 0 || $name === 'if') && ($this->process_gedcoms === 0 || $name === 'Gedcom') && ($this->process_repeats === 0 || $name === 'Facts' || $name === 'RepeatTag' || $name === 'List' || $name === 'Relatives')) { $method = $name . 'EndHandler'; if (method_exists($this, $method)) { call_user_func([$this, $method]); } } } /** * XML character data handler * * @param resource $parser the resource handler for the XML parser * @param string $data the name of the XML element parsed * * @return void */ protected function characterData($parser, string $data): void { if ($this->print_data && $this->process_gedcoms === 0 && $this->process_ifs === 0 && $this->process_repeats === 0) { $this->current_element->addText($data); } } /** * Handle