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

xemlock / htmlpurifier-html5 / 13168072550

05 Feb 2025 10:52PM UTC coverage: 99.406%. Remained the same
13168072550

push

github

xemlock
Adjust tests for HTMLPurifier 4.18.0

Support for conditional comments was removed in HTMLPurifier 4.18.0,
see: https://github.com/ezyang/htmlpurifier/commit/4828fdf.

1507 of 1516 relevant lines covered (99.41%)

3215.37 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)) {
280✔
47
            // Table children are optional
48
            return $children;
20✔
49
        }
50

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

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

60
        // Whitespace accumulators
61
        $initial_ws = array();
260✔
62
        $after_caption_ws = array();
260✔
63
        $after_thead_ws = array();
260✔
64
        $after_tfoot_ws = array();
260✔
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;
260✔
71

72
        $ws_accum =& $initial_ws;
260✔
73

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

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

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

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

99
                case 'thead':
100✔
100
                    $tbody_mode = true;
60✔
101
                    if (empty($thead)) {
60✔
102
                        $thead[] = $node;
60✔
103
                        $ws_accum =& $after_thead_ws;
60✔
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;
60✔
113

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

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

132
                case '#PCDATA':
30✔
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)) {
30✔
137
                        $ws_accum[] = $node;
20✔
138
                    }
4✔
139
                    break;
76✔
140
            }
52✔
141
        }
52✔
142

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

154
        if ($tbody_mode) {
260✔
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;
120✔
158

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

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

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

188
        return $ret;
260✔
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