ReportHtmlTextbox   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 250
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 143
dl 0
loc 250
rs 8.64
c 0
b 0
f 0
wmc 47

1 Method

Rating   Name   Duplication   Size   Complexity  
F render() 0 241 47

How to fix   Complexity   

Complex Class

Complex classes like ReportHtmlTextbox 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 ReportHtmlTextbox, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2025 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Report;
21
22
use function abs;
23
use function count;
24
use function is_object;
25
use function ksort;
26
use function str_replace;
27
use function trim;
28
29
class ReportHtmlTextbox extends ReportBaseTextbox
30
{
31
    /**
32
     * Render the elements.
33
     *
34
     * @param HtmlRenderer $renderer
35
     *
36
     * @return void
37
     */
38
    public function render($renderer): void
39
    {
40
        // checkFootnote
41
        $newelements      = [];
42
        $lastelement      = [];
43
        $footnote_element = [];
44
        // Element counter
45
        $cE = count($this->elements);
46
        //-- collapse duplicate elements
47
        for ($i = 0; $i < $cE; $i++) {
48
            $element = $this->elements[$i];
49
            if ($element instanceof ReportBaseElement) {
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Report\ReportBaseElement was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
50
                if ($element instanceof ReportBaseText) {
51
                    if (!empty($footnote_element)) {
52
                        ksort($footnote_element);
53
                        foreach ($footnote_element as $links) {
54
                            $newelements[] = $links;
55
                        }
56
                        $footnote_element = [];
57
                    }
58
                    if (empty($lastelement)) {
59
                        $lastelement = $element;
60
                    } elseif ($element->getStyleName() === $lastelement->getStyleName()) {
61
                        // Checking if the Text has the same style
62
                        $lastelement->addText(str_replace("\n", '<br>', $element->getValue()));
63
                    } else {
64
                        $newelements[] = $lastelement;
65
                        $lastelement   = $element;
66
                    }
67
                } elseif ($element instanceof ReportHtmlFootnote) {
68
                    // Check if the Footnote has been set with it’s link number
69
                    $renderer->checkFootnote($element);
70
                    // Save first the last element if any
71
                    if (!empty($lastelement)) {
72
                        $newelements[] = $lastelement;
73
                        $lastelement   = [];
74
                    }
75
                    // Save the Footnote with it’s link number as key for sorting later
76
                    $footnote_element[$element->num] = $element;
77
                } elseif (trim($element->getValue()) !== '') {
78
                    // Do not keep empty footnotes
79
                    if (!empty($footnote_element)) {
80
                        ksort($footnote_element);
81
                        foreach ($footnote_element as $links) {
82
                            $newelements[] = $links;
83
                        }
84
                        $footnote_element = [];
85
                    }
86
                    if (!empty($lastelement)) {
87
                        $newelements[] = $lastelement;
88
                        $lastelement   = [];
89
                    }
90
                    $newelements[] = $element;
91
                }
92
            } else {
93
                if (!empty($lastelement)) {
94
                    $newelements[] = $lastelement;
95
                    $lastelement   = [];
96
                }
97
                if (!empty($footnote_element)) {
98
                    ksort($footnote_element);
99
                    foreach ($footnote_element as $links) {
100
                        $newelements[] = $links;
101
                    }
102
                    $footnote_element = [];
103
                }
104
                $newelements[] = $element;
105
            }
106
        }
107
        if (!empty($lastelement)) {
108
            $newelements[] = $lastelement;
109
        }
110
        if (!empty($footnote_element)) {
111
            ksort($footnote_element);
112
            foreach ($footnote_element as $links) {
113
                $newelements[] = $links;
114
            }
115
        }
116
        $this->elements = $newelements;
0 ignored issues
show
Documentation Bug introduced by
$newelements is of type array<mixed,array|mixed>, but the property $elements was declared to be of type array<mixed,Fisharebest\...portBaseElement|string>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof 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 given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
117
        unset($footnote_element, $lastelement, $newelements);
118
119
        $cP = 0; // Class Padding
120
121
        // Used with line breaks and cell height calculation within this box only
122
        $renderer->largestFontHeight = 0;
123
124
        // If current position (left)
125
        if ($this->left === ReportBaseElement::CURRENT_POSITION) {
126
            $cX = $renderer->getX();
127
        } else {
128
            $cX = $this->left;
129
            $renderer->setX($cX);
130
        }
131
        // If current position (top)
132
        if ($this->top === ReportBaseElement::CURRENT_POSITION) {
133
            $this->top = $renderer->getY();
134
        } else {
135
            $renderer->setY($this->top);
136
        }
137
138
        // Check the width if set to page wide OR set by xml to larger then page width (margin)
139
        if ($this->width === 0.0 || $this->width > $renderer->getRemainingWidth()) {
140
            $this->width = $renderer->getRemainingWidth();
141
        }
142
        // Setup the CellPadding
143
        if ($this->padding) {
144
            $cP = $renderer->cPadding;
145
        }
146
147
        // For padding, we have to use less wrap width
148
        $cW = $this->width - $cP * 2.0;
149
150
        //-- calculate the text box height
151
        // Number of lines, will be converted to height
152
        $cHT = 0;
153
        // Element height (except text)
154
        $eH = 0.0;
155
        // Footnote height (in points)
156
        $fH = 0;
157
        $w  = 0;
158
        //-- $lw is an array
159
        // 0 => last line width
160
        // 1 => 1 if text was wrapped, 0 if text did not wrap
161
        // 2 => number of LF
162
        $lw = [];
163
        // Element counter
164
        $cE = count($this->elements);
165
        for ($i = 0; $i < $cE; $i++) {
166
            if (is_object($this->elements[$i])) {
167
                $ew = $this->elements[$i]->setWrapWidth($cW - $w - 2, $cW);
168
                if ($ew === $cW) {
169
                    $w = 0;
170
                }
171
                $lw = $this->elements[$i]->getWidth($renderer);
172
                // Text is already gets the # LF
173
                $cHT += $lw[2];
174
                if ($lw[1] === 1) {
175
                    $w = $lw[0];
176
                } elseif ($lw[1] === 2) {
177
                    $w = 0;
178
                } else {
179
                    $w += $lw[0];
180
                }
181
                if ($w > $cW) {
182
                    $w = $lw[0];
183
                }
184
                // For anything else but text (images), get the height
185
                $eH += $this->elements[$i]->getHeight($renderer);
186
            } else {
187
                $fH += abs($renderer->getFootnotesHeight($cW));
188
            }
189
        }
190
191
        // Add up what’s the final height
192
        $cH = $this->height;
193
        // If any element exist
194
        if ($cE > 0) {
195
            // Check if this is text or some other element, like images
196
            if ($eH === 0.0) {
0 ignored issues
show
introduced by
The condition $eH === 0.0 is always true.
Loading history...
197
                // Number of LF but at least one line
198
                $cHT = ($cHT + 1) * $renderer->cellHeightRatio;
199
                // Calculate the cell height with the largest font size used
200
                $cHT *= $renderer->largestFontHeight;
201
                if ($cH < $cHT) {
202
                    $cH = $cHT;
203
                }
204
            } else {
205
                // This is any other element
206
                if ($cH < $eH) {
207
                    $cH = $eH;
208
                }
209
                // Add Footnote height to the rest of the height
210
                $cH += $fH;
211
            }
212
        }
213
214
        unset($lw, $cHT, $fH, $w);
215
216
        // Finally, check the last cells height
217
        if ($cH < $renderer->lastCellHeight) {
218
            $cH = $renderer->lastCellHeight;
219
        }
220
        // Update max Y in case of a pagebreak
221
        // We don't want to over write any images or other stuff
222
        $renderer->addMaxY($this->top + $cH);
223
224
        // Start to print HTML
225
        echo '<div style="position:absolute;top:', $this->top, 'pt;';
226
        // LTR (left) or RTL (right)
227
        echo $renderer->alignRTL, ':', $cX, 'pt;';
228
        // Background color
229
        if ($this->fill && $this->bgcolor !== '') {
230
            echo ' background-color:', $this->bgcolor, ';';
231
        }
232
        // Print padding only when it’s set
233
        if ($this->padding) {
234
            // Use Cell around padding to support RTL also
235
            echo 'padding:', $cP, 'pt;';
236
        }
237
        // Border setup
238
        if ($this->border) {
239
            echo ' border:solid black 1pt;';
240
            echo 'width:', $this->width - 1 - $cP * 2, 'pt;height:', $cH - 1, 'pt;';
241
        } else {
242
            echo 'width:', $this->width - $cP * 2, 'pt;height:', $cH, 'pt;';
243
        }
244
        echo '">';
245
246
        // Do a little "margin" trick before print
247
        // to get the correct current position => "."
248
        $cXT = $renderer->getX();
249
        $cYT = $renderer->getY();
250
        $renderer->setXy(0, 0);
251
252
        // Print the text elements
253
        foreach ($this->elements as $element) {
254
            if ($element instanceof ReportHtmlText) {
255
                $element->render($renderer, false);
256
            } elseif ($element instanceof ReportBaseElement) {
257
                $element->render($renderer);
258
            } elseif ($element === 'footnotetexts') {
259
                $renderer->footnotes();
260
            } elseif ($element === 'addpage') {
261
                $renderer->addPage();
262
            }
263
        }
264
        echo "</div>\n";
265
266
        // Reset "margins"
267
        $renderer->setXy($cXT, $cYT);
268
        // This will be mostly used to trick the multiple images last height
269
        if ($this->reseth) {
270
            $cH = 0;
271
        }
272
        // New line and some clean up
273
        if (!$this->newline) {
274
            $renderer->setXy($cX + $this->width, $this->top);
275
            $renderer->lastCellHeight = $cH;
276
        } else {
277
            $renderer->setXy(0, $this->top + $cH + $cP * 2);
278
            $renderer->lastCellHeight = 0;
279
        }
280
    }
281
}
282