AttrCollections.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <?php
  2. /**
  3. * Defines common attribute collections that modules reference
  4. */
  5. class HTMLPurifier_AttrCollections
  6. {
  7. /**
  8. * Associative array of attribute collections, indexed by name.
  9. * @type array
  10. */
  11. public $info = array();
  12. /**
  13. * Performs all expansions on internal data for use by other inclusions
  14. * It also collects all attribute collection extensions from
  15. * modules
  16. * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
  17. * @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
  18. */
  19. public function __construct($attr_types, $modules)
  20. {
  21. $this->doConstruct($attr_types, $modules);
  22. }
  23. public function doConstruct($attr_types, $modules)
  24. {
  25. // load extensions from the modules
  26. foreach ($modules as $module) {
  27. foreach ($module->attr_collections as $coll_i => $coll) {
  28. if (!isset($this->info[$coll_i])) {
  29. $this->info[$coll_i] = array();
  30. }
  31. foreach ($coll as $attr_i => $attr) {
  32. if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
  33. // merge in includes
  34. $this->info[$coll_i][$attr_i] = array_merge(
  35. $this->info[$coll_i][$attr_i],
  36. $attr
  37. );
  38. continue;
  39. }
  40. $this->info[$coll_i][$attr_i] = $attr;
  41. }
  42. }
  43. }
  44. // perform internal expansions and inclusions
  45. foreach ($this->info as $name => $attr) {
  46. // merge attribute collections that include others
  47. $this->performInclusions($this->info[$name]);
  48. // replace string identifiers with actual attribute objects
  49. $this->expandIdentifiers($this->info[$name], $attr_types);
  50. }
  51. }
  52. /**
  53. * Takes a reference to an attribute associative array and performs
  54. * all inclusions specified by the zero index.
  55. * @param array &$attr Reference to attribute array
  56. */
  57. public function performInclusions(&$attr)
  58. {
  59. if (!isset($attr[0])) {
  60. return;
  61. }
  62. $merge = $attr[0];
  63. $seen = array(); // recursion guard
  64. // loop through all the inclusions
  65. for ($i = 0; isset($merge[$i]); $i++) {
  66. if (isset($seen[$merge[$i]])) {
  67. continue;
  68. }
  69. $seen[$merge[$i]] = true;
  70. // foreach attribute of the inclusion, copy it over
  71. if (!isset($this->info[$merge[$i]])) {
  72. continue;
  73. }
  74. foreach ($this->info[$merge[$i]] as $key => $value) {
  75. if (isset($attr[$key])) {
  76. continue;
  77. } // also catches more inclusions
  78. $attr[$key] = $value;
  79. }
  80. if (isset($this->info[$merge[$i]][0])) {
  81. // recursion
  82. $merge = array_merge($merge, $this->info[$merge[$i]][0]);
  83. }
  84. }
  85. unset($attr[0]);
  86. }
  87. /**
  88. * Expands all string identifiers in an attribute array by replacing
  89. * them with the appropriate values inside HTMLPurifier_AttrTypes
  90. * @param array &$attr Reference to attribute array
  91. * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
  92. */
  93. public function expandIdentifiers(&$attr, $attr_types)
  94. {
  95. // because foreach will process new elements we add, make sure we
  96. // skip duplicates
  97. $processed = array();
  98. foreach ($attr as $def_i => $def) {
  99. // skip inclusions
  100. if ($def_i === 0) {
  101. continue;
  102. }
  103. if (isset($processed[$def_i])) {
  104. continue;
  105. }
  106. // determine whether or not attribute is required
  107. if ($required = (strpos($def_i, '*') !== false)) {
  108. // rename the definition
  109. unset($attr[$def_i]);
  110. $def_i = trim($def_i, '*');
  111. $attr[$def_i] = $def;
  112. }
  113. $processed[$def_i] = true;
  114. // if we've already got a literal object, move on
  115. if (is_object($def)) {
  116. // preserve previous required
  117. $attr[$def_i]->required = ($required || $attr[$def_i]->required);
  118. continue;
  119. }
  120. if ($def === false) {
  121. unset($attr[$def_i]);
  122. continue;
  123. }
  124. if ($t = $attr_types->get($def)) {
  125. $attr[$def_i] = $t;
  126. $attr[$def_i]->required = $required;
  127. } else {
  128. unset($attr[$def_i]);
  129. }
  130. }
  131. }
  132. }
  133. // vim: et sw=4 sts=4