• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xemlock / htmlpurifier-html5 / 16898969866

12 Aug 2025 04:28AM UTC coverage: 99.276%. Remained the same
16898969866

Pull #87

github

web-flow
Bump actions/checkout from 4 to 5

Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #87: Bump actions/checkout from 4 to 5

1508 of 1519 relevant lines covered (99.28%)

3883.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.88
/library/HTMLPurifier/ChildDef/HTML5/Table.php
1
<?php
2

3
/**
4
 * HTML5 compliant definition for table contents.
5
 *
6
 * Content model: In this order: optionally a caption element, followed by zero or more
7
 * colgroup elements, followed optionally by a thead element, followed by either zero
8
 * or more tbody elements or one or more tr elements, followed optionally by a tfoot
9
 * element.
10
 *
11
 * @see https://html.spec.whatwg.org/multipage/tables.html#the-table-element
12
 */
13
class HTMLPurifier_ChildDef_HTML5_Table extends HTMLPurifier_ChildDef
14
{
15
    /**
16
     * @type bool
17
     */
18
    public $allow_empty = false;
19

20
    /**
21
     * @type string
22
     */
23
    public $type = 'table';
24

25
    /**
26
     * @type array
27
     */
28
    public $elements = array(
29
        'tr' => true,
30
        'tbody' => true,
31
        'thead' => true,
32
        'tfoot' => true,
33
        'caption' => true,
34
        'colgroup' => true,
35
        'col' => true,
36
    );
37

38
    /**
39
     * @param array $children
40
     * @param HTMLPurifier_Config $config
41
     * @param HTMLPurifier_Context $context
42
     * @return array
43
     */
44
    public function validateChildren($children, $config, $context)
45
    {
46
        if (empty($children)) {
336✔
47
            // Table children are optional
48
            return $children;
24✔
49
        }
50

51
        // At most one of each of these elements is allowed in a table
52
        $caption = array();
312✔
53
        $thead = array();
312✔
54
        $tfoot = array();
312✔
55

56
        // and as many of these as you want
57
        $cols = array(); // <col>, <colgroup> and following whitespace nodes
312✔
58
        $content = array(); // <tr> and <tbody>
312✔
59

60
        // Whitespace accumulators
61
        $initial_ws = array();
312✔
62
        $after_caption_ws = array();
312✔
63
        $after_thead_ws = array();
312✔
64
        $after_tfoot_ws = array();
312✔
65

66
        // Essentially, we have two modes: thead/tfoot/tbody mode, and tr mode.
67
        // If we encounter a thead, tfoot or tbody, we are placed in the former
68
        // mode, and we *must* wrap any stray tr segments with a tbody. But if
69
        // we don't run into any of them, just having tr tags is OK.
70
        $tbody_mode = false;
312✔
71

72
        $ws_accum =& $initial_ws;
312✔
73

74
        foreach ($children as $node) {
312✔
75
            if ($node instanceof HTMLPurifier_Node_Comment) {
312✔
76
                $ws_accum[] = $node;
×
77
                continue;
×
78
            }
79

80
            switch ($node->name) {
312✔
81
                case 'tbody':
312✔
82
                    $tbody_mode = true;
108✔
83
                    // no break
84

85
                case 'tr':
292✔
86
                    $content[] = $node;
192✔
87
                    $ws_accum =& $content;
192✔
88
                    break;
192✔
89

90
                case 'caption':
216✔
91
                    // there can only be one caption!
92
                    if (count($caption)) {
132✔
93
                        break;
12✔
94
                    }
95
                    $caption[] = $node;
132✔
96
                    $ws_accum =& $after_caption_ws;
132✔
97
                    break;
132✔
98

99
                case 'thead':
120✔
100
                    $tbody_mode = true;
72✔
101
                    if (empty($thead)) {
72✔
102
                        $thead[] = $node;
72✔
103
                        $ws_accum =& $after_thead_ws;
72✔
104
                    } else {
12✔
105
                        // Oops, there's a second one! What should we do? Current behavior
106
                        // is to mutate the first and last entries into <tbody> tags, and
107
                        // then put into content, same as in HTMLPurifier_ChildDef_Table.
108
                        $node->name = 'tbody';
×
109
                        $content[] = $node;
×
110
                        $ws_accum =& $content;
×
111
                    }
112
                    break;
72✔
113

114
                case 'tfoot':
72✔
115
                    $tbody_mode = true;
48✔
116
                    if (empty($tfoot)) {
48✔
117
                        $tfoot[] = $node;
48✔
118
                        $ws_accum =& $after_tfoot_ws;
48✔
119
                    } else {
8✔
120
                        $node->name = 'tbody';
12✔
121
                        $content[] = $node;
12✔
122
                        $ws_accum =& $content;
12✔
123
                    }
124
                    break;
48✔
125

126
                case 'colgroup':
60✔
127
                case 'col':
60✔
128
                    $cols[] = $node;
24✔
129
                    $ws_accum =& $cols;
24✔
130
                    break;
24✔
131

132
                case '#PCDATA':
36✔
133
                    // How is whitespace handled? We treat is as sticky to the *end* of
134
                    // the previous element. So all the nonsense we have worked on is to
135
                    // keep things together.
136
                    if (!empty($node->is_whitespace)) {
36✔
137
                        $ws_accum[] = $node;
24✔
138
                    }
4✔
139
                    break;
82✔
140
            }
52✔
141
        }
52✔
142

143
        $ret = array_merge(
312✔
144
            $initial_ws,
312✔
145
            $caption,
312✔
146
            $after_caption_ws,
312✔
147
            $cols,
312✔
148
            $thead,
312✔
149
            $after_thead_ws,
312✔
150
            $tfoot,
312✔
151
            $after_tfoot_ws
260✔
152
        );
234✔
153

154
        if ($tbody_mode) {
312✔
155
            // At least one of thead/tbody/tfoot children is present, we have to
156
            // shuffle any <tr> children into <tbody>
157
            $current_tr_tbody = null;
144✔
158

159
            foreach ($content as $node) {
144✔
160
                switch ($node->name) {
120✔
161
                    case 'tbody':
120✔
162
                        $current_tr_tbody = null;
108✔
163
                        $ret[] = $node;
108✔
164
                        break;
108✔
165

166
                    case 'tr':
48✔
167
                        if ($current_tr_tbody === null) {
36✔
168
                            $current_tr_tbody = new HTMLPurifier_Node_Element('tbody');
36✔
169
                            $ret[] = $current_tr_tbody;
36✔
170
                        }
6✔
171
                        $current_tr_tbody->children[] = $node;
36✔
172
                        break;
36✔
173

174
                    case '#PCDATA':
12✔
175
                        // The only text nodes present here are whitespace
176
                        if ($current_tr_tbody === null) {
12✔
177
                            $ret[] = $node;
12✔
178
                        } else {
2✔
179
                            $current_tr_tbody->children[] = $node;
×
180
                        }
181
                        break;
34✔
182
                }
20✔
183
            }
24✔
184
        } else {
24✔
185
            $ret = array_merge($ret, $content);
180✔
186
        }
187

188
        return $ret;
312✔
189
    }
190
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc