1<?php
2
3/**
4 * Definition that allows a set of elements, but disallows empty children.
5 */
6class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
7{
8    /**
9     * Lookup table of allowed elements.
10     * @type array
11     */
12    public $elements = array();
13
14    /**
15     * Whether or not the last passed node was all whitespace.
16     * @type bool
17     */
18    protected $whitespace = false;
19
20    /**
21     * @param array|string $elements List of allowed element names (lowercase).
22     */
23    public function __construct($elements)
24    {
25        if (is_string($elements)) {
26            $elements = str_replace(' ', '', $elements);
27            $elements = explode('|', $elements);
28        }
29        $keys = array_keys($elements);
30        if ($keys == array_keys($keys)) {
31            $elements = array_flip($elements);
32            foreach ($elements as $i => $x) {
33                $elements[$i] = true;
34                if (empty($i)) {
35                    unset($elements[$i]);
36                } // remove blank
37            }
38        }
39        $this->elements = $elements;
40    }
41
42    /**
43     * @type bool
44     */
45    public $allow_empty = false;
46
47    /**
48     * @type string
49     */
50    public $type = 'required';
51
52    /**
53     * @param array $children
54     * @param HTMLPurifier_Config $config
55     * @param HTMLPurifier_Context $context
56     * @return array
57     */
58    public function validateChildren($children, $config, $context)
59    {
60        // Flag for subclasses
61        $this->whitespace = false;
62
63        // if there are no tokens, delete parent node
64        if (empty($children)) {
65            return false;
66        }
67
68        // the new set of children
69        $result = array();
70
71        // whether or not parsed character data is allowed
72        // this controls whether or not we silently drop a tag
73        // or generate escaped HTML from it
74        $pcdata_allowed = isset($this->elements['#PCDATA']);
75
76        // a little sanity check to make sure it's not ALL whitespace
77        $all_whitespace = true;
78
79        $stack = array_reverse($children);
80        while (!empty($stack)) {
81            $node = array_pop($stack);
82            if (!empty($node->is_whitespace)) {
83                $result[] = $node;
84                continue;
85            }
86            $all_whitespace = false; // phew, we're not talking about whitespace
87
88            if (!isset($this->elements[$node->name])) {
89                // special case text
90                // XXX One of these ought to be redundant or something
91                if ($pcdata_allowed && $node instanceof HTMLPurifier_Node_Text) {
92                    $result[] = $node;
93                    continue;
94                }
95                // spill the child contents in
96                // ToDo: Make configurable
97                if ($node instanceof HTMLPurifier_Node_Element) {
98                    for ($i = count($node->children) - 1; $i >= 0; $i--) {
99                        $stack[] = $node->children[$i];
100                    }
101                    continue;
102                }
103                continue;
104            }
105            $result[] = $node;
106        }
107        if (empty($result)) {
108            return false;
109        }
110        if ($all_whitespace) {
111            $this->whitespace = true;
112            return false;
113        }
114        return $result;
115    }
116}
117
118// vim: et sw=4 sts=4
119