HTMLPurifier/HTMLModuleManager.php Quellcode

HTMLModuleManager.php
gehe zur Dokumentation dieser Datei
1 <?php
2 
4 {
5 
9  public $doctypes;
10 
15  public $doctype;
16 
20  public $attrTypes;
21 
27  public $modules = array();
28 
35  public $registeredModules = array();
36 
43  public $userModules = array();
44 
50  public $elementLookup = array();
51 
56  public $prefixes = array('HTMLPurifier_HTMLModule_');
57 
61  public $contentSets;
62 
67 
72  public $trusted = false;
73 
74  public function __construct()
75  {
76  // editable internal objects
77  $this->attrTypes = new HTMLPurifier_AttrTypes();
78  $this->doctypes = new HTMLPurifier_DoctypeRegistry();
79 
80  // setup basic modules
81  $common = array(
82  'CommonAttributes', 'Text', 'Hypertext', 'List',
83  'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
84  'StyleAttribute',
85  // Unsafe:
86  'Scripting', 'Object', 'Forms',
87  // Sorta legacy, but present in strict:
88  'Name',
89  );
90  $transitional = array('Legacy', 'Target', 'Iframe');
91  $xml = array('XMLCommonAttributes');
92  $non_xml = array('NonXMLCommonAttributes');
93 
94  // setup basic doctypes
95  $this->doctypes->register(
96  'HTML 4.01 Transitional',
97  false,
98  array_merge($common, $transitional, $non_xml),
99  array('Tidy_Transitional', 'Tidy_Proprietary'),
100  array(),
101  '-//W3C//DTD HTML 4.01 Transitional//EN',
102  'http://www.w3.org/TR/html4/loose.dtd'
103  );
104 
105  $this->doctypes->register(
106  'HTML 4.01 Strict',
107  false,
108  array_merge($common, $non_xml),
109  array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
110  array(),
111  '-//W3C//DTD HTML 4.01//EN',
112  'http://www.w3.org/TR/html4/strict.dtd'
113  );
114 
115  $this->doctypes->register(
116  'XHTML 1.0 Transitional',
117  true,
118  array_merge($common, $transitional, $xml, $non_xml),
119  array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'),
120  array(),
121  '-//W3C//DTD XHTML 1.0 Transitional//EN',
122  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
123  );
124 
125  $this->doctypes->register(
126  'XHTML 1.0 Strict',
127  true,
128  array_merge($common, $xml, $non_xml),
129  array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
130  array(),
131  '-//W3C//DTD XHTML 1.0 Strict//EN',
132  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
133  );
134 
135  $this->doctypes->register(
136  'XHTML 1.1',
137  true,
138  // Iframe is a real XHTML 1.1 module, despite being
139  // "transitional"!
140  array_merge($common, $xml, array('Ruby', 'Iframe')),
141  array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1
142  array(),
143  '-//W3C//DTD XHTML 1.1//EN',
144  'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
145  );
146 
147  }
148 
170  public function registerModule($module, $overload = false)
171  {
172  if (is_string($module)) {
173  // attempt to load the module
174  $original_module = $module;
175  $ok = false;
176  foreach ($this->prefixes as $prefix) {
177  $module = $prefix . $original_module;
178  if (class_exists($module)) {
179  $ok = true;
180  break;
181  }
182  }
183  if (!$ok) {
184  $module = $original_module;
185  if (!class_exists($module)) {
186  trigger_error(
187  $original_module . ' module does not exist',
188  E_USER_ERROR
189  );
190  return;
191  }
192  }
193  $module = new $module();
194  }
195  if (empty($module->name)) {
196  trigger_error('Module instance of ' . get_class($module) . ' must have name');
197  return;
198  }
199  if (!$overload && isset($this->registeredModules[$module->name])) {
200  trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING);
201  }
202  $this->registeredModules[$module->name] = $module;
203  }
204 
209  public function addModule($module)
210  {
211  $this->registerModule($module);
212  if (is_object($module)) {
213  $module = $module->name;
214  }
215  $this->userModules[] = $module;
216  }
217 
222  public function addPrefix($prefix)
223  {
224  $this->prefixes[] = $prefix;
225  }
226 
232  public function setup($config)
233  {
234  $this->trusted = $config->get('HTML.Trusted');
235 
236  // generate
237  $this->doctype = $this->doctypes->make($config);
238  $modules = $this->doctype->modules;
239 
240  // take out the default modules that aren't allowed
241  $lookup = $config->get('HTML.AllowedModules');
242  $special_cases = $config->get('HTML.CoreModules');
243 
244  if (is_array($lookup)) {
245  foreach ($modules as $k => $m) {
246  if (isset($special_cases[$m])) {
247  continue;
248  }
249  if (!isset($lookup[$m])) {
250  unset($modules[$k]);
251  }
252  }
253  }
254 
255  // custom modules
256  if ($config->get('HTML.Proprietary')) {
257  $modules[] = 'Proprietary';
258  }
259  if ($config->get('HTML.SafeObject')) {
260  $modules[] = 'SafeObject';
261  }
262  if ($config->get('HTML.SafeEmbed')) {
263  $modules[] = 'SafeEmbed';
264  }
265  if ($config->get('HTML.SafeScripting') !== array()) {
266  $modules[] = 'SafeScripting';
267  }
268  if ($config->get('HTML.Nofollow')) {
269  $modules[] = 'Nofollow';
270  }
271  if ($config->get('HTML.TargetBlank')) {
272  $modules[] = 'TargetBlank';
273  }
274 
275  // merge in custom modules
276  $modules = array_merge($modules, $this->userModules);
277 
278  foreach ($modules as $module) {
279  $this->processModule($module);
280  $this->modules[$module]->setup($config);
281  }
282 
283  foreach ($this->doctype->tidyModules as $module) {
284  $this->processModule($module);
285  $this->modules[$module]->setup($config);
286  }
287 
288  // prepare any injectors
289  foreach ($this->modules as $module) {
290  $n = array();
291  foreach ($module->info_injector as $injector) {
292  if (!is_object($injector)) {
293  $class = "HTMLPurifier_Injector_$injector";
294  $injector = new $class;
295  }
296  $n[$injector->name] = $injector;
297  }
298  $module->info_injector = $n;
299  }
300 
301  // setup lookup table based on all valid modules
302  foreach ($this->modules as $module) {
303  foreach ($module->info as $name => $def) {
304  if (!isset($this->elementLookup[$name])) {
305  $this->elementLookup[$name] = array();
306  }
307  $this->elementLookup[$name][] = $module->name;
308  }
309  }
310 
311  // note the different choice
312  $this->contentSets = new HTMLPurifier_ContentSets(
313  // content set assembly deals with all possible modules,
314  // not just ones deemed to be "safe"
315  $this->modules
316  );
317  $this->attrCollections = new HTMLPurifier_AttrCollections(
318  $this->attrTypes,
319  // there is no way to directly disable a global attribute,
320  // but using AllowedAttributes or simply not including
321  // the module in your custom doctype should be sufficient
322  $this->modules
323  );
324  }
325 
330  public function processModule($module)
331  {
332  if (!isset($this->registeredModules[$module]) || is_object($module)) {
333  $this->registerModule($module);
334  }
335  $this->modules[$module] = $this->registeredModules[$module];
336  }
337 
342  public function getElements()
343  {
344  $elements = array();
345  foreach ($this->modules as $module) {
346  if (!$this->trusted && !$module->safe) {
347  continue;
348  }
349  foreach ($module->info as $name => $v) {
350  if (isset($elements[$name])) {
351  continue;
352  }
353  $elements[$name] = $this->getElement($name);
354  }
355  }
356 
357  // remove dud elements, this happens when an element that
358  // appeared to be safe actually wasn't
359  foreach ($elements as $n => $v) {
360  if ($v === false) {
361  unset($elements[$n]);
362  }
363  }
364 
365  return $elements;
366 
367  }
368 
379  public function getElement($name, $trusted = null)
380  {
381  if (!isset($this->elementLookup[$name])) {
382  return false;
383  }
384 
385  // setup global state variables
386  $def = false;
387  if ($trusted === null) {
389  }
390 
391  // iterate through each module that has registered itself to this
392  // element
393  foreach ($this->elementLookup[$name] as $module_name) {
394  $module = $this->modules[$module_name];
395 
396  // refuse to create/merge from a module that is deemed unsafe--
397  // pretend the module doesn't exist--when trusted mode is not on.
398  if (!$trusted && !$module->safe) {
399  continue;
400  }
401 
402  // clone is used because, ideally speaking, the original
403  // definition should not be modified. Usually, this will
404  // make no difference, but for consistency's sake
405  $new_def = clone $module->info[$name];
406 
407  if (!$def && $new_def->standalone) {
408  $def = $new_def;
409  } elseif ($def) {
410  // This will occur even if $new_def is standalone. In practice,
411  // this will usually result in a full replacement.
412  $def->mergeIn($new_def);
413  } else {
414  // :TODO:
415  // non-standalone definitions that don't have a standalone
416  // to merge into could be deferred to the end
417  // HOWEVER, it is perfectly valid for a non-standalone
418  // definition to lack a standalone definition, even
419  // after all processing: this allows us to safely
420  // specify extra attributes for elements that may not be
421  // enabled all in one place. In particular, this might
422  // be the case for trusted elements. WARNING: care must
423  // be taken that the /extra/ definitions are all safe.
424  continue;
425  }
426 
427  // attribute value expansions
428  $this->attrCollections->performInclusions($def->attr);
429  $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes);
430 
431  // descendants_are_inline, for ChildDef_Chameleon
432  if (is_string($def->content_model) &&
433  strpos($def->content_model, 'Inline') !== false) {
434  if ($name != 'del' && $name != 'ins') {
435  // this is for you, ins/del
436  $def->descendants_are_inline = true;
437  }
438  }
439 
440  $this->contentSets->generateChildDef($def, $module);
441  }
442 
443  // This can occur if there is a blank definition, but no base to
444  // mix it in with
445  if (!$def) {
446  return false;
447  }
448 
449  // add information on required attributes
450  foreach ($def->attr as $attr_name => $attr_def) {
451  if ($attr_def->required) {
452  $def->required_attr[] = $attr_name;
453  }
454  }
455  return $def;
456  }
457 }
458 
459 // vim: et sw=4 sts=4




Korrekturen, Hinweise und Ergänzungen

Bitte scheuen Sie sich nicht und melden Sie, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Support Forum wählen. Es ist eine kostenlose Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden kommentarlos entfernt.