1: <?php
2:
3: /**
4: * @todo Unit test
5: */
6: class HTMLPurifier_ContentSets
7: {
8:
9: /**
10: * List of content set strings (pipe separators) indexed by name.
11: * @type array
12: */
13: public $info = array();
14:
15: /**
16: * List of content set lookups (element => true) indexed by name.
17: * @type array
18: * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
19: */
20: public $lookup = array();
21:
22: /**
23: * Synchronized list of defined content sets (keys of info).
24: * @type array
25: */
26: protected $keys = array();
27: /**
28: * Synchronized list of defined content values (values of info).
29: * @type array
30: */
31: protected $values = array();
32:
33: /**
34: * Merges in module's content sets, expands identifiers in the content
35: * sets and populates the keys, values and lookup member variables.
36: * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule
37: */
38: public function __construct($modules)
39: {
40: if (!is_array($modules)) {
41: $modules = array($modules);
42: }
43: // populate content_sets based on module hints
44: // sorry, no way of overloading
45: foreach ($modules as $module) {
46: foreach ($module->content_sets as $key => $value) {
47: $temp = $this->convertToLookup($value);
48: if (isset($this->lookup[$key])) {
49: // add it into the existing content set
50: $this->lookup[$key] = array_merge($this->lookup[$key], $temp);
51: } else {
52: $this->lookup[$key] = $temp;
53: }
54: }
55: }
56: $old_lookup = false;
57: while ($old_lookup !== $this->lookup) {
58: $old_lookup = $this->lookup;
59: foreach ($this->lookup as $i => $set) {
60: $add = array();
61: foreach ($set as $element => $x) {
62: if (isset($this->lookup[$element])) {
63: $add += $this->lookup[$element];
64: unset($this->lookup[$i][$element]);
65: }
66: }
67: $this->lookup[$i] += $add;
68: }
69: }
70:
71: foreach ($this->lookup as $key => $lookup) {
72: $this->info[$key] = implode(' | ', array_keys($lookup));
73: }
74: $this->keys = array_keys($this->info);
75: $this->values = array_values($this->info);
76: }
77:
78: /**
79: * Accepts a definition; generates and assigns a ChildDef for it
80: * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference
81: * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
82: */
83: public function generateChildDef(&$def, $module)
84: {
85: if (!empty($def->child)) { // already done!
86: return;
87: }
88: $content_model = $def->content_model;
89: if (is_string($content_model)) {
90: // Assume that $this->keys is alphanumeric
91: $def->content_model = preg_replace_callback(
92: '/\b(' . implode('|', $this->keys) . ')\b/',
93: array($this, 'generateChildDefCallback'),
94: $content_model
95: );
96: //$def->content_model = str_replace(
97: // $this->keys, $this->values, $content_model);
98: }
99: $def->child = $this->getChildDef($def, $module);
100: }
101:
102: public function generateChildDefCallback($matches)
103: {
104: return $this->info[$matches[0]];
105: }
106:
107: /**
108: * Instantiates a ChildDef based on content_model and content_model_type
109: * member variables in HTMLPurifier_ElementDef
110: * @note This will also defer to modules for custom HTMLPurifier_ChildDef
111: * subclasses that need content set expansion
112: * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted
113: * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
114: * @return HTMLPurifier_ChildDef corresponding to ElementDef
115: */
116: public function getChildDef($def, $module)
117: {
118: $value = $def->content_model;
119: if (is_object($value)) {
120: trigger_error(
121: 'Literal object child definitions should be stored in '.
122: 'ElementDef->child not ElementDef->content_model',
123: E_USER_NOTICE
124: );
125: return $value;
126: }
127: switch ($def->content_model_type) {
128: case 'required':
129: return new HTMLPurifier_ChildDef_Required($value);
130: case 'optional':
131: return new HTMLPurifier_ChildDef_Optional($value);
132: case 'empty':
133: return new HTMLPurifier_ChildDef_Empty();
134: case 'custom':
135: return new HTMLPurifier_ChildDef_Custom($value);
136: }
137: // defer to its module
138: $return = false;
139: if ($module->defines_child_def) { // save a func call
140: $return = $module->getChildDef($def);
141: }
142: if ($return !== false) {
143: return $return;
144: }
145: // error-out
146: trigger_error(
147: 'Could not determine which ChildDef class to instantiate',
148: E_USER_ERROR
149: );
150: return false;
151: }
152:
153: /**
154: * Converts a string list of elements separated by pipes into
155: * a lookup array.
156: * @param string $string List of elements
157: * @return array Lookup array of elements
158: */
159: protected function convertToLookup($string)
160: {
161: $array = explode('|', str_replace(' ', '', $string));
162: $ret = array();
163: foreach ($array as $k) {
164: $ret[$k] = true;
165: }
166: return $ret;
167: }
168: }
169:
170: // vim: et sw=4 sts=4
171: