ReportParserGenerate   F
last analyzed

Complexity

Total Complexity 613

Size/Duplication

Total Lines 2856
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1630
dl 0
loc 2856
rs 0.8
c 0
b 0
f 0
wmc 613

61 Methods

Rating   Name   Duplication   Size   Complexity  
A inputEndHandler() 0 2 1
A inputStartHandler() 0 2 1
A headerStartHandler() 0 5 1
A pageNumStartHandler() 0 3 1
A footerStartHandler() 0 3 1
A descriptionEndHandler() 0 3 1
C repeatTagStartHandler() 0 65 15
A brStartHandler() 0 4 3
C gedcomStartHandler() 0 58 14
A htmlEndHandler() 0 13 3
A textEndHandler() 0 4 1
A htmlStartHandler() 0 11 2
C factsEndHandler() 0 76 13
B repeatTagEndHandler() 0 65 11
C addAncestors() 0 38 13
A ifEndHandler() 0 4 2
C endElement() 0 9 14
F relativesStartHandler() 0 135 34
B ifStartHandler() 0 50 11
F textBoxStartHandler() 0 118 30
D ageAtDeathStartHandler() 0 52 22
A newPageStartHandler() 0 4 1
F gedcomValueStartHandler() 0 63 14
F addDescendancy() 0 49 16
F setVarStartHandler() 0 68 20
A spStartHandler() 0 4 3
A footnoteTextsStartHandler() 0 4 1
A bodyStartHandler() 0 3 1
A styleStartHandler() 0 31 5
F getGedcomValue() 0 67 20
A pageHeaderEndHandler() 0 6 1
F listStartHandler() 0 375 91
A substituteVars() 0 19 3
A generationStartHandler() 0 3 1
B varStartHandler() 0 35 11
A cellEndHandler() 0 4 1
A reportEndHandler() 0 2 1
F cellStartHandler() 0 149 35
A footnoteEndHandler() 0 11 3
F highlightedImageStartHandler() 0 93 22
C relativesEndHandler() 0 69 13
A footnoteStartHandler() 0 21 5
F docStartHandler() 0 85 28
A nowStartHandler() 0 4 1
A gedcomEndHandler() 0 6 2
F imageStartHandler() 0 109 29
A totalPagesStartHandler() 0 3 1
A docEndHandler() 0 3 1
A titleEndHandler() 0 3 1
C listEndHandler() 0 71 13
A pageHeaderStartHandler() 0 6 1
C factsStartHandler() 0 41 12
C startElement() 0 20 15
F lineStartHandler() 0 49 17
A textBoxEndHandler() 0 6 1
C getPersonNameStartHandler() 0 75 17
A listTotalStartHandler() 0 6 2
A __construct() 0 7 1
A characterData() 0 4 5
A textStartHandler() 0 18 3
A reportStartHandler() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like ReportParserGenerate often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ReportParserGenerate, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2019 webtrees development team
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
 * GNU General Public License for more details.
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
 */
16
namespace Fisharebest\Webtrees\Report;
17
18
use Fisharebest\Webtrees\Auth;
19
use Fisharebest\Webtrees\Database;
20
use Fisharebest\Webtrees\Date;
21
use Fisharebest\Webtrees\Family;
22
use Fisharebest\Webtrees\Filter;
23
use Fisharebest\Webtrees\Functions\Functions;
24
use Fisharebest\Webtrees\Functions\FunctionsDate;
25
use Fisharebest\Webtrees\GedcomRecord;
26
use Fisharebest\Webtrees\GedcomTag;
27
use Fisharebest\Webtrees\I18N;
28
use Fisharebest\Webtrees\Individual;
29
use Fisharebest\Webtrees\Log;
30
use Fisharebest\Webtrees\Media;
31
use Fisharebest\Webtrees\Note;
32
use Fisharebest\Webtrees\Place;
33
34
/**
35
 * Class ReportParserGenerate - parse a report.xml file and generate the report.
36
 */
37
class ReportParserGenerate extends ReportParserBase
38
{
39
    /** @var bool Are we collecting data from <Footnote> elements  */
40
    private $process_footnote = true;
41
42
    /** @var bool Are we currently outputing data? */
43
    private $print_data = false;
44
45
    /** @var bool[] Push-down stack of $print_data */
46
    private $print_data_stack = array();
47
48
    /** @var int Are we processing GEDCOM data */
49
    private $process_gedcoms = 0;
50
51
    /** @var int Are we processing conditionals */
52
    private $process_ifs = 0;
53
54
    /** @var int Are we processing repeats*/
55
    private $process_repeats = 0;
56
57
    /** @var int Quantity of data to repeat during loops */
58
    private $repeat_bytes = 0;
59
60
    /** @var array[] Repeated data when iterating over loops */
61
    private $repeats = array();
62
63
    /** @var array[] Nested repeating data */
64
    private $repeats_stack = array();
65
66
    /** @var ReportBase[] Nested repeating data */
67
    private $wt_report_stack = array();
68
69
    /** @var resource Nested repeating data */
70
    private $parser;
71
72
    /** @var resource[] Nested repeating data */
73
    private $parser_stack = array();
74
75
    /** @var string The current GEDCOM record */
76
    private $gedrec = '';
77
78
    /** @var string[] Nested GEDCOM records */
79
    private $gedrec_stack = array();
80
81
    /** @var ReportBaseElement The currently processed element */
82
    private $current_element;
83
84
    /** @var ReportBaseElement The currently processed element */
85
    private $footnote_element;
86
87
    /** @var string The GEDCOM fact currently being processed */
88
    private $fact = '';
89
90
    /** @var string The GEDCOM value currently being processed */
91
    private $desc = '';
92
93
    /** @var string The GEDCOM type currently being processed */
94
    private $type = '';
95
96
    /** @var int The current generational level */
97
    private $generation = 1;
98
99
    /** @var array Source data for processing lists */
100
    private $list = array();
101
102
    /** @var int Number of items in lists */
103
    private $list_total = 0;
104
105
    /** @var int Number of items filtered from lists */
106
    private $list_private = 0;
107
108
    /** @var ReportBase A factory for creating report elements */
109
    private $report_root;
110
111
    /** @var ReportBase Nested report elements */
112
    private $wt_report;
113
114
    /** @todo This attribute is public to support the PHP5.3 closure workaround. */
115
    /** @var string[][] Variables defined in the report at run-time */
116
    public $vars;
117
118
    /**
119
     * Create a parser for a report
120
     *
121
     * @param string     $report     The XML filename
122
     * @param ReportBase $report_root
123
     * @param string[][] $vars
124
     */
125
    public function __construct($report, ReportBase $report_root = null, array $vars = array())
126
    {
127
        $this->report_root     = $report_root;
128
        $this->wt_report       = $report_root;
129
        $this->current_element = new ReportBaseElement;
130
        $this->vars            = $vars;
131
        parent::__construct($report);
132
    }
133
134
    /**
135
     * XML start element handler
136
     *
137
     * This function is called whenever a starting element is reached
138
     * The element handler will be called if found, otherwise it must be HTML
139
     *
140
     * @param resource $parser the resource handler for the XML parser
141
     * @param string   $name   the name of the XML element parsed
142
     * @param array    $attrs  an array of key value pairs for the attributes
143
     */
144
    protected function startElement($parser, $name, $attrs)
145
    {
146
        $newattrs = array();
147
148
        foreach ($attrs as $key => $value) {
149
            if (preg_match("/^\\$(\w+)$/", $value, $match)) {
150
                if ((isset($this->vars[$match[1]]['id'])) && (!isset($this->vars[$match[1]]['gedcom']))) {
151
                    $value = $this->vars[$match[1]]['id'];
152
                }
153
            }
154
            $newattrs[$key] = $value;
155
        }
156
        $attrs = $newattrs;
157
        if ($this->process_footnote && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag")) {
158
            $start_method = $name . 'StartHandler';
159
            $end_method   = $name . 'EndHandler';
160
            if (method_exists($this, $start_method)) {
161
                $this->$start_method($attrs);
162
            } elseif (!method_exists($this, $end_method)) {
163
                $this->htmlStartHandler($name, $attrs);
164
            }
165
        }
166
    }
167
168
    /**
169
     * XML end element handler
170
     *
171
     * This function is called whenever an ending element is reached
172
     * The element handler will be called if found, otherwise it must be HTML
173
     *
174
     * @param resource $parser the resource handler for the XML parser
175
     * @param string   $name   the name of the XML element parsed
176
     */
177
    protected function endElement($parser, $name)
178
    {
179
        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")) {
180
            $start_method = $name . 'StartHandler';
181
            $end_method   = $name . 'EndHandler';
182
            if (method_exists($this, $end_method)) {
183
                $this->$end_method();
184
            } elseif (!method_exists($this, $start_method)) {
185
                $this->htmlEndHandler($name);
186
            }
187
        }
188
    }
189
190
    /**
191
     * XML character data handler
192
     *
193
     * @param resource $parser the resource handler for the XML parser
194
     * @param string   $data   the name of the XML element parsed
195
     */
196
    protected function characterData($parser, $data)
197
    {
198
        if ($this->print_data && $this->process_gedcoms === 0 && $this->process_ifs === 0 && $this->process_repeats === 0) {
199
            $this->current_element->addText($data);
200
        }
201
    }
202
203
    /**
204
     * XML <style>
205
     *
206
     * @param array $attrs an array of key value pairs for the attributes
207
     */
208
    private function styleStartHandler($attrs)
209
    {
210
        if (empty($attrs['name'])) {
211
            throw new \DomainException('REPORT ERROR Style: The "name" of the style is missing or not set in the XML file.');
212
        }
213
214
        // array Style that will be passed on
215
        $s = array();
216
217
        // string Name af the style
218
        $s['name'] = $attrs['name'];
219
220
        // string Name of the DEFAULT font
221
        $s['font'] = $this->wt_report->defaultFont;
222
        if (!empty($attrs['font'])) {
223
            $s['font'] = $attrs['font'];
224
        }
225
226
        // int The size of the font in points
227
        $s['size'] = $this->wt_report->defaultFontSize;
228
        if (!empty($attrs['size'])) {
229
            $s['size'] = (int) $attrs['size'];
230
        } // Get it as int to ignore all decimal points or text (if any text then int(0))
231
232
        // string B: bold, I: italic, U: underline, D: line trough, The default value is regular.
233
        $s['style'] = "";
234
        if (!empty($attrs['style'])) {
235
            $s['style'] = $attrs['style'];
236
        }
237
238
        $this->wt_report->addStyle($s);
239
    }
240
241
    /**
242
     * XML <Doc>
243
     *
244
     * Sets up the basics of the document proparties
245
     *
246
     * @param array $attrs an array of key value pairs for the attributes
247
     */
248
    private function docStartHandler($attrs)
249
    {
250
        $this->parser = $this->xml_parser;
251
252
        // Custom page width
253
        if (!empty($attrs['customwidth'])) {
254
            $this->wt_report->pagew = (int) $attrs['customwidth'];
255
        } // Get it as int to ignore all decimal points or text (if any text then int(0))
256
        // Custom Page height
257
        if (!empty($attrs['customheight'])) {
258
            $this->wt_report->pageh = (int) $attrs['customheight'];
259
        } // Get it as int to ignore all decimal points or text (if any text then int(0))
260
261
        // Left Margin
262
        if (isset($attrs['leftmargin'])) {
263
            if ($attrs['leftmargin'] === "0") {
264
                $this->wt_report->leftmargin = 0;
265
            } elseif (!empty($attrs['leftmargin'])) {
266
                $this->wt_report->leftmargin = (int) $attrs['leftmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
267
            }
268
        }
269
        // Right Margin
270
        if (isset($attrs['rightmargin'])) {
271
            if ($attrs['rightmargin'] === "0") {
272
                $this->wt_report->rightmargin = 0;
273
            } elseif (!empty($attrs['rightmargin'])) {
274
                $this->wt_report->rightmargin = (int) $attrs['rightmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
275
            }
276
        }
277
        // Top Margin
278
        if (isset($attrs['topmargin'])) {
279
            if ($attrs['topmargin'] === "0") {
280
                $this->wt_report->topmargin = 0;
281
            } elseif (!empty($attrs['topmargin'])) {
282
                $this->wt_report->topmargin = (int) $attrs['topmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
283
            }
284
        }
285
        // Bottom Margin
286
        if (isset($attrs['bottommargin'])) {
287
            if ($attrs['bottommargin'] === "0") {
288
                $this->wt_report->bottommargin = 0;
289
            } elseif (!empty($attrs['bottommargin'])) {
290
                $this->wt_report->bottommargin = (int) $attrs['bottommargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
291
            }
292
        }
293
        // Header Margin
294
        if (isset($attrs['headermargin'])) {
295
            if ($attrs['headermargin'] === "0") {
296
                $this->wt_report->headermargin = 0;
297
            } elseif (!empty($attrs['headermargin'])) {
298
                $this->wt_report->headermargin = (int) $attrs['headermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
299
            }
300
        }
301
        // Footer Margin
302
        if (isset($attrs['footermargin'])) {
303
            if ($attrs['footermargin'] === "0") {
304
                $this->wt_report->footermargin = 0;
305
            } elseif (!empty($attrs['footermargin'])) {
306
                $this->wt_report->footermargin = (int) $attrs['footermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
307
            }
308
        }
309
310
        // Page Orientation
311
        if (!empty($attrs['orientation'])) {
312
            if ($attrs['orientation'] == "landscape") {
313
                $this->wt_report->orientation = "landscape";
314
            } elseif ($attrs['orientation'] == "portrait") {
315
                $this->wt_report->orientation = "portrait";
316
            }
317
        }
318
        // Page Size
319
        if (!empty($attrs['pageSize'])) {
320
            $this->wt_report->pageFormat = strtoupper($attrs['pageSize']);
321
        }
322
323
        // Show Generated By...
324
        if (isset($attrs['showGeneratedBy'])) {
325
            if ($attrs['showGeneratedBy'] === "0") {
326
                $this->wt_report->showGenText = false;
327
            } elseif ($attrs['showGeneratedBy'] === "1") {
328
                $this->wt_report->showGenText = true;
329
            }
330
        }
331
332
        $this->wt_report->setup();
333
    }
334
335
    /**
336
     * XML </Doc>
337
     */
338
    private function docEndHandler()
339
    {
340
        $this->wt_report->run();
0 ignored issues
show
introduced by
The method run() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

340
        $this->wt_report->/** @scrutinizer ignore-call */ 
341
                          run();
Loading history...
341
    }
342
343
    /**
344
     * XML <Header>
345
     */
346
    private function headerStartHandler()
347
    {
348
        // Clear the Header before any new elements are added
349
        $this->wt_report->clearHeader();
0 ignored issues
show
introduced by
The method clearHeader() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

349
        $this->wt_report->/** @scrutinizer ignore-call */ 
350
                          clearHeader();
Loading history...
350
        $this->wt_report->setProcessing("H");
351
    }
352
353
    /**
354
     * XML <PageHeader>
355
     */
356
    private function pageHeaderStartHandler()
357
    {
358
        array_push($this->print_data_stack, $this->print_data);
359
        $this->print_data = false;
360
        array_push($this->wt_report_stack, $this->wt_report);
361
        $this->wt_report = $this->report_root->createPageHeader();
0 ignored issues
show
introduced by
The method createPageHeader() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

361
        /** @scrutinizer ignore-call */ 
362
        $this->wt_report = $this->report_root->createPageHeader();
Loading history...
362
    }
363
364
    /**
365
     * XML <pageHeaderEndHandler>
366
     */
367
    private function pageHeaderEndHandler()
368
    {
369
        $this->print_data        = array_pop($this->print_data_stack);
370
        $this->current_element   = $this->wt_report;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->wt_report of type Fisharebest\Webtrees\Report\ReportBase is incompatible with the declared type Fisharebest\Webtrees\Report\ReportBaseElement of property $current_element.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
371
        $this->wt_report         = array_pop($this->wt_report_stack);
372
        $this->wt_report->addElement($this->current_element);
373
    }
374
375
    /**
376
     * XML <bodyStartHandler>
377
     */
378
    private function bodyStartHandler()
379
    {
380
        $this->wt_report->setProcessing("B");
381
    }
382
383
    /**
384
     * XML <footerStartHandler>
385
     */
386
    private function footerStartHandler()
387
    {
388
        $this->wt_report->setProcessing("F");
389
    }
390
391
    /**
392
     * XML <Cell>
393
     *
394
     * @param array $attrs an array of key value pairs for the attributes
395
     */
396
    private function cellStartHandler($attrs)
397
    {
398
        // string The text alignment of the text in this box.
399
        $align = "";
400
        if (!empty($attrs['align'])) {
401
            $align = $attrs['align'];
402
            // RTL supported left/right alignment
403
            if ($align == "rightrtl") {
404
                if ($this->wt_report->rtl) {
405
                    $align = "left";
406
                } else {
407
                    $align = "right";
408
                }
409
            } elseif ($align == "leftrtl") {
410
                if ($this->wt_report->rtl) {
411
                    $align = "right";
412
                } else {
413
                    $align = "left";
414
                }
415
            }
416
        }
417
418
        // string The color to fill the background of this cell
419
        $bgcolor = "";
420
        if (!empty($attrs['bgcolor'])) {
421
            $bgcolor = $attrs['bgcolor'];
422
        }
423
424
        // int Whether or not the background should be painted
425
        $fill = 1;
426
        if (isset($attrs['fill'])) {
427
            if ($attrs['fill'] === "0") {
428
                $fill = 0;
429
            } elseif ($attrs['fill'] === "1") {
430
                $fill = 1;
431
            }
432
        }
433
434
        $reseth = true;
435
        // boolean   if true reset the last cell height (default true)
436
        if (isset($attrs['reseth'])) {
437
            if ($attrs['reseth'] === "0") {
438
                $reseth = false;
439
            } elseif ($attrs['reseth'] === "1") {
440
                $reseth = true;
441
            }
442
        }
443
444
        // mixed Whether or not a border should be printed around this box
445
        $border = 0;
446
        if (!empty($attrs['border'])) {
447
            $border = $attrs['border'];
448
        }
449
        // string Border color in HTML code
450
        $bocolor = "";
451
        if (!empty($attrs['bocolor'])) {
452
            $bocolor = $attrs['bocolor'];
453
        }
454
455
        // int Cell height (expressed in points) The starting height of this cell. If the text wraps the height will automatically be adjusted.
456
        $height = 0;
457
        if (!empty($attrs['height'])) {
458
            $height = (int) $attrs['height'];
459
        }
460
        // int Cell width (expressed in points) Setting the width to 0 will make it the width from the current location to the right margin.
461
        $width = 0;
462
        if (!empty($attrs['width'])) {
463
            $width = (int) $attrs['width'];
464
        }
465
466
        // int Stretch carachter mode
467
        $stretch = 0;
468
        if (!empty($attrs['stretch'])) {
469
            $stretch = (int) $attrs['stretch'];
470
        }
471
472
        // mixed Position the left corner of this box on the page. The default is the current position.
473
        $left = ".";
474
        if (isset($attrs['left'])) {
475
            if ($attrs['left'] === ".") {
476
                $left = ".";
477
            } elseif (!empty($attrs['left'])) {
478
                $left = (int) $attrs['left'];
479
            } elseif ($attrs['left'] === "0") {
480
                $left = 0;
481
            }
482
        }
483
        // mixed Position the top corner of this box on the page. the default is the current position
484
        $top = ".";
485
        if (isset($attrs['top'])) {
486
            if ($attrs['top'] === ".") {
487
                $top = ".";
488
            } elseif (!empty($attrs['top'])) {
489
                $top = (int) $attrs['top'];
490
            } elseif ($attrs['top'] === "0") {
491
                $top = 0;
492
            }
493
        }
494
495
        // string The name of the Style that should be used to render the text.
496
        $style = "";
497
        if (!empty($attrs['style'])) {
498
            $style = $attrs['style'];
499
        }
500
501
        // string Text color in html code
502
        $tcolor = "";
503
        if (!empty($attrs['tcolor'])) {
504
            $tcolor = $attrs['tcolor'];
505
        }
506
507
        // int Indicates where the current position should go after the call.
508
        $ln = 0;
509
        if (isset($attrs['newline'])) {
510
            if (!empty($attrs['newline'])) {
511
                $ln = (int) $attrs['newline'];
512
            } elseif ($attrs['newline'] === "0") {
513
                $ln = 0;
514
            }
515
        }
516
517
        if ($align == "left") {
518
            $align = "L";
519
        } elseif ($align == "right") {
520
            $align = "R";
521
        } elseif ($align == "center") {
522
            $align = "C";
523
        } elseif ($align == "justify") {
524
            $align = "J";
525
        }
526
527
        array_push($this->print_data_stack, $this->print_data);
528
        $this->print_data = true;
529
530
        $this->current_element = $this->report_root->createCell(
0 ignored issues
show
introduced by
The method createCell() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

530
        /** @scrutinizer ignore-call */ 
531
        $this->current_element = $this->report_root->createCell(
Loading history...
531
            $width,
532
            $height,
533
            $border,
534
            $align,
535
            $bgcolor,
536
            $style,
537
            $ln,
538
            $top,
539
            $left,
540
            $fill,
541
            $stretch,
542
            $bocolor,
543
            $tcolor,
544
            $reseth
545
        );
546
    }
547
548
    /**
549
     * XML </Cell>
550
     */
551
    private function cellEndHandler()
552
    {
553
        $this->print_data = array_pop($this->print_data_stack);
554
        $this->wt_report->addElement($this->current_element);
0 ignored issues
show
introduced by
The method addElement() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

554
        $this->wt_report->/** @scrutinizer ignore-call */ 
555
                          addElement($this->current_element);
Loading history...
555
    }
556
557
    /**
558
     * XML <Now /> element handler
559
     */
560
    private function nowStartHandler()
561
    {
562
        $g = FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
563
        $this->current_element->addText($g->display());
564
    }
565
566
    /**
567
     * XML <PageNum /> element handler
568
     */
569
    private function pageNumStartHandler()
570
    {
571
        $this->current_element->addText("#PAGENUM#");
572
    }
573
574
    /**
575
     * XML <TotalPages /> element handler
576
     */
577
    private function totalPagesStartHandler()
578
    {
579
        $this->current_element->addText("{{:ptp:}}");
580
    }
581
582
    /**
583
     * Called at the start of an element.
584
     *
585
     * @param array $attrs an array of key value pairs for the attributes
586
     */
587
    private function gedcomStartHandler($attrs)
588
    {
589
        global $WT_TREE;
590
591
        if ($this->process_gedcoms > 0) {
592
            $this->process_gedcoms++;
593
594
            return;
595
        }
596
597
        $tag       = $attrs['id'];
598
        $tag       = str_replace("@fact", $this->fact, $tag);
599
        $tags      = explode(":", $tag);
600
        $newgedrec = '';
601
        if (count($tags) < 2) {
602
            $tmp       = GedcomRecord::getInstance($attrs['id'], $WT_TREE);
603
            $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
604
        }
605
        if (empty($newgedrec)) {
606
            $tgedrec   = $this->gedrec;
607
            $newgedrec = '';
608
            foreach ($tags as $tag) {
609
                if (preg_match("/\\$(.+)/", $tag, $match)) {
610
                    if (isset($this->vars[$match[1]]['gedcom'])) {
611
                        $newgedrec = $this->vars[$match[1]]['gedcom'];
612
                    } else {
613
                        $tmp       = GedcomRecord::getInstance($match[1], $WT_TREE);
614
                        $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
615
                    }
616
                } else {
617
                    if (preg_match("/@(.+)/", $tag, $match)) {
618
                        $gmatch = array();
619
                        if (preg_match("/\d $match[1] @([^@]+)@/", $tgedrec, $gmatch)) {
620
                            $tmp       = GedcomRecord::getInstance($gmatch[1], $WT_TREE);
621
                            $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
622
                            $tgedrec   = $newgedrec;
623
                        } else {
624
                            $newgedrec = '';
625
                            break;
626
                        }
627
                    } else {
628
                        $temp      = explode(" ", trim($tgedrec));
629
                        $level     = $temp[0] + 1;
630
                        $newgedrec = Functions::getSubRecord($level, "$level $tag", $tgedrec);
631
                        $tgedrec   = $newgedrec;
632
                    }
633
                }
634
            }
635
        }
636
        if (!empty($newgedrec)) {
637
            array_push($this->gedrec_stack, array($this->gedrec, $this->fact, $this->desc));
638
            $this->gedrec = $newgedrec;
639
            if (preg_match("/(\d+) (_?[A-Z0-9]+) (.*)/", $this->gedrec, $match)) {
640
                $this->fact = $match[2];
641
                $this->desc = trim($match[3]);
642
            }
643
        } else {
644
            $this->process_gedcoms++;
645
        }
646
    }
647
648
    /**
649
     * Called at the end of an element.
650
     */
651
    private function gedcomEndHandler()
652
    {
653
        if ($this->process_gedcoms > 0) {
654
            $this->process_gedcoms--;
655
        } else {
656
            list($this->gedrec, $this->fact, $this->desc) = array_pop($this->gedrec_stack);
657
        }
658
    }
659
660
    /**
661
     * XML <textBoxStartHandler>
662
     *
663
     * @param array $attrs an array of key value pairs for the attributes
664
     */
665
    private function textBoxStartHandler($attrs)
666
    {
667
        // string Background color code
668
        $bgcolor = "";
669
        if (!empty($attrs['bgcolor'])) {
670
            $bgcolor = $attrs['bgcolor'];
671
        }
672
673
        // boolean Wether or not fill the background color
674
        $fill = true;
675
        if (isset($attrs['fill'])) {
676
            if ($attrs['fill'] === "0") {
677
                $fill = false;
678
            } elseif ($attrs['fill'] === "1") {
679
                $fill = true;
680
            }
681
        }
682
683
        // var boolean Whether or not a border should be printed around this box. 0 = no border, 1 = border. Default is 0
684
        $border = false;
685
        if (isset($attrs['border'])) {
686
            if ($attrs['border'] === "1") {
687
                $border = true;
688
            } elseif ($attrs['border'] === "0") {
689
                $border = false;
690
            }
691
        }
692
693
        // int The starting height of this cell. If the text wraps the height will automatically be adjusted
694
        $height = 0;
695
        if (!empty($attrs['height'])) {
696
            $height = (int) $attrs['height'];
697
        }
698
        // int Setting the width to 0 will make it the width from the current location to the margin
699
        $width = 0;
700
        if (!empty($attrs['width'])) {
701
            $width = (int) $attrs['width'];
702
        }
703
704
        // mixed Position the left corner of this box on the page. The default is the current position.
705
        $left = ".";
706
        if (isset($attrs['left'])) {
707
            if ($attrs['left'] === ".") {
708
                $left = ".";
709
            } elseif (!empty($attrs['left'])) {
710
                $left = (int) $attrs['left'];
711
            } elseif ($attrs['left'] === "0") {
712
                $left = 0;
713
            }
714
        }
715
        // mixed Position the top corner of this box on the page. the default is the current position
716
        $top = ".";
717
        if (isset($attrs['top'])) {
718
            if ($attrs['top'] === ".") {
719
                $top = ".";
720
            } elseif (!empty($attrs['top'])) {
721
                $top = (int) $attrs['top'];
722
            } elseif ($attrs['top'] === "0") {
723
                $top = 0;
724
            }
725
        }
726
        // boolean After this box is finished rendering, should the next section of text start immediately after the this box or should it start on a new line under this box. 0 = no new line, 1 = force new line. Default is 0
727
        $newline = false;
728
        if (isset($attrs['newline'])) {
729
            if ($attrs['newline'] === "1") {
730
                $newline = true;
731
            } elseif ($attrs['newline'] === "0") {
732
                $newline = false;
733
            }
734
        }
735
        // boolean
736
        $pagecheck = true;
737
        if (isset($attrs['pagecheck'])) {
738
            if ($attrs['pagecheck'] === "0") {
739
                $pagecheck = false;
740
            } elseif ($attrs['pagecheck'] === "1") {
741
                $pagecheck = true;
742
            }
743
        }
744
        // boolean Cell padding
745
        $padding = true;
746
        if (isset($attrs['padding'])) {
747
            if ($attrs['padding'] === "0") {
748
                $padding = false;
749
            } elseif ($attrs['padding'] === "1") {
750
                $padding = true;
751
            }
752
        }
753
        // boolean Reset this box Height
754
        $reseth = false;
755
        if (isset($attrs['reseth'])) {
756
            if ($attrs['reseth'] === "1") {
757
                $reseth = true;
758
            } elseif ($attrs['reseth'] === "0") {
759
                $reseth = false;
760
            }
761
        }
762
763
        // string Style of rendering
764
        $style = "";
765
766
        array_push($this->print_data_stack, $this->print_data);
767
        $this->print_data = false;
768
769
        array_push($this->wt_report_stack, $this->wt_report);
770
        $this->wt_report = $this->report_root->createTextBox(
0 ignored issues
show
introduced by
The method createTextBox() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

770
        /** @scrutinizer ignore-call */ 
771
        $this->wt_report = $this->report_root->createTextBox(
Loading history...
771
            $width,
772
            $height,
773
            $border,
774
            $bgcolor,
775
            $newline,
776
            $left,
777
            $top,
778
            $pagecheck,
779
            $style,
780
            $fill,
781
            $padding,
782
            $reseth
783
        );
784
    }
785
786
    /**
787
     * XML <textBoxEndHandler>
788
     */
789
    private function textBoxEndHandler()
790
    {
791
        $this->print_data      = array_pop($this->print_data_stack);
792
        $this->current_element = $this->wt_report;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->wt_report of type Fisharebest\Webtrees\Report\ReportBase is incompatible with the declared type Fisharebest\Webtrees\Report\ReportBaseElement of property $current_element.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
793
        $this->wt_report       = array_pop($this->wt_report_stack);
794
        $this->wt_report->addElement($this->current_element);
795
    }
796
797
    /**
798
     * XLM <Text>.
799
     *
800
     * @param array $attrs an array of key value pairs for the attributes
801
     */
802
    private function textStartHandler($attrs)
803
    {
804
        array_push($this->print_data_stack, $this->print_data);
805
        $this->print_data = true;
806
807
        // string The name of the Style that should be used to render the text.
808
        $style = "";
809
        if (!empty($attrs['style'])) {
810
            $style = $attrs['style'];
811
        }
812
813
        // string  The color of the text - Keep the black color as default
814
        $color = "";
815
        if (!empty($attrs['color'])) {
816
            $color = $attrs['color'];
817
        }
818
819
        $this->current_element = $this->report_root->createText($style, $color);
0 ignored issues
show
introduced by
The method createText() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

819
        /** @scrutinizer ignore-call */ 
820
        $this->current_element = $this->report_root->createText($style, $color);
Loading history...
820
    }
821
822
    /**
823
     * XML </Text>
824
     */
825
    private function textEndHandler()
826
    {
827
        $this->print_data = array_pop($this->print_data_stack);
828
        $this->wt_report->addElement($this->current_element);
829
    }
830
831
    /**
832
     * XML <GetPersonName/>
833
     *
834
     * Get the name
835
     * 1. id is empty - current GEDCOM record
836
     * 2. id is set with a record id
837
     *
838
     * @param array $attrs an array of key value pairs for the attributes
839
     */
840
    private function getPersonNameStartHandler($attrs)
841
    {
842
        global $WT_TREE;
843
844
        $id    = "";
845
        $match = array();
846
        if (empty($attrs['id'])) {
847
            if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
848
                $id = $match[1];
849
            }
850
        } else {
851
            if (preg_match("/\\$(.+)/", $attrs['id'], $match)) {
852
                if (isset($this->vars[$match[1]]['id'])) {
853
                    $id = $this->vars[$match[1]]['id'];
854
                }
855
            } else {
856
                if (preg_match("/@(.+)/", $attrs['id'], $match)) {
857
                    $gmatch = array();
858
                    if (preg_match("/\d $match[1] @([^@]+)@/", $this->gedrec, $gmatch)) {
859
                        $id = $gmatch[1];
860
                    }
861
                } else {
862
                    $id = $attrs['id'];
863
                }
864
            }
865
        }
866
        if (!empty($id)) {
867
            $record = GedcomRecord::getInstance($id, $WT_TREE);
868
            if ($record === null) {
869
                return;
870
            }
871
            if (!$record->canShowName()) {
872
                $this->current_element->addText(I18N::translate('Private'));
873
            } else {
874
                $name = $record->getFullName();
875
                $name = preg_replace(
876
                    array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
877
                    array('«', '', '»'),
878
                    $name
879
                );
880
                $name = strip_tags($name);
881
                if (!empty($attrs['truncate'])) {
882
                    if (mb_strlen($name) > $attrs['truncate']) {
883
                        $name  = preg_replace("/\(.*\) ?/", '', $name); //removes () and text inbetween - what about ", [ and { etc?
884
                        $words = preg_split('/[, -]+/', $name); // names separated with space, comma or hyphen - any others?
885
                        $name  = $words[count($words) - 1];
886
                        for ($i = count($words) - 2; $i >= 0; $i--) {
887
                            $len = mb_strlen($name);
888
                            for ($j = count($words) - 3; $j >= 0; $j--) {
889
                                $len += mb_strlen($words[$j]);
890
                            }
891
                            if ($len > $attrs['truncate']) {
892
                                $first_letter = mb_substr($words[$i], 0, 1);
893
                                // Do not show " of nick-names
894
                                if ($first_letter != "\"") {
895
                                    $name = mb_substr($words[$i], 0, 1) . '. ' . $name;
896
                                }
897
                            } else {
898
                                $name = $words[$i] . ' ' . $name;
899
                            }
900
                        }
901
                    }
902
                } else {
903
                    $addname = $record->getAddName();
904
                    $addname = preg_replace(
905
                        array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
906
                        array('«', '', '»'),
907
                        $addname
908
                    );
909
                    $addname = strip_tags($addname);
910
                    if (!empty($addname)) {
911
                        $name .= " " . $addname;
912
                    }
913
                }
914
                $this->current_element->addText(trim($name));
915
            }
916
        }
917
    }
918
919
    /**
920
     * XML <GedcomValue/>
921
     *
922
     * @param array $attrs an array of key value pairs for the attributes
923
     */
924
    private function gedcomValueStartHandler($attrs)
925
    {
926
        global $WT_TREE;
927
928
        $id    = "";
929
        $match = array();
930
        if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
931
            $id = $match[1];
932
        }
933
934
        if (isset($attrs['newline']) && $attrs['newline'] == "1") {
935
            $useBreak = "1";
936
        } else {
937
            $useBreak = "0";
938
        }
939
940
        $tag = $attrs['tag'];
941
        if (!empty($tag)) {
942
            if ($tag == "@desc") {
943
                $value = $this->desc;
944
                $value = trim($value);
945
                $this->current_element->addText($value);
946
            }
947
            if ($tag == "@id") {
948
                $this->current_element->addText($id);
949
            } else {
950
                $tag = str_replace("@fact", $this->fact, $tag);
951
                if (empty($attrs['level'])) {
952
                    $temp  = explode(" ", trim($this->gedrec));
953
                    $level = $temp[0];
954
                    if ($level == 0) {
955
                        $level++;
956
                    }
957
                } else {
958
                    $level = $attrs['level'];
959
                }
960
                $tags  = preg_split('/[: ]/', $tag);
961
                $value = $this->getGedcomValue($tag, $level, $this->gedrec);
962
                switch (end($tags)) {
963
                    case 'DATE':
964
                        $tmp   = new Date($value);
965
                        $value = $tmp->display();
966
                        break;
967
                    case 'PLAC':
968
                        $tmp   = new Place($value, $WT_TREE);
969
                        $value = $tmp->getShortName();
970
                        break;
971
                }
972
                if ($useBreak == "1") {
973
                    // Insert <br> when multiple dates exist.
974
                    // This works around a TCPDF bug that incorrectly wraps RTL dates on LTR pages
975
                    $value = str_replace('(', '<br>(', $value);
976
                    $value = str_replace('<span dir="ltr"><br>', '<br><span dir="ltr">', $value);
977
                    $value = str_replace('<span dir="rtl"><br>', '<br><span dir="rtl">', $value);
978
                    if (substr($value, 0, 6) == '<br>') {
979
                        $value = substr($value, 6);
980
                    }
981
                }
982
                $tmp = explode(':', $tag);
983
                if (in_array(end($tmp), array('NOTE', 'TEXT'))) {
984
                    $value = Filter::formatText($value, $WT_TREE); // We'll strip HTML in addText()
985
                }
986
                $this->current_element->addText($value);
987
            }
988
        }
989
    }
990
991
    /**
992
     * XML <RepeatTag>
993
     *
994
     * @param array $attrs an array of key value pairs for the attributes
995
     */
996
    private function repeatTagStartHandler($attrs)
997
    {
998
        global $WT_TREE;
999
1000
        $this->process_repeats++;
1001
        if ($this->process_repeats > 1) {
1002
            return;
1003
        }
1004
1005
        array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
1006
        $this->repeats      = array();
1007
        $this->repeat_bytes = xml_get_current_line_number($this->parser);
1008
1009
        $tag = "";
1010
        if (isset($attrs['tag'])) {
1011
            $tag = $attrs['tag'];
1012
        }
1013
        if (!empty($tag)) {
1014
            if ($tag == "@desc") {
1015
                $value = $this->desc;
1016
                $value = trim($value);
1017
                $this->current_element->addText($value);
1018
            } else {
1019
                $tag   = str_replace("@fact", $this->fact, $tag);
1020
                $tags  = explode(":", $tag);
1021
                $temp  = explode(" ", trim($this->gedrec));
1022
                $level = $temp[0];
1023
                if ($level == 0) {
1024
                    $level++;
1025
                }
1026
                $subrec = $this->gedrec;
1027
                $t      = $tag;
1028
                $count  = count($tags);
1029
                $i      = 0;
1030
                while ($i < $count) {
1031
                    $t = $tags[$i];
1032
                    if (!empty($t)) {
1033
                        if ($i < ($count - 1)) {
1034
                            $subrec = Functions::getSubRecord($level, "$level $t", $subrec);
0 ignored issues
show
Bug introduced by
$level of type string is incompatible with the type integer expected by parameter $level of Fisharebest\Webtrees\Fun...nctions::getSubRecord(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1034
                            $subrec = Functions::getSubRecord(/** @scrutinizer ignore-type */ $level, "$level $t", $subrec);
Loading history...
1035
                            if (empty($subrec)) {
1036
                                $level--;
1037
                                $subrec = Functions::getSubRecord($level, "@ $t", $this->gedrec);
1038
                                if (empty($subrec)) {
1039
                                    return;
1040
                                }
1041
                            }
1042
                        }
1043
                        $level++;
1044
                    }
1045
                    $i++;
1046
                }
1047
                $level--;
1048
                $count = preg_match_all("/$level $t(.*)/", $subrec, $match, PREG_SET_ORDER);
1049
                $i     = 0;
1050
                while ($i < $count) {
1051
                    $i++;
1052
                    // Privacy check - is this a link, and are we allowed to view the linked object?
1053
                    $subrecord = Functions::getSubRecord($level, "$level $t", $subrec, $i);
1054
                    if (preg_match('/^\d ' . WT_REGEX_TAG . ' @(' . WT_REGEX_XREF . ')@/', $subrecord, $xref_match)) {
1055
                        $linked_object = GedcomRecord::getInstance($xref_match[1], $WT_TREE);
1056
                        if ($linked_object && !$linked_object->canShow()) {
1057
                            continue;
1058
                        }
1059
                    }
1060
                    $this->repeats[] = $subrecord;
1061
                }
1062
            }
1063
        }
1064
    }
1065
1066
    /**
1067
     * XML </ RepeatTag>
1068
     */
1069
    private function repeatTagEndHandler()
1070
    {
1071
        global $report;
1072
1073
        $this->process_repeats--;
1074
        if ($this->process_repeats > 0) {
1075
            return;
1076
        }
1077
1078
        // Check if there is anything to repeat
1079
        if (count($this->repeats) > 0) {
1080
            // No need to load them if not used...
1081
1082
            $lineoffset = 0;
1083
            foreach ($this->repeats_stack as $rep) {
1084
                $lineoffset += $rep[1];
1085
            }
1086
            //-- read the xml from the file
1087
            $lines = file($report);
1088
            while (strpos($lines[$lineoffset + $this->repeat_bytes], "<RepeatTag") === false) {
1089
                $lineoffset--;
1090
            }
1091
            $lineoffset++;
1092
            $reportxml = "<tempdoc>\n";
1093
            $line_nr   = $lineoffset + $this->repeat_bytes;
1094
            // RepeatTag Level counter
1095
            $count = 1;
1096
            while (0 < $count) {
1097
                if (strstr($lines[$line_nr], "<RepeatTag") !== false) {
1098
                    $count++;
1099
                } elseif (strstr($lines[$line_nr], "</RepeatTag") !== false) {
1100
                    $count--;
1101
                }
1102
                if (0 < $count) {
1103
                    $reportxml .= $lines[$line_nr];
1104
                }
1105
                $line_nr++;
1106
            }
1107
            // No need to drag this
1108
            unset($lines);
1109
            $reportxml .= "</tempdoc>\n";
1110
            // Save original values
1111
            array_push($this->parser_stack, $this->parser);
1112
            $oldgedrec = $this->gedrec;
1113
            foreach ($this->repeats as $gedrec) {
1114
                $this->gedrec  = $gedrec;
0 ignored issues
show
Documentation Bug introduced by
It seems like $gedrec of type array is incompatible with the declared type string of property $gedrec.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1115
                $repeat_parser = xml_parser_create();
1116
                $this->parser  = $repeat_parser;
0 ignored issues
show
Documentation Bug introduced by
It seems like $repeat_parser can also be of type XmlParser. However, the property $parser is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1117
                xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1118
                xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1119
                xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1120
                if (!xml_parse($repeat_parser, $reportxml, true)) {
1121
                    throw new \DomainException(sprintf(
1122
                        'RepeatTagEHandler XML error: %s at line %d',
1123
                        xml_error_string(xml_get_error_code($repeat_parser)),
1124
                        xml_get_current_line_number($repeat_parser)
1125
                    ));
1126
                }
1127
                xml_parser_free($repeat_parser);
1128
            }
1129
            // Restore original values
1130
            $this->gedrec = $oldgedrec;
1131
            $this->parser = array_pop($this->parser_stack);
1132
        }
1133
        list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1134
    }
1135
1136
    /**
1137
     * Variable lookup
1138
     *
1139
     * Retrieve predefined variables :
1140
     *
1141
     * @ desc GEDCOM fact description, example:
1142
     *        1 EVEN This is a description
1143
     * @ fact GEDCOM fact tag, such as BIRT, DEAT etc.
1144
     * $ I18N::translate('....')
1145
     * $ language_settings[]
1146
     *
1147
     * @param array $attrs an array of key value pairs for the attributes
1148
     */
1149
    private function varStartHandler($attrs)
1150
    {
1151
        if (empty($attrs['var'])) {
1152
            throw new \DomainException('REPORT ERROR var: The attribute "var=" is missing or not set in the XML file on line: ' . xml_get_current_line_number($this->parser));
1153
        }
1154
1155
        $var = $attrs['var'];
1156
        // SetVar element preset variables
1157
        if (!empty($this->vars[$var]['id'])) {
1158
            $var = $this->vars[$var]['id'];
1159
        } else {
1160
            $tfact = $this->fact;
1161
            if (($this->fact === "EVEN" || $this->fact === "FACT") && $this->type !== " ") {
1162
                // Use :
1163
                // n TYPE This text if string
1164
                $tfact = $this->type;
1165
            }
1166
            $var = str_replace(array("@fact", "@desc"), array(GedcomTag::getLabel($tfact), $this->desc), $var);
1167
            if (preg_match('/^I18N::number\((.+)\)$/', $var, $match)) {
1168
                $var = I18N::number($match[1]);
1169
            } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $var, $match)) {
1170
                $var = I18N::translate($match[1]);
1171
            } elseif (preg_match('/^I18N::translateContext\(\'(.+)\', *\'(.+)\'\)$/', $var, $match)) {
1172
                $var = I18N::translateContext($match[1], $match[2]);
1173
            }
1174
        }
1175
        // Check if variable is set as a date and reformat the date
1176
        if (isset($attrs['date'])) {
1177
            if ($attrs['date'] === "1") {
1178
                $g   = new Date($var);
1179
                $var = $g->display();
1180
            }
1181
        }
1182
        $this->current_element->addText($var);
1183
        $this->text = $var; // Used for title/descriptio
1184
    }
1185
1186
    /**
1187
     * XML <Facts>
1188
     *
1189
     * @param array $attrs an array of key value pairs for the attributes
1190
     */
1191
    private function factsStartHandler($attrs)
1192
    {
1193
        global $WT_TREE;
1194
1195
        $this->process_repeats++;
1196
        if ($this->process_repeats > 1) {
1197
            return;
1198
        }
1199
1200
        array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
1201
        $this->repeats      = array();
1202
        $this->repeat_bytes = xml_get_current_line_number($this->parser);
1203
1204
        $id    = "";
1205
        $match = array();
1206
        if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1207
            $id = $match[1];
1208
        }
1209
        $tag = "";
1210
        if (isset($attrs['ignore'])) {
1211
            $tag .= $attrs['ignore'];
1212
        }
1213
        if (preg_match("/\\$(.+)/", $tag, $match)) {
1214
            $tag = $this->vars[$match[1]]['id'];
1215
        }
1216
1217
        $record = GedcomRecord::getInstance($id, $WT_TREE);
1218
        if (empty($attrs['diff']) && !empty($id)) {
1219
            $facts = $record->getFacts();
1220
            Functions::sortFacts($facts);
1221
            $this->repeats  = array();
1222
            $nonfacts       = explode(',', $tag);
1223
            foreach ($facts as $event) {
1224
                if (!in_array($event->getTag(), $nonfacts)) {
1225
                    $this->repeats[] = $event->getGedcom();
1226
                }
1227
            }
1228
        } else {
1229
            foreach ($record->getFacts() as $fact) {
1230
                if ($fact->isPendingAddition() && $fact->getTag() !== 'CHAN') {
1231
                    $this->repeats[] = $fact->getGedcom();
1232
                }
1233
            }
1234
        }
1235
    }
1236
1237
    /**
1238
     * XML </Facts>
1239
     */
1240
    private function factsEndHandler()
1241
    {
1242
        global $report;
1243
1244
        $this->process_repeats--;
1245
        if ($this->process_repeats > 0) {
1246
            return;
1247
        }
1248
1249
        // Check if there is anything to repeat
1250
        if (count($this->repeats) > 0) {
1251
1252
            $line       = xml_get_current_line_number($this->parser) - 1;
1253
            $lineoffset = 0;
1254
            foreach ($this->repeats_stack as $rep) {
1255
                $lineoffset += $rep[1];
1256
            }
1257
1258
            //-- read the xml from the file
1259
            $lines = file($report);
1260
            while ($lineoffset + $this->repeat_bytes > 0 && strpos($lines[$lineoffset + $this->repeat_bytes], '<Facts ') === false) {
1261
                $lineoffset--;
1262
            }
1263
            $lineoffset++;
1264
            $reportxml = "<tempdoc>\n";
1265
            $i         = $line + $lineoffset;
1266
            $line_nr   = $this->repeat_bytes + $lineoffset;
1267
            while ($line_nr < $i) {
1268
                $reportxml .= $lines[$line_nr];
1269
                $line_nr++;
1270
            }
1271
            // No need to drag this
1272
            unset($lines);
1273
            $reportxml .= "</tempdoc>\n";
1274
            // Save original values
1275
            array_push($this->parser_stack, $this->parser);
1276
            $oldgedrec = $this->gedrec;
1277
            $count     = count($this->repeats);
1278
            $i         = 0;
1279
            while ($i < $count) {
1280
                $this->gedrec = $this->repeats[$i];
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->repeats[$i] of type array is incompatible with the declared type string of property $gedrec.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1281
                $this->fact   = '';
1282
                $this->desc   = '';
1283
                if (preg_match('/1 (\w+)(.*)/', $this->gedrec, $match)) {
0 ignored issues
show
Bug introduced by
$this->gedrec of type array is incompatible with the type string expected by parameter $subject of preg_match(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1283
                if (preg_match('/1 (\w+)(.*)/', /** @scrutinizer ignore-type */ $this->gedrec, $match)) {
Loading history...
1284
                    $this->fact = $match[1];
1285
                    if ($this->fact === 'EVEN' || $this->fact === 'FACT') {
1286
                        $tmatch = array();
1287
                        if (preg_match('/2 TYPE (.+)/', $this->gedrec, $tmatch)) {
1288
                            $this->type = trim($tmatch[1]);
1289
                        } else {
1290
                            $this->type = ' ';
1291
                        }
1292
                    }
1293
                    $this->desc = trim($match[2]);
1294
                    $this->desc .= Functions::getCont(2, $this->gedrec);
0 ignored issues
show
Bug introduced by
$this->gedrec of type array is incompatible with the type string expected by parameter $nrec of Fisharebest\Webtrees\Fun...ns\Functions::getCont(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1294
                    $this->desc .= Functions::getCont(2, /** @scrutinizer ignore-type */ $this->gedrec);
Loading history...
1295
                }
1296
                $repeat_parser = xml_parser_create();
1297
                $this->parser  = $repeat_parser;
0 ignored issues
show
Documentation Bug introduced by
It seems like $repeat_parser can also be of type XmlParser. However, the property $parser is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1298
                xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1299
                xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1300
                xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1301
                if (!xml_parse($repeat_parser, $reportxml, true)) {
1302
                    throw new \DomainException(sprintf(
1303
                        'FactsEHandler XML error: %s at line %d',
1304
                        xml_error_string(xml_get_error_code($repeat_parser)),
1305
                        xml_get_current_line_number($repeat_parser)
1306
                    ));
1307
                }
1308
                xml_parser_free($repeat_parser);
1309
                $i++;
1310
            }
1311
            // Restore original values
1312
            $this->parser = array_pop($this->parser_stack);
1313
            $this->gedrec = $oldgedrec;
1314
        }
1315
        list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1316
    }
1317
1318
    /**
1319
     * Setting upp or changing variables in the XML
1320
     * The XML variable name and value is stored in $this->vars
1321
     *
1322
     * @param array $attrs an array of key value pairs for the attributes
1323
     */
1324
    private function setVarStartHandler($attrs)
1325
    {
1326
        if (empty($attrs['name'])) {
1327
            throw new \DomainException('REPORT ERROR var: The attribute "name" is missing or not set in the XML file');
1328
        }
1329
1330
        $name  = $attrs['name'];
1331
        $value = $attrs['value'];
1332
        $match = array();
1333
        // Current GEDCOM record strings
1334
        if ($value == "@ID") {
1335
            if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1336
                $value = $match[1];
1337
            }
1338
        } elseif ($value == "@fact") {
1339
            $value = $this->fact;
1340
        } elseif ($value == "@desc") {
1341
            $value = $this->desc;
1342
        } elseif ($value == "@generation") {
1343
            $value = $this->generation;
1344
        } elseif (preg_match("/@(\w+)/", $value, $match)) {
1345
            $gmatch = array();
1346
            if (preg_match("/\d $match[1] (.+)/", $this->gedrec, $gmatch)) {
1347
                $value = str_replace("@", "", trim($gmatch[1]));
1348
            }
1349
        }
1350
        if (preg_match("/\\$(\w+)/", $name, $match)) {
1351
            $name = $this->vars["'" . $match[1] . "'"]['id'];
1352
        }
1353
        $count = preg_match_all("/\\$(\w+)/", $value, $match, PREG_SET_ORDER);
1354
        $i     = 0;
1355
        while ($i < $count) {
1356
            $t     = $this->vars[$match[$i][1]]['id'];
1357
            $value = preg_replace("/\\$" . $match[$i][1] . "/", $t, $value, 1);
1358
            $i++;
1359
        }
1360
        if (preg_match('/^I18N::number\((.+)\)$/', $value, $match)) {
1361
            $value = I18N::number($match[1]);
1362
        } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $value, $match)) {
1363
            $value = I18N::translate($match[1]);
1364
        } elseif (preg_match('/^I18N::translateContext\(\'(.+)\', *\'(.+)\'\)$/', $value, $match)) {
1365
            $value = I18N::translateContext($match[1], $match[2]);
1366
        }
1367
        // Arithmetic functions
1368
        if (preg_match("/(\d+)\s*([\-\+\*\/])\s*(\d+)/", $value, $match)) {
1369
            switch ($match[2]) {
1370
                case "+":
1371
                    $t     = $match[1] + $match[3];
1372
                    $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1373
                    break;
1374
                case "-":
1375
                    $t     = $match[1] - $match[3];
1376
                    $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1377
                    break;
1378
                case "*":
1379
                    $t     = $match[1] * $match[3];
1380
                    $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1381
                    break;
1382
                case "/":
1383
                    $t     = $match[1] / $match[3];
1384
                    $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1385
                    break;
1386
            }
1387
        }
1388
        if (strpos($value, "@") !== false) {
1389
            $value = "";
1390
        }
1391
        $this->vars[$name]['id'] = $value;
1392
    }
1393
1394
    /**
1395
     * XML <if > start element
1396
     *
1397
     * @param array $attrs an array of key value pairs for the attributes
1398
     */
1399
    private function ifStartHandler($attrs)
1400
    {
1401
        if ($this->process_ifs > 0) {
1402
            $this->process_ifs++;
1403
1404
            return;
1405
        }
1406
1407
        $condition = $attrs['condition'];
1408
        $condition = $this->substituteVars($condition, true);
1409
        $condition = str_replace(array(" LT ", " GT "), array("<", ">"), $condition);
1410
        // Replace the first accurance only once of @fact:DATE or in any other combinations to the current fact, such as BIRT
1411
        $condition = str_replace("@fact:", $this->fact . ':', $condition);
1412
        $match     = array();
1413
        $count     = preg_match_all("/@([\w:\.]+)/", $condition, $match, PREG_SET_ORDER);
1414
        $i         = 0;
1415
        while ($i < $count) {
1416
            $id    = $match[$i][1];
1417
            $value = '""';
1418
            if ($id == "ID") {
1419
                if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1420
                    $value = "'" . $match[1] . "'";
1421
                }
1422
            } elseif ($id === "fact") {
1423
                $value = '"' . $this->fact . '"';
1424
            } elseif ($id === "desc") {
1425
                $value = '"' . addslashes($this->desc) . '"';
1426
            } elseif ($id === "generation") {
1427
                $value = '"' . $this->generation . '"';
1428
            } else {
1429
1430
                $temp  = explode(" ", trim($this->gedrec));
1431
                $level = $temp[0];
1432
                if ($level == 0) {
1433
                    $level++;
1434
                }
1435
                $value = $this->getGedcomValue($id, $level, $this->gedrec);
0 ignored issues
show
Bug introduced by
$level of type string is incompatible with the type integer expected by parameter $level of Fisharebest\Webtrees\Rep...erate::getGedcomValue(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1435
                $value = $this->getGedcomValue($id, /** @scrutinizer ignore-type */ $level, $this->gedrec);
Loading history...
1436
                if (empty($value)) {
1437
                    $level++;
1438
                    $value = $this->getGedcomValue($id, $level, $this->gedrec);
1439
                }
1440
                $value = preg_replace('/^@(' . WT_REGEX_XREF . ')@$/', '$1', $value);
1441
                $value = '"' . addslashes($value) . '"';
1442
            }
1443
            $condition = str_replace("@$id", $value, $condition);
1444
            $i++;
1445
        }
1446
        $ret = eval("return (bool) ($condition);");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
1447
        if (!$ret) {
1448
            $this->process_ifs++;
1449
        }
1450
    }
1451
1452
    /**
1453
     * XML <if /> end element
1454
     */
1455
    private function ifEndHandler()
1456
    {
1457
        if ($this->process_ifs > 0) {
1458
            $this->process_ifs--;
1459
        }
1460
    }
1461
1462
    /**
1463
     * XML <Footnote > start element
1464
     * Collect the Footnote links
1465
     * GEDCOM Records that are protected by Privacy setting will be ignore
1466
     *
1467
     * @param array $attrs an array of key value pairs for the attributes
1468
     */
1469
    private function footnoteStartHandler($attrs)
1470
    {
1471
        global $WT_TREE;
1472
1473
        $id = "";
1474
        if (preg_match("/[0-9] (.+) @(.+)@/", $this->gedrec, $match)) {
1475
            $id = $match[2];
1476
        }
1477
        $record = GedcomRecord::getInstance($id, $WT_TREE);
1478
        if ($record && $record->canShow()) {
1479
            array_push($this->print_data_stack, $this->print_data);
1480
            $this->print_data = true;
1481
            $style            = "";
1482
            if (!empty($attrs['style'])) {
1483
                $style = $attrs['style'];
1484
            }
1485
            $this->footnote_element = $this->current_element;
1486
            $this->current_element  = $this->report_root->createFootnote($style);
0 ignored issues
show
introduced by
The method createFootnote() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1486
            /** @scrutinizer ignore-call */ 
1487
            $this->current_element  = $this->report_root->createFootnote($style);
Loading history...
1487
        } else {
1488
            $this->print_data       = false;
1489
            $this->process_footnote = false;
1490
        }
1491
    }
1492
1493
    /**
1494
     * XML <Footnote /> end element
1495
     * Print the collected Footnote data
1496
     */
1497
    private function footnoteEndHandler()
1498
    {
1499
        if ($this->process_footnote) {
1500
            $this->print_data = array_pop($this->print_data_stack);
1501
            $temp             = trim($this->current_element->getValue());
1502
            if (strlen($temp) > 3) {
1503
                $this->wt_report->addElement($this->current_element);
1504
            }
1505
            $this->current_element = $this->footnote_element;
1506
        } else {
1507
            $this->process_footnote = true;
1508
        }
1509
    }
1510
1511
    /**
1512
     * XML <FootnoteTexts /> element
1513
     */
1514
    private function footnoteTextsStartHandler()
1515
    {
1516
        $temp = "footnotetexts";
1517
        $this->wt_report->addElement($temp);
1518
    }
1519
1520
    /**
1521
     * XML <AgeAtDeath /> element handler
1522
     */
1523
    private function ageAtDeathStartHandler()
1524
    {
1525
        // This duplicates functionality in FunctionsPrint::format_fact_date()
1526
        global $factrec, $WT_TREE;
1527
1528
        $match = array();
1529
        if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1530
            $person = Individual::getInstance($match[1], $WT_TREE);
1531
            // Recorded age
1532
            if (preg_match('/\n2 AGE (.+)/', $factrec, $match)) {
1533
                $fact_age = $match[1];
1534
            } else {
1535
                $fact_age = '';
1536
            }
1537
            if (preg_match('/\n2 HUSB\n3 AGE (.+)/', $factrec, $match)) {
1538
                $husb_age = $match[1];
1539
            } else {
1540
                $husb_age = '';
1541
            }
1542
            if (preg_match('/\n2 WIFE\n3 AGE (.+)/', $factrec, $match)) {
1543
                $wife_age = $match[1];
1544
            } else {
1545
                $wife_age = '';
1546
            }
1547
1548
            // Calculated age
1549
            $birth_date = $person->getBirthDate();
0 ignored issues
show
Bug introduced by
The method getBirthDate() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1549
            /** @scrutinizer ignore-call */ 
1550
            $birth_date = $person->getBirthDate();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getBirthDate() does not exist on Fisharebest\Webtrees\Family. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1549
            /** @scrutinizer ignore-call */ 
1550
            $birth_date = $person->getBirthDate();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getBirthDate() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1549
            /** @scrutinizer ignore-call */ 
1550
            $birth_date = $person->getBirthDate();
Loading history...
Bug introduced by
The method getBirthDate() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1549
            /** @scrutinizer ignore-call */ 
1550
            $birth_date = $person->getBirthDate();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getBirthDate() does not exist on Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1549
            /** @scrutinizer ignore-call */ 
1550
            $birth_date = $person->getBirthDate();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getBirthDate() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1549
            /** @scrutinizer ignore-call */ 
1550
            $birth_date = $person->getBirthDate();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1550
            // Can't use getDeathDate(), as this also gives BURI/CREM events, which
1551
            // wouldn't give the correct "days after death" result for people with
1552
            // no DEAT.
1553
            $death_event = $person->getFirstFact('DEAT');
1554
            if ($death_event) {
1555
                $death_date = $death_event->getDate();
1556
            } else {
1557
                $death_date = new Date('');
1558
            }
1559
            $value = '';
1560
            if (Date::compare($birth_date, $death_date) <= 0 || !$person->isDead()) {
0 ignored issues
show
Bug introduced by
The method isDead() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1560
            if (Date::compare($birth_date, $death_date) <= 0 || !$person->/** @scrutinizer ignore-call */ isDead()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method isDead() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1560
            if (Date::compare($birth_date, $death_date) <= 0 || !$person->/** @scrutinizer ignore-call */ isDead()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method isDead() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1560
            if (Date::compare($birth_date, $death_date) <= 0 || !$person->/** @scrutinizer ignore-call */ isDead()) {
Loading history...
Bug introduced by
The method isDead() does not exist on Fisharebest\Webtrees\Family. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1560
            if (Date::compare($birth_date, $death_date) <= 0 || !$person->/** @scrutinizer ignore-call */ isDead()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method isDead() does not exist on Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1560
            if (Date::compare($birth_date, $death_date) <= 0 || !$person->/** @scrutinizer ignore-call */ isDead()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method isDead() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1560
            if (Date::compare($birth_date, $death_date) <= 0 || !$person->/** @scrutinizer ignore-call */ isDead()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1561
                $age = Date::getAgeGedcom($birth_date, $death_date);
1562
                // Only show calculated age if it differs from recorded age
1563
                if ($age != '' && $age != "0d") {
1564
                    if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age
0 ignored issues
show
Bug introduced by
The method getSex() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1564
                    if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->/** @scrutinizer ignore-call */ getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getSex() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1564
                    if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->/** @scrutinizer ignore-call */ getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getSex() does not exist on Fisharebest\Webtrees\Family. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1564
                    if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->/** @scrutinizer ignore-call */ getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getSex() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1564
                    if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->/** @scrutinizer ignore-call */ getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age
Loading history...
Bug introduced by
The method getSex() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1564
                    if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->/** @scrutinizer ignore-call */ getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getSex() does not exist on Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1564
                    if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->/** @scrutinizer ignore-call */ getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1565
                    ) {
1566
                        $value  = FunctionsDate::getAgeAtEvent($age);
1567
                        $abbrev = substr($value, 0, strpos($value, ' ') + 5);
1568
                        if ($value !== $abbrev) {
1569
                            $value = $abbrev . '.';
1570
                        }
1571
                    }
1572
                }
1573
            }
1574
            $this->current_element->addText($value);
1575
        }
1576
    }
1577
1578
    /**
1579
     * XML element Forced line break handler - HTML code
1580
     */
1581
    private function brStartHandler()
1582
    {
1583
        if ($this->print_data && $this->process_gedcoms === 0) {
1584
            $this->current_element->addText('<br>');
1585
        }
1586
    }
1587
1588
    /**
1589
     * XML <sp />element Forced space handler
1590
     */
1591
    private function spStartHandler()
1592
    {
1593
        if ($this->print_data && $this->process_gedcoms === 0) {
1594
            $this->current_element->addText(' ');
1595
        }
1596
    }
1597
1598
    /**
1599
     * XML <HighlightedImage/>
1600
     *
1601
     * @param array $attrs an array of key value pairs for the attributes
1602
     */
1603
    private function highlightedImageStartHandler($attrs)
1604
    {
1605
        global $WT_TREE;
1606
1607
        $id    = '';
1608
        $match = array();
1609
        if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1610
            $id = $match[1];
1611
        }
1612
1613
        // mixed Position the top corner of this box on the page. the default is the current position
1614
        $top = '.';
1615
        if (isset($attrs['top'])) {
1616
            if ($attrs['top'] === '0') {
1617
                $top = 0;
1618
            } elseif ($attrs['top'] === '.') {
1619
                $top = '.';
1620
            } elseif (!empty($attrs['top'])) {
1621
                $top = (int) $attrs['top'];
1622
            }
1623
        }
1624
1625
        // mixed Position the left corner of this box on the page. the default is the current position
1626
        $left = '.';
1627
        if (isset($attrs['left'])) {
1628
            if ($attrs['left'] === '0') {
1629
                $left = 0;
1630
            } elseif ($attrs['left'] === '.') {
1631
                $left = '.';
1632
            } elseif (!empty($attrs['left'])) {
1633
                $left = (int) $attrs['left'];
1634
            }
1635
        }
1636
1637
        // string Align the image in left, center, right
1638
        $align = '';
1639
        if (!empty($attrs['align'])) {
1640
            $align = $attrs['align'];
1641
        }
1642
1643
        // string Next Line should be T:next to the image, N:next line
1644
        $ln = '';
1645
        if (!empty($attrs['ln'])) {
1646
            $ln = $attrs['ln'];
1647
        }
1648
1649
        $width  = 0;
1650
        $height = 0;
1651
        if (!empty($attrs['width'])) {
1652
            $width = (int) $attrs['width'];
1653
        }
1654
        if (!empty($attrs['height'])) {
1655
            $height = (int) $attrs['height'];
1656
        }
1657
1658
        $person      = Individual::getInstance($id, $WT_TREE);
1659
        $mediaobject = $person->findHighlightedMedia();
0 ignored issues
show
Bug introduced by
The method findHighlightedMedia() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1659
        /** @scrutinizer ignore-call */ 
1660
        $mediaobject = $person->findHighlightedMedia();
Loading history...
Bug introduced by
The method findHighlightedMedia() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1659
        /** @scrutinizer ignore-call */ 
1660
        $mediaobject = $person->findHighlightedMedia();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method findHighlightedMedia() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1659
        /** @scrutinizer ignore-call */ 
1660
        $mediaobject = $person->findHighlightedMedia();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method findHighlightedMedia() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1659
        /** @scrutinizer ignore-call */ 
1660
        $mediaobject = $person->findHighlightedMedia();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method findHighlightedMedia() does not exist on Fisharebest\Webtrees\Family. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1659
        /** @scrutinizer ignore-call */ 
1660
        $mediaobject = $person->findHighlightedMedia();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method findHighlightedMedia() does not exist on Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1659
        /** @scrutinizer ignore-call */ 
1660
        $mediaobject = $person->findHighlightedMedia();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1660
        if ($mediaobject) {
1661
            $attributes = $mediaobject->getImageAttributes('thumb');
1662
            if (in_array(
1663
                    $attributes['ext'],
1664
                    array(
1665
                        'GIF',
1666
                        'JPG',
1667
                        'PNG',
1668
                        'SWF',
1669
                        'PSD',
1670
                        'BMP',
1671
                        'TIFF',
1672
                        'TIFF',
1673
                        'JPC',
1674
                        'JP2',
1675
                        'JPX',
1676
                        'JB2',
1677
                        'SWC',
1678
                        'IFF',
1679
                        'WBMP',
1680
                        'XBM',
1681
                    )
1682
                ) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1683
            ) {
1684
                if ($width > 0 && $height == 0) {
1685
                    $perc   = $width / $attributes['adjW'];
1686
                    $height = round($attributes['adjH'] * $perc);
1687
                } elseif ($height > 0 && $width == 0) {
1688
                    $perc  = $height / $attributes['adjH'];
1689
                    $width = round($attributes['adjW'] * $perc);
1690
                } else {
1691
                    $width  = $attributes['adjW'];
1692
                    $height = $attributes['adjH'];
1693
                }
1694
                $image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
0 ignored issues
show
introduced by
The method createImageFromObject() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1694
                /** @scrutinizer ignore-call */ 
1695
                $image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
Loading history...
1695
                $this->wt_report->addElement($image);
1696
            }
1697
        }
1698
    }
1699
1700
    /**
1701
     * XML <Image/>
1702
     *
1703
     * @param array $attrs an array of key value pairs for the attributes
1704
     */
1705
    private function imageStartHandler($attrs)
1706
    {
1707
        global $WT_TREE;
1708
1709
        // mixed Position the top corner of this box on the page. the default is the current position
1710
        $top = '.';
1711
        if (isset($attrs['top'])) {
1712
            if ($attrs['top'] === "0") {
1713
                $top = 0;
1714
            } elseif ($attrs['top'] === '.') {
1715
                $top = '.';
1716
            } elseif (!empty($attrs['top'])) {
1717
                $top = (int) $attrs['top'];
1718
            }
1719
        }
1720
1721
        // mixed Position the left corner of this box on the page. the default is the current position
1722
        $left = '.';
1723
        if (isset($attrs['left'])) {
1724
            if ($attrs['left'] === '0') {
1725
                $left = 0;
1726
            } elseif ($attrs['left'] === '.') {
1727
                $left = '.';
1728
            } elseif (!empty($attrs['left'])) {
1729
                $left = (int) $attrs['left'];
1730
            }
1731
        }
1732
1733
        // string Align the image in left, center, right
1734
        $align = '';
1735
        if (!empty($attrs['align'])) {
1736
            $align = $attrs['align'];
1737
        }
1738
1739
        // string Next Line should be T:next to the image, N:next line
1740
        $ln = 'T';
1741
        if (!empty($attrs['ln'])) {
1742
            $ln = $attrs['ln'];
1743
        }
1744
1745
        $width  = 0;
1746
        $height = 0;
1747
        if (!empty($attrs['width'])) {
1748
            $width = (int) $attrs['width'];
1749
        }
1750
        if (!empty($attrs['height'])) {
1751
            $height = (int) $attrs['height'];
1752
        }
1753
1754
        $file = '';
1755
        if (!empty($attrs['file'])) {
1756
            $file = $attrs['file'];
1757
        }
1758
        if ($file == "@FILE") {
1759
            $match = array();
1760
            if (preg_match("/\d OBJE @(.+)@/", $this->gedrec, $match)) {
1761
                $mediaobject = Media::getInstance($match[1], $WT_TREE);
1762
                $attributes  = $mediaobject->getImageAttributes('thumb');
0 ignored issues
show
Bug introduced by
The method getImageAttributes() does not exist on Fisharebest\Webtrees\Family. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1762
                /** @scrutinizer ignore-call */ 
1763
                $attributes  = $mediaobject->getImageAttributes('thumb');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getImageAttributes() does not exist on Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1762
                /** @scrutinizer ignore-call */ 
1763
                $attributes  = $mediaobject->getImageAttributes('thumb');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getImageAttributes() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1762
                /** @scrutinizer ignore-call */ 
1763
                $attributes  = $mediaobject->getImageAttributes('thumb');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getImageAttributes() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1762
                /** @scrutinizer ignore-call */ 
1763
                $attributes  = $mediaobject->getImageAttributes('thumb');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getImageAttributes() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1762
                /** @scrutinizer ignore-call */ 
1763
                $attributes  = $mediaobject->getImageAttributes('thumb');
Loading history...
Bug introduced by
The method getImageAttributes() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1762
                /** @scrutinizer ignore-call */ 
1763
                $attributes  = $mediaobject->getImageAttributes('thumb');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1763
                if (in_array(
1764
                        $attributes['ext'],
1765
                        array(
1766
                            'GIF',
1767
                            'JPG',
1768
                            'PNG',
1769
                            'SWF',
1770
                            'PSD',
1771
                            'BMP',
1772
                            'TIFF',
1773
                            'TIFF',
1774
                            'JPC',
1775
                            'JP2',
1776
                            'JPX',
1777
                            'JB2',
1778
                            'SWC',
1779
                            'IFF',
1780
                            'WBMP',
1781
                            'XBM',
1782
                        )
1783
                    ) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
0 ignored issues
show
Bug introduced by
The method fileExists() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1783
                    ) && $mediaobject->canShow() && $mediaobject->/** @scrutinizer ignore-call */ fileExists('thumb')
Loading history...
Bug introduced by
The method fileExists() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1783
                    ) && $mediaobject->canShow() && $mediaobject->/** @scrutinizer ignore-call */ fileExists('thumb')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method fileExists() does not exist on Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1783
                    ) && $mediaobject->canShow() && $mediaobject->/** @scrutinizer ignore-call */ fileExists('thumb')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method fileExists() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1783
                    ) && $mediaobject->canShow() && $mediaobject->/** @scrutinizer ignore-call */ fileExists('thumb')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method fileExists() does not exist on Fisharebest\Webtrees\Family. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1783
                    ) && $mediaobject->canShow() && $mediaobject->/** @scrutinizer ignore-call */ fileExists('thumb')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method fileExists() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1783
                    ) && $mediaobject->canShow() && $mediaobject->/** @scrutinizer ignore-call */ fileExists('thumb')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1784
                ) {
1785
                    if ($width > 0 && $height == 0) {
1786
                        $perc   = $width / $attributes['adjW'];
1787
                        $height = round($attributes['adjH'] * $perc);
1788
                    } elseif ($height > 0 && $width == 0) {
1789
                        $perc  = $height / $attributes['adjH'];
1790
                        $width = round($attributes['adjW'] * $perc);
1791
                    } else {
1792
                        $width  = $attributes['adjW'];
1793
                        $height = $attributes['adjH'];
1794
                    }
1795
                    $image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1796
                    $this->wt_report->addElement($image);
1797
                }
1798
            }
1799
        } else {
1800
            if (file_exists($file) && preg_match("/(jpg|jpeg|png|gif)$/i", $file)) {
1801
                $size = getimagesize($file);
1802
                if ($width > 0 && $height == 0) {
1803
                    $perc   = $width / $size[0];
1804
                    $height = round($size[1] * $perc);
1805
                } elseif ($height > 0 && $width == 0) {
1806
                    $perc  = $height / $size[1];
1807
                    $width = round($size[0] * $perc);
1808
                } else {
1809
                    $width  = $size[0];
1810
                    $height = $size[1];
1811
                }
1812
                $image = $this->report_root->createImage($file, $left, $top, $width, $height, $align, $ln);
0 ignored issues
show
introduced by
The method createImage() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1812
                /** @scrutinizer ignore-call */ 
1813
                $image = $this->report_root->createImage($file, $left, $top, $width, $height, $align, $ln);
Loading history...
1813
                $this->wt_report->addElement($image);
1814
            }
1815
        }
1816
    }
1817
1818
    /**
1819
     * XML <Line> element handler
1820
     *
1821
     * @param array $attrs an array of key value pairs for the attributes
1822
     */
1823
    private function lineStartHandler($attrs)
1824
    {
1825
        // Start horizontal position, current position (default)
1826
        $x1 = ".";
1827
        if (isset($attrs['x1'])) {
1828
            if ($attrs['x1'] === "0") {
1829
                $x1 = 0;
1830
            } elseif ($attrs['x1'] === ".") {
1831
                $x1 = ".";
1832
            } elseif (!empty($attrs['x1'])) {
1833
                $x1 = (int) $attrs['x1'];
1834
            }
1835
        }
1836
        // Start vertical position, current position (default)
1837
        $y1 = ".";
1838
        if (isset($attrs['y1'])) {
1839
            if ($attrs['y1'] === "0") {
1840
                $y1 = 0;
1841
            } elseif ($attrs['y1'] === ".") {
1842
                $y1 = ".";
1843
            } elseif (!empty($attrs['y1'])) {
1844
                $y1 = (int) $attrs['y1'];
1845
            }
1846
        }
1847
        // End horizontal position, maximum width (default)
1848
        $x2 = ".";
1849
        if (isset($attrs['x2'])) {
1850
            if ($attrs['x2'] === "0") {
1851
                $x2 = 0;
1852
            } elseif ($attrs['x2'] === ".") {
1853
                $x2 = ".";
1854
            } elseif (!empty($attrs['x2'])) {
1855
                $x2 = (int) $attrs['x2'];
1856
            }
1857
        }
1858
        // End vertical position
1859
        $y2 = ".";
1860
        if (isset($attrs['y2'])) {
1861
            if ($attrs['y2'] === "0") {
1862
                $y2 = 0;
1863
            } elseif ($attrs['y2'] === ".") {
1864
                $y2 = ".";
1865
            } elseif (!empty($attrs['y2'])) {
1866
                $y2 = (int) $attrs['y2'];
1867
            }
1868
        }
1869
1870
        $line = $this->report_root->createLine($x1, $y1, $x2, $y2);
0 ignored issues
show
introduced by
The method createLine() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1870
        /** @scrutinizer ignore-call */ 
1871
        $line = $this->report_root->createLine($x1, $y1, $x2, $y2);
Loading history...
1871
        $this->wt_report->addElement($line);
1872
    }
1873
1874
    /**
1875
     * XML <List>
1876
     *
1877
     * @param array $attrs an array of key value pairs for the attributes
1878
     */
1879
    private function listStartHandler($attrs)
1880
    {
1881
        global $WT_TREE;
1882
1883
        $this->process_repeats++;
1884
        if ($this->process_repeats > 1) {
1885
            return;
1886
        }
1887
1888
        $match = array();
1889
        if (isset($attrs['sortby'])) {
1890
            $sortby = $attrs['sortby'];
1891
            if (preg_match("/\\$(\w+)/", $sortby, $match)) {
1892
                $sortby = $this->vars[$match[1]]['id'];
1893
                $sortby = trim($sortby);
1894
            }
1895
        } else {
1896
            $sortby = "NAME";
1897
        }
1898
1899
        if (isset($attrs['list'])) {
1900
            $listname = $attrs['list'];
1901
        } else {
1902
            $listname = "individual";
1903
        }
1904
        // Some filters/sorts can be applied using SQL, while others require PHP
1905
        switch ($listname) {
1906
            case "pending":
1907
                $rows = Database::prepare(
1908
                "SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" .
1909
                " FROM `##change`" . " WHERE (xref, change_id) IN (" .
1910
                "  SELECT xref, MAX(change_id)" .
1911
                "  FROM `##change`" .
1912
                "  WHERE status = 'pending' AND gedcom_id = :tree_id" .
1913
                "  GROUP BY xref" .
1914
                " )"
1915
                )->execute(array(
1916
                    'tree_id' => $WT_TREE->getTreeId(),
1917
                ))->fetchAll();
1918
                $this->list = array();
1919
                foreach ($rows as $row) {
1920
                    $this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
1921
                }
1922
                break;
1923
            case 'individual':
1924
                $sql_select   = "SELECT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
1925
                $sql_join     = "";
1926
                $sql_where    = " WHERE i_file = :tree_id";
1927
                $sql_order_by = "";
1928
                $sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1929
                foreach ($attrs as $attr => $value) {
1930
                    if (strpos($attr, 'filter') === 0 && $value) {
1931
                        $value = $this->substituteVars($value, false);
1932
                        // Convert the various filters into SQL
1933
                        if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1934
                            $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
1935
                            $sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1936
                            $sql_params[$attr . 'fact'] = $match[1];
1937
                            $date                       = new Date($match[3]);
1938
                            if ($match[2] == "LTE") {
1939
                                $sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1940
                                $sql_params[$attr . 'date'] = $date->maximumJulianDay();
1941
                            } else {
1942
                                $sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1943
                                $sql_params[$attr . 'date'] = $date->minimumJulianDay();
1944
                            }
1945
                            if ($sortby == $match[1]) {
1946
                                $sortby = "";
1947
                                $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1948
                            }
1949
                            unset($attrs[$attr]); // This filter has been fully processed
1950
                        } elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
1951
                            // Do nothing, unless you have to
1952
                            if ($match[1] != '' || $sortby == 'NAME') {
1953
                                $sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
1954
                                // Search the DB only if there is any name supplied
1955
                                if ($match[1] != "") {
1956
                                    $names = explode(" ", $match[1]);
1957
                                    foreach ($names as $n => $name) {
1958
                                        $sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1959
                                        $sql_params[$attr . 'name' . $n] = $name;
1960
                                    }
1961
                                }
1962
                                // Let the DB do the name sorting even when no name was entered
1963
                                if ($sortby == "NAME") {
1964
                                    $sortby = "";
1965
                                    $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1966
                                }
1967
                            }
1968
                            unset($attrs[$attr]); // This filter has been fully processed
1969
                        } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1970
                            $sql_where .= " AND i_gedcom REGEXP :{$attr}gedcom";
1971
                            // PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1972
                            $sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1973
                            unset($attrs[$attr]); // This filter has been fully processed
1974
                        } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1975
                            $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file = i_file)";
1976
                            $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file = {$attr}b.pl_file AND {$attr}b.pl_p_id = {$attr}a.p_id AND {$attr}b.pl_gid = i_id)";
1977
                            $sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
1978
                            $sql_params[$attr . 'place'] = $match[1];
1979
                            // Don't unset this filter. This is just initial filtering
1980
                        } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
1981
                            $sql_where .= " AND i_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
1982
                            $sql_params[$attr . 'contains1'] = $match[1];
1983
                            $sql_params[$attr . 'contains2'] = $match[2];
1984
                            $sql_params[$attr . 'contains3'] = $match[3];
1985
                            // Don't unset this filter. This is just initial filtering
1986
                        }
1987
                    }
1988
                }
1989
1990
                $this->list = array();
1991
                $rows       = Database::prepare(
1992
                $sql_select . $sql_join . $sql_where . $sql_order_by
1993
                )->execute($sql_params)->fetchAll();
1994
1995
                foreach ($rows as $row) {
1996
                    $this->list[$row->xref] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
1997
                }
1998
                break;
1999
2000
            case 'family':
2001
                $sql_select   = "SELECT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
2002
                $sql_join     = "";
2003
                $sql_where    = " WHERE f_file = :tree_id";
2004
                $sql_order_by = "";
2005
                $sql_params   = array('tree_id' => $WT_TREE->getTreeId());
2006
                foreach ($attrs as $attr => $value) {
2007
                    if (strpos($attr, 'filter') === 0 && $value) {
2008
                        $value = $this->substituteVars($value, false);
2009
                        // Convert the various filters into SQL
2010
                        if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
2011
                            $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=f_file AND {$attr}.d_gid=f_id)";
2012
                            $sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
2013
                            $sql_params[$attr . 'fact'] = $match[1];
2014
                            $date                       = new Date($match[3]);
2015
                            if ($match[2] == "LTE") {
2016
                                $sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
2017
                                $sql_params[$attr . 'date'] = $date->maximumJulianDay();
2018
                            } else {
2019
                                $sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
2020
                                $sql_params[$attr . 'date'] = $date->minimumJulianDay();
2021
                            }
2022
                            if ($sortby == $match[1]) {
2023
                                $sortby = "";
2024
                                $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
2025
                            }
2026
                            unset($attrs[$attr]); // This filter has been fully processed
2027
                        } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
2028
                            $sql_where .= " AND f_gedcom REGEXP :{$attr}gedcom";
2029
                            // PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
2030
                            $sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
2031
                            unset($attrs[$attr]); // This filter has been fully processed
2032
                        } elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
2033
                            // Do nothing, unless you have to
2034
                            if ($match[1] != '' || $sortby == 'NAME') {
2035
                                $sql_join .= " JOIN `##name` AS {$attr} ON n_file = f_file AND n_id IN (f_husb, f_wife)";
2036
                                // Search the DB only if there is any name supplied
2037
                                if ($match[1] != "") {
2038
                                    $names = explode(" ", $match[1]);
2039
                                    foreach ($names as $n => $name) {
2040
                                        $sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
2041
                                        $sql_params[$attr . 'name' . $n] = $name;
2042
                                    }
2043
                                }
2044
                                // Let the DB do the name sorting even when no name was entered
2045
                                if ($sortby == "NAME") {
2046
                                    $sortby = "";
2047
                                    $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
2048
                                }
2049
                            }
2050
                            unset($attrs[$attr]); // This filter has been fully processed
2051
2052
                        } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
2053
                            $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
2054
                            $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=f_id)";
2055
                            $sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
2056
                            $sql_params[$attr . 'place'] = $match[1];
2057
                            // Don't unset this filter. This is just initial filtering
2058
                        } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
2059
                            $sql_where .= " AND f_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
2060
                            $sql_params[$attr . 'contains1'] = $match[1];
2061
                            $sql_params[$attr . 'contains2'] = $match[2];
2062
                            $sql_params[$attr . 'contains3'] = $match[3];
2063
                            // Don't unset this filter. This is just initial filtering
2064
                        }
2065
                    }
2066
                }
2067
2068
                $this->list = array();
2069
                $rows       = Database::prepare(
2070
                $sql_select . $sql_join . $sql_where . $sql_order_by
2071
                )->execute($sql_params)->fetchAll();
2072
2073
                foreach ($rows as $row) {
2074
                    $this->list[$row->xref] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
2075
                }
2076
                break;
2077
2078
            default:
2079
                throw new \DomainException('Invalid list name: ' . $listname);
2080
        }
2081
2082
        $filters  = array();
2083
        $filters2 = array();
2084
        if (isset($attrs['filter1']) && count($this->list) > 0) {
2085
            foreach ($attrs as $key => $value) {
2086
                if (preg_match("/filter(\d)/", $key)) {
2087
                    $condition = $value;
2088
                    if (preg_match("/@(\w+)/", $condition, $match)) {
2089
                        $id    = $match[1];
2090
                        $value = "''";
2091
                        if ($id == "ID") {
2092
                            if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
2093
                                $value = "'" . $match[1] . "'";
2094
                            }
2095
                        } elseif ($id == "fact") {
2096
                            $value = "'" . $this->fact . "'";
2097
                        } elseif ($id == "desc") {
2098
                            $value = "'" . $this->desc . "'";
2099
                        } else {
2100
                            if (preg_match("/\d $id (.+)/", $this->gedrec, $match)) {
2101
                                $value = "'" . str_replace("@", "", trim($match[1])) . "'";
2102
                            }
2103
                        }
2104
                        $condition = preg_replace("/@$id/", $value, $condition);
2105
                    }
2106
                    //-- handle regular expressions
2107
                    if (preg_match("/([A-Z:]+)\s*([^\s]+)\s*(.+)/", $condition, $match)) {
2108
                        $tag  = trim($match[1]);
2109
                        $expr = trim($match[2]);
2110
                        $val  = trim($match[3]);
2111
                        if (preg_match("/\\$(\w+)/", $val, $match)) {
2112
                            $val = $this->vars[$match[1]]['id'];
2113
                            $val = trim($val);
2114
                        }
2115
                        if ($val) {
2116
                            $searchstr = "";
2117
                            $tags      = explode(":", $tag);
2118
                            //-- only limit to a level number if we are specifically looking at a level
2119
                            if (count($tags) > 1) {
2120
                                $level = 1;
2121
                                foreach ($tags as $t) {
2122
                                    if (!empty($searchstr)) {
2123
                                        $searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
2124
                                    }
2125
                                    //-- search for both EMAIL and _EMAIL... silly double gedcom standard
2126
                                    if ($t == "EMAIL" || $t == "_EMAIL") {
2127
                                        $t = "_?EMAIL";
2128
                                    }
2129
                                    $searchstr .= $level . " " . $t;
2130
                                    $level++;
2131
                                }
2132
                            } else {
2133
                                if ($tag == "EMAIL" || $tag == "_EMAIL") {
2134
                                    $tag = "_?EMAIL";
2135
                                }
2136
                                $t         = $tag;
2137
                                $searchstr = "1 " . $tag;
2138
                            }
2139
                            switch ($expr) {
2140
                                case "CONTAINS":
2141
                                    if ($t == "PLAC") {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $t does not seem to be defined for all execution paths leading up to this point.
Loading history...
2142
                                        $searchstr .= "[^\n]*[, ]*" . $val;
2143
                                    } else {
2144
                                        $searchstr .= "[^\n]*" . $val;
2145
                                    }
2146
                                    $filters[] = $searchstr;
2147
                                    break;
2148
                                default:
2149
                                    $filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
2150
                                    break;
2151
                            }
2152
                        }
2153
                    }
2154
                }
2155
            }
2156
        }
2157
        //-- apply other filters to the list that could not be added to the search string
2158
        if ($filters) {
2159
            foreach ($this->list as $key => $record) {
2160
                foreach ($filters as $filter) {
2161
                    if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(Auth::accessLevel($WT_TREE)))) {
2162
                        unset($this->list[$key]);
2163
                        break;
2164
                    }
2165
                }
2166
            }
2167
        }
2168
        if ($filters2) {
2169
            $mylist = array();
2170
            foreach ($this->list as $indi) {
2171
                $key  = $indi->getXref();
2172
                $grec = $indi->privatizeGedcom(Auth::accessLevel($WT_TREE));
2173
                $keep = true;
2174
                foreach ($filters2 as $filter) {
2175
                    if ($keep) {
2176
                        $tag  = $filter['tag'];
2177
                        $expr = $filter['expr'];
2178
                        $val  = $filter['val'];
2179
                        if ($val == "''") {
2180
                            $val = "";
2181
                        }
2182
                        $tags = explode(":", $tag);
2183
                        $t    = end($tags);
2184
                        $v    = $this->getGedcomValue($tag, 1, $grec);
2185
                        //-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
2186
                        if ($t == "EMAIL" && empty($v)) {
2187
                            $tag  = str_replace("EMAIL", "_EMAIL", $tag);
2188
                            $tags = explode(":", $tag);
2189
                            $t    = end($tags);
2190
                            $v    = Functions::getSubRecord(1, $tag, $grec);
2191
                        }
2192
2193
                        switch ($expr) {
2194
                            case "GTE":
2195
                                if ($t == "DATE") {
2196
                                    $date1 = new Date($v);
2197
                                    $date2 = new Date($val);
2198
                                    $keep  = (Date::compare($date1, $date2) >= 0);
2199
                                } elseif ($val >= $v) {
2200
                                    $keep = true;
2201
                                }
2202
                                break;
2203
                            case "LTE":
2204
                                if ($t == "DATE") {
2205
                                    $date1 = new Date($v);
2206
                                    $date2 = new Date($val);
2207
                                    $keep  = (Date::compare($date1, $date2) <= 0);
2208
                                } elseif ($val >= $v) {
2209
                                    $keep = true;
2210
                                }
2211
                                break;
2212
                            default:
2213
                                if ($v == $val) {
2214
                                    $keep = true;
2215
                                } else {
2216
                                    $keep = false;
2217
                                }
2218
                                break;
2219
                        }
2220
                    }
2221
                }
2222
                if ($keep) {
2223
                    $mylist[$key] = $indi;
2224
                }
2225
            }
2226
            $this->list = $mylist;
2227
        }
2228
2229
        switch ($sortby) {
2230
            case 'NAME':
2231
                uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2232
                break;
2233
            case 'CHAN':
2234
                uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
2235
                    return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
2236
                });
2237
                break;
2238
            case 'BIRT:DATE':
2239
                uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2240
                break;
2241
            case 'DEAT:DATE':
2242
                uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2243
                break;
2244
            case 'MARR:DATE':
2245
                uasort($this->list, '\Fisharebest\Webtrees\Family::compareMarrDate');
2246
                break;
2247
            default:
2248
                // unsorted or already sorted by SQL
2249
                break;
2250
        }
2251
2252
        array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2253
        $this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
2254
    }
2255
2256
    /**
2257
     * XML <List>
2258
     */
2259
    private function listEndHandler()
2260
    {
2261
        global $report;
2262
2263
        $this->process_repeats--;
2264
        if ($this->process_repeats > 0) {
2265
            return;
2266
        }
2267
2268
        // Check if there is any list
2269
        if (count($this->list) > 0) {
2270
            $lineoffset = 0;
2271
            foreach ($this->repeats_stack as $rep) {
2272
                $lineoffset += $rep[1];
2273
            }
2274
            //-- read the xml from the file
2275
            $lines = file($report);
2276
            while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<List") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2277
                $lineoffset--;
2278
            }
2279
            $lineoffset++;
2280
            $reportxml = "<tempdoc>\n";
2281
            $line_nr   = $lineoffset + $this->repeat_bytes;
2282
            // List Level counter
2283
            $count = 1;
2284
            while (0 < $count) {
2285
                if (strpos($lines[$line_nr], "<List") !== false) {
2286
                    $count++;
2287
                } elseif (strpos($lines[$line_nr], "</List") !== false) {
2288
                    $count--;
2289
                }
2290
                if (0 < $count) {
2291
                    $reportxml .= $lines[$line_nr];
2292
                }
2293
                $line_nr++;
2294
            }
2295
            // No need to drag this
2296
            unset($lines);
2297
            $reportxml .= "</tempdoc>";
2298
            // Save original values
2299
            array_push($this->parser_stack, $this->parser);
2300
            $oldgedrec = $this->gedrec;
2301
2302
            $this->list_total   = count($this->list);
2303
            $this->list_private = 0;
2304
            foreach ($this->list as $record) {
2305
                if ($record->canShow()) {
2306
                    $this->gedrec = $record->privatizeGedcom(Auth::accessLevel($record->getTree()));
2307
                    //-- start the sax parser
2308
                    $repeat_parser = xml_parser_create();
2309
                    $this->parser  = $repeat_parser;
0 ignored issues
show
Documentation Bug introduced by
It seems like $repeat_parser can also be of type XmlParser. However, the property $parser is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2310
                    xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2311
                    xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2312
                    xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2313
                    if (!xml_parse($repeat_parser, $reportxml, true)) {
2314
                        throw new \DomainException(sprintf(
2315
                            'ListEHandler XML error: %s at line %d',
2316
                            xml_error_string(xml_get_error_code($repeat_parser)),
2317
                            xml_get_current_line_number($repeat_parser)
2318
                        ));
2319
                    }
2320
                    xml_parser_free($repeat_parser);
2321
                } else {
2322
                    $this->list_private++;
2323
                }
2324
            }
2325
            $this->list   = array();
2326
            $this->parser = array_pop($this->parser_stack);
2327
            $this->gedrec = $oldgedrec;
2328
        }
2329
        list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2330
    }
2331
2332
    /**
2333
     * XML <ListTotal> element handler
2334
     *
2335
     * Prints the total number of records in a list
2336
     * The total number is collected from
2337
     * List and Relatives
2338
     */
2339
    private function listTotalStartHandler()
2340
    {
2341
        if ($this->list_private == 0) {
2342
            $this->current_element->addText($this->list_total);
2343
        } else {
2344
            $this->current_element->addText(($this->list_total - $this->list_private) . " / " . $this->list_total);
2345
        }
2346
    }
2347
2348
    /**
2349
     * XML <Relatives>
2350
     *
2351
     * @param array $attrs an array of key value pairs for the attributes
2352
     */
2353
    private function relativesStartHandler($attrs)
2354
    {
2355
        global $WT_TREE;
2356
2357
        $this->process_repeats++;
2358
        if ($this->process_repeats > 1) {
2359
            return;
2360
        }
2361
2362
        $sortby = "NAME";
2363
        if (isset($attrs['sortby'])) {
2364
            $sortby = $attrs['sortby'];
2365
        }
2366
        $match = array();
2367
        if (preg_match("/\\$(\w+)/", $sortby, $match)) {
2368
            $sortby = $this->vars[$match[1]]['id'];
2369
            $sortby = trim($sortby);
2370
        }
2371
2372
        $maxgen = -1;
2373
        if (isset($attrs['maxgen'])) {
2374
            $maxgen = $attrs['maxgen'];
2375
        }
2376
        if ($maxgen == "*") {
2377
            $maxgen = -1;
2378
        }
2379
2380
        $group = "child-family";
2381
        if (isset($attrs['group'])) {
2382
            $group = $attrs['group'];
2383
        }
2384
        if (preg_match("/\\$(\w+)/", $group, $match)) {
2385
            $group = $this->vars[$match[1]]['id'];
2386
            $group = trim($group);
2387
        }
2388
2389
        $id = "";
2390
        if (isset($attrs['id'])) {
2391
            $id = $attrs['id'];
2392
        }
2393
        if (preg_match("/\\$(\w+)/", $id, $match)) {
2394
            $id = $this->vars[$match[1]]['id'];
2395
            $id = trim($id);
2396
        }
2397
2398
        $this->list = array();
2399
        $person     = Individual::getInstance($id, $WT_TREE);
2400
        if (!empty($person)) {
2401
            $this->list[$id] = $person;
2402
            switch ($group) {
2403
                case "child-family":
2404
                    foreach ($person->getChildFamilies() as $family) {
0 ignored issues
show
Bug introduced by
The method getChildFamilies() does not exist on Fisharebest\Webtrees\Family. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2404
                    foreach ($person->/** @scrutinizer ignore-call */ getChildFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getChildFamilies() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2404
                    foreach ($person->/** @scrutinizer ignore-call */ getChildFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getChildFamilies() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2404
                    foreach ($person->/** @scrutinizer ignore-call */ getChildFamilies() as $family) {
Loading history...
Bug introduced by
The method getChildFamilies() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2404
                    foreach ($person->/** @scrutinizer ignore-call */ getChildFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getChildFamilies() does not exist on Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2404
                    foreach ($person->/** @scrutinizer ignore-call */ getChildFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getChildFamilies() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2404
                    foreach ($person->/** @scrutinizer ignore-call */ getChildFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2405
                        $husband = $family->getHusband();
2406
                        $wife    = $family->getWife();
2407
                        if (!empty($husband)) {
2408
                            $this->list[$husband->getXref()] = $husband;
2409
                        }
2410
                        if (!empty($wife)) {
2411
                            $this->list[$wife->getXref()] = $wife;
2412
                        }
2413
                        $children = $family->getChildren();
2414
                        foreach ($children as $child) {
2415
                            if (!empty($child)) {
2416
                                $this->list[$child->getXref()] = $child;
2417
                            }
2418
                        }
2419
                    }
2420
                    break;
2421
                case "spouse-family":
2422
                    foreach ($person->getSpouseFamilies() as $family) {
0 ignored issues
show
Bug introduced by
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2422
                    foreach ($person->/** @scrutinizer ignore-call */ getSpouseFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Family. Did you maybe mean getSpouse()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2422
                    foreach ($person->/** @scrutinizer ignore-call */ getSpouseFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2422
                    foreach ($person->/** @scrutinizer ignore-call */ getSpouseFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2422
                    foreach ($person->/** @scrutinizer ignore-call */ getSpouseFamilies() as $family) {
Loading history...
Bug introduced by
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2422
                    foreach ($person->/** @scrutinizer ignore-call */ getSpouseFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2422
                    foreach ($person->/** @scrutinizer ignore-call */ getSpouseFamilies() as $family) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2423
                        $husband = $family->getHusband();
2424
                        $wife    = $family->getWife();
2425
                        if (!empty($husband)) {
2426
                            $this->list[$husband->getXref()] = $husband;
2427
                        }
2428
                        if (!empty($wife)) {
2429
                            $this->list[$wife->getXref()] = $wife;
2430
                        }
2431
                        $children = $family->getChildren();
2432
                        foreach ($children as $child) {
2433
                            if (!empty($child)) {
2434
                                $this->list[$child->getXref()] = $child;
2435
                            }
2436
                        }
2437
                    }
2438
                    break;
2439
                case "direct-ancestors":
2440
                    $this->addAncestors($this->list, $id, false, $maxgen);
2441
                    break;
2442
                case "ancestors":
2443
                    $this->addAncestors($this->list, $id, true, $maxgen);
2444
                    break;
2445
                case "descendants":
2446
                    $this->list[$id]->generation = 1;
2447
                    $this->addDescendancy($this->list, $id, false, $maxgen);
2448
                    break;
2449
                case "all":
2450
                    $this->addAncestors($this->list, $id, true, $maxgen);
2451
                    $this->addDescendancy($this->list, $id, true, $maxgen);
2452
                    break;
2453
            }
2454
        }
2455
2456
        switch ($sortby) {
2457
            case 'NAME':
2458
                uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2459
                break;
2460
            case 'BIRT:DATE':
2461
                uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2462
                break;
2463
            case 'DEAT:DATE':
2464
                uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2465
                break;
2466
            case 'generation':
2467
                $newarray = array();
2468
                reset($this->list);
2469
                $genCounter = 1;
2470
                while (count($newarray) < count($this->list)) {
2471
                    foreach ($this->list as $key => $value) {
2472
                        $this->generation = $value->generation;
2473
                        if ($this->generation == $genCounter) {
2474
                            $newarray[$key]             = new \stdClass;
2475
                            $newarray[$key]->generation = $this->generation;
2476
                        }
2477
                    }
2478
                    $genCounter++;
2479
                }
2480
                $this->list = $newarray;
2481
                break;
2482
            default:
2483
                // unsorted
2484
                break;
2485
        }
2486
        array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2487
        $this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
2488
    }
2489
2490
    /**
2491
     * XML </ Relatives>
2492
     */
2493
    private function relativesEndHandler()
2494
    {
2495
        global $report, $WT_TREE;
2496
2497
        $this->process_repeats--;
2498
        if ($this->process_repeats > 0) {
2499
            return;
2500
        }
2501
2502
        // Check if there is any relatives
2503
        if (count($this->list) > 0) {
2504
            $lineoffset = 0;
2505
            foreach ($this->repeats_stack as $rep) {
2506
                $lineoffset += $rep[1];
2507
            }
2508
            //-- read the xml from the file
2509
            $lines = file($report);
2510
            while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<Relatives") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2511
                $lineoffset--;
2512
            }
2513
            $lineoffset++;
2514
            $reportxml = "<tempdoc>\n";
2515
            $line_nr   = $lineoffset + $this->repeat_bytes;
2516
            // Relatives Level counter
2517
            $count = 1;
2518
            while (0 < $count) {
2519
                if (strpos($lines[$line_nr], "<Relatives") !== false) {
2520
                    $count++;
2521
                } elseif (strpos($lines[$line_nr], "</Relatives") !== false) {
2522
                    $count--;
2523
                }
2524
                if (0 < $count) {
2525
                    $reportxml .= $lines[$line_nr];
2526
                }
2527
                $line_nr++;
2528
            }
2529
            // No need to drag this
2530
            unset($lines);
2531
            $reportxml .= "</tempdoc>\n";
2532
            // Save original values
2533
            array_push($this->parser_stack, $this->parser);
2534
            $oldgedrec = $this->gedrec;
2535
2536
            $this->list_total   = count($this->list);
2537
            $this->list_private = 0;
2538
            foreach ($this->list as $key => $value) {
2539
                if (isset($value->generation)) {
2540
                    $this->generation = $value->generation;
2541
                }
2542
                $tmp          = GedcomRecord::getInstance($key, $WT_TREE);
2543
                $this->gedrec = $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE));
2544
2545
                $repeat_parser = xml_parser_create();
2546
                $this->parser  = $repeat_parser;
0 ignored issues
show
Documentation Bug introduced by
It seems like $repeat_parser can also be of type XmlParser. However, the property $parser is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2547
                xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2548
                xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2549
                xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2550
2551
                if (!xml_parse($repeat_parser, $reportxml, true)) {
2552
                    throw new \DomainException(sprintf("RelativesEHandler XML error: %s at line %d", xml_error_string(xml_get_error_code($repeat_parser)), xml_get_current_line_number($repeat_parser)));
2553
                }
2554
                xml_parser_free($repeat_parser);
2555
            }
2556
            // Clean up the list array
2557
            $this->list   = array();
2558
            $this->parser = array_pop($this->parser_stack);
2559
            $this->gedrec = $oldgedrec;
2560
        }
2561
        list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2562
    }
2563
2564
    /**
2565
     * XML <Generation /> element handler
2566
     *
2567
     * Prints the number of generations
2568
     */
2569
    private function generationStartHandler()
2570
    {
2571
        $this->current_element->addText($this->generation);
2572
    }
2573
2574
    /**
2575
     * XML <NewPage /> element handler
2576
     *
2577
     * Has to be placed in an element (header, pageheader, body or footer)
2578
     */
2579
    private function newPageStartHandler()
2580
    {
2581
        $temp = "addpage";
2582
        $this->wt_report->addElement($temp);
2583
    }
2584
2585
    /**
2586
     * XML <html>
2587
     *
2588
     * @param string  $tag   HTML tag name
2589
     * @param array[] $attrs an array of key value pairs for the attributes
2590
     */
2591
    private function htmlStartHandler($tag, $attrs)
2592
    {
2593
        if ($tag === "tempdoc") {
2594
            return;
2595
        }
2596
        array_push($this->wt_report_stack, $this->wt_report);
2597
        $this->wt_report       = $this->report_root->createHTML($tag, $attrs);
0 ignored issues
show
introduced by
The method createHTML() does not exist on Fisharebest\Webtrees\Report\ReportBase. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2597
        /** @scrutinizer ignore-call */ 
2598
        $this->wt_report       = $this->report_root->createHTML($tag, $attrs);
Loading history...
2598
        $this->current_element = $this->wt_report;
2599
2600
        array_push($this->print_data_stack, $this->print_data);
2601
        $this->print_data = true;
2602
    }
2603
2604
    /**
2605
     * XML </html>
2606
     *
2607
     * @param string $tag
2608
     */
2609
    private function htmlEndHandler($tag)
2610
    {
2611
        if ($tag === "tempdoc") {
2612
            return;
2613
        }
2614
2615
        $this->print_data      = array_pop($this->print_data_stack);
2616
        $this->current_element = $this->wt_report;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->wt_report of type Fisharebest\Webtrees\Report\ReportBase is incompatible with the declared type Fisharebest\Webtrees\Report\ReportBaseElement of property $current_element.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2617
        $this->wt_report       = array_pop($this->wt_report_stack);
2618
        if ($this->wt_report !== null) {
2619
            $this->wt_report->addElement($this->current_element);
2620
        } else {
2621
            $this->wt_report = $this->current_element;
2622
        }
2623
    }
2624
2625
    /**
2626
     * Handle <Input>
2627
     */
2628
    private function inputStartHandler()
2629
    {
2630
        // Dummy function, to prevent the default HtmlStartHandler() being called
2631
    }
2632
2633
    /**
2634
     * Handle </Input>
2635
     */
2636
    private function inputEndHandler()
2637
    {
2638
        // Dummy function, to prevent the default HtmlEndHandler() being called
2639
    }
2640
2641
    /**
2642
     * Handle <Report>
2643
     */
2644
    private function reportStartHandler()
2645
    {
2646
        // Dummy function, to prevent the default HtmlStartHandler() being called
2647
    }
2648
2649
    /**
2650
     * Handle </Report>
2651
     */
2652
    private function reportEndHandler()
2653
    {
2654
        // Dummy function, to prevent the default HtmlEndHandler() being called
2655
    }
2656
2657
    /**
2658
     * XML </titleEndHandler>
2659
     */
2660
    private function titleEndHandler()
2661
    {
2662
        $this->report_root->addTitle($this->text);
2663
    }
2664
2665
    /**
2666
     * XML </descriptionEndHandler>
2667
     */
2668
    private function descriptionEndHandler()
2669
    {
2670
        $this->report_root->addDescription($this->text);
2671
    }
2672
2673
    /**
2674
     * Create a list of all descendants.
2675
     *
2676
     * @param string[] $list
2677
     * @param string   $pid
2678
     * @param bool  $parents
2679
     * @param int  $generations
2680
     */
2681
    private function addDescendancy(&$list, $pid, $parents = false, $generations = -1)
2682
    {
2683
        global $WT_TREE;
2684
2685
        $person = Individual::getInstance($pid, $WT_TREE);
2686
        if ($person === null) {
2687
            return;
2688
        }
2689
        if (!isset($list[$pid])) {
2690
            $list[$pid] = $person;
2691
        }
2692
        if (!isset($list[$pid]->generation)) {
0 ignored issues
show
Bug introduced by
The property generation does not exist on string.
Loading history...
2693
            $list[$pid]->generation = 0;
2694
        }
2695
        foreach ($person->getSpouseFamilies() as $family) {
2696
            if ($parents) {
2697
                $husband = $family->getHusband();
2698
                $wife    = $family->getWife();
2699
                if ($husband) {
2700
                    $list[$husband->getXref()] = $husband;
2701
                    if (isset($list[$pid]->generation)) {
2702
                        $list[$husband->getXref()]->generation = $list[$pid]->generation - 1;
2703
                    } else {
2704
                        $list[$husband->getXref()]->generation = 1;
2705
                    }
2706
                }
2707
                if ($wife) {
2708
                    $list[$wife->getXref()] = $wife;
2709
                    if (isset($list[$pid]->generation)) {
2710
                        $list[$wife->getXref()]->generation = $list[$pid]->generation - 1;
2711
                    } else {
2712
                        $list[$wife->getXref()]->generation = 1;
2713
                    }
2714
                }
2715
            }
2716
            $children = $family->getChildren();
2717
            foreach ($children as $child) {
2718
                if ($child) {
2719
                    $list[$child->getXref()] = $child;
2720
                    if (isset($list[$pid]->generation)) {
2721
                        $list[$child->getXref()]->generation = $list[$pid]->generation + 1;
2722
                    } else {
2723
                        $list[$child->getXref()]->generation = 2;
2724
                    }
2725
                }
2726
            }
2727
            if ($generations == -1 || $list[$pid]->generation + 1 < $generations) {
2728
                foreach ($children as $child) {
2729
                    $this->addDescendancy($list, $child->getXref(), $parents, $generations); // recurse on the childs family
2730
                }
2731
            }
2732
        }
2733
    }
2734
2735
    /**
2736
     * Create a list of all ancestors.
2737
     *
2738
     * @param string[] $list
2739
     * @param string   $pid
2740
     * @param bool  $children
2741
     * @param int  $generations
2742
     */
2743
    private function addAncestors(&$list, $pid, $children = false, $generations = -1)
2744
    {
2745
        global $WT_TREE;
2746
2747
        $genlist                = array($pid);
2748
        $list[$pid]->generation = 1;
0 ignored issues
show
Bug introduced by
The property generation does not exist on string.
Loading history...
2749
        while (count($genlist) > 0) {
2750
            $id = array_shift($genlist);
2751
            if (strpos($id, 'empty') === 0) {
2752
                continue; // id can be something like “empty7”
2753
            }
2754
            $person = Individual::getInstance($id, $WT_TREE);
2755
            foreach ($person->getChildFamilies() as $family) {
2756
                $husband = $family->getHusband();
2757
                $wife    = $family->getWife();
2758
                if ($husband) {
2759
                    $list[$husband->getXref()]             = $husband;
2760
                    $list[$husband->getXref()]->generation = $list[$id]->generation + 1;
2761
                }
2762
                if ($wife) {
2763
                    $list[$wife->getXref()]             = $wife;
2764
                    $list[$wife->getXref()]->generation = $list[$id]->generation + 1;
2765
                }
2766
                if ($generations == -1 || $list[$id]->generation + 1 < $generations) {
2767
                    if ($husband) {
2768
                        array_push($genlist, $husband->getXref());
2769
                    }
2770
                    if ($wife) {
2771
                        array_push($genlist, $wife->getXref());
2772
                    }
2773
                }
2774
                if ($children) {
2775
                    foreach ($family->getChildren() as $child) {
2776
                        $list[$child->getXref()] = $child;
2777
                        if (isset($list[$id]->generation)) {
2778
                            $list[$child->getXref()]->generation = $list[$id]->generation;
2779
                        } else {
2780
                            $list[$child->getXref()]->generation = 1;
2781
                        }
2782
                    }
2783
                }
2784
            }
2785
        }
2786
    }
2787
2788
    /**
2789
     * get gedcom tag value
2790
     *
2791
     * @param string  $tag    The tag to find, use : to delineate subtags
2792
     * @param int $level  The gedcom line level of the first tag to find, setting level to 0 will cause it to use 1+ the level of the incoming record
2793
     * @param string  $gedrec The gedcom record to get the value from
2794
     *
2795
     * @return string the value of a gedcom tag from the given gedcom record
2796
     */
2797
    private function getGedcomValue($tag, $level, $gedrec)
2798
    {
2799
        global $WT_TREE;
2800
2801
        if (empty($gedrec)) {
2802
            return '';
2803
        }
2804
        $tags      = explode(':', $tag);
2805
        $origlevel = $level;
2806
        if ($level == 0) {
2807
            $level = $gedrec[0] + 1;
2808
        }
2809
2810
        $subrec = $gedrec;
2811
        foreach ($tags as $t) {
2812
            $lastsubrec = $subrec;
2813
            $subrec     = Functions::getSubRecord($level, "$level $t", $subrec);
2814
            if (empty($subrec) && $origlevel == 0) {
2815
                $level--;
2816
                $subrec = Functions::getSubRecord($level, "$level $t", $lastsubrec);
2817
            }
2818
            if (empty($subrec)) {
2819
                if ($t == "TITL") {
2820
                    $subrec = Functions::getSubRecord($level, "$level ABBR", $lastsubrec);
2821
                    if (!empty($subrec)) {
2822
                        $t = "ABBR";
2823
                    }
2824
                }
2825
                if (empty($subrec)) {
2826
                    if ($level > 0) {
2827
                        $level--;
2828
                    }
2829
                    $subrec = Functions::getSubRecord($level, "@ $t", $gedrec);
2830
                    if (empty($subrec)) {
2831
                        return '';
2832
                    }
2833
                }
2834
            }
2835
            $level++;
2836
        }
2837
        $level--;
2838
        $ct = preg_match("/$level $t(.*)/", $subrec, $match);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $t seems to be defined by a foreach iteration on line 2811. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
2839
        if ($ct == 0) {
2840
            $ct = preg_match("/$level @.+@ (.+)/", $subrec, $match);
2841
        }
2842
        if ($ct == 0) {
2843
            $ct = preg_match("/@ $t (.+)/", $subrec, $match);
2844
        }
2845
        if ($ct > 0) {
2846
            $value = trim($match[1]);
2847
            if ($t == 'NOTE' && preg_match('/^@(.+)@$/', $value, $match)) {
2848
                $note = Note::getInstance($match[1], $WT_TREE);
2849
                if ($note) {
2850
                    $value = $note->getNote();
0 ignored issues
show
Bug introduced by
The method getNote() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2850
                    /** @scrutinizer ignore-call */ 
2851
                    $value = $note->getNote();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getNote() does not exist on Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2850
                    /** @scrutinizer ignore-call */ 
2851
                    $value = $note->getNote();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getNote() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2850
                    /** @scrutinizer ignore-call */ 
2851
                    $value = $note->getNote();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getNote() does not exist on Fisharebest\Webtrees\Family. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2850
                    /** @scrutinizer ignore-call */ 
2851
                    $value = $note->getNote();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getNote() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Media or Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2850
                    /** @scrutinizer ignore-call */ 
2851
                    $value = $note->getNote();
Loading history...
2851
                } else {
2852
                    //-- set the value to the id without the @
2853
                    $value = $match[1];
2854
                }
2855
            }
2856
            if ($level != 0 || $t != "NOTE") {
2857
                $value .= Functions::getCont($level + 1, $subrec);
2858
            }
2859
2860
            return $value;
2861
        }
2862
2863
        return "";
2864
    }
2865
2866
    /**
2867
     * Replace variable identifiers with their values.
2868
     *
2869
     * @param string $expression An expression such as "$foo == 123"
2870
     * @param bool   $quote      Whether to add quotation marks
2871
     *
2872
     * @return string
2873
     */
2874
    private function substituteVars($expression, $quote)
2875
    {
2876
        $that = $this; // PHP5.3 cannot access $this inside a closure
2877
        return preg_replace_callback(
2878
            '/\$(\w+)/',
2879
            function ($matches) use ($that, $quote) {
2880
                if (isset($that->vars[$matches[1]]['id'])) {
2881
                    if ($quote) {
2882
                        return "'" . addcslashes($that->vars[$matches[1]]['id'], "'") . "'";
2883
                    } else {
2884
                        return $that->vars[$matches[1]]['id'];
2885
                    }
2886
                } else {
2887
                    Log::addErrorLog(sprintf('Undefined variable $%s in report', $matches[1]));
2888
2889
                    return '$' . $matches[1];
2890
                }
2891
            },
2892
            $expression
2893
        );
2894
    }
2895
}
2896