ContentSets.php 5.5 KB

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