Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

   1  <?php
   2  
   3  /**
   4   * Structure that stores an HTML element definition. Used by
   5   * HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule.
   6   * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition.
   7   *       Please update that class too.
   8   * @warning If you add new properties to this class, you MUST update
   9   *          the mergeIn() method.
  10   */
  11  class HTMLPurifier_ElementDef
  12  {
  13      /**
  14       * Does the definition work by itself, or is it created solely
  15       * for the purpose of merging into another definition?
  16       * @type bool
  17       */
  18      public $standalone = true;
  19  
  20      /**
  21       * Associative array of attribute name to HTMLPurifier_AttrDef.
  22       * @type array
  23       * @note Before being processed by HTMLPurifier_AttrCollections
  24       *       when modules are finalized during
  25       *       HTMLPurifier_HTMLDefinition->setup(), this array may also
  26       *       contain an array at index 0 that indicates which attribute
  27       *       collections to load into the full array. It may also
  28       *       contain string indentifiers in lieu of HTMLPurifier_AttrDef,
  29       *       see HTMLPurifier_AttrTypes on how they are expanded during
  30       *       HTMLPurifier_HTMLDefinition->setup() processing.
  31       */
  32      public $attr = array();
  33  
  34      // XXX: Design note: currently, it's not possible to override
  35      // previously defined AttrTransforms without messing around with
  36      // the final generated config. This is by design; a previous version
  37      // used an associated list of attr_transform, but it was extremely
  38      // easy to accidentally override other attribute transforms by
  39      // forgetting to specify an index (and just using 0.)  While we
  40      // could check this by checking the index number and complaining,
  41      // there is a second problem which is that it is not at all easy to
  42      // tell when something is getting overridden. Combine this with a
  43      // codebase where this isn't really being used, and it's perfect for
  44      // nuking.
  45  
  46      /**
  47       * List of tags HTMLPurifier_AttrTransform to be done before validation.
  48       * @type array
  49       */
  50      public $attr_transform_pre = array();
  51  
  52      /**
  53       * List of tags HTMLPurifier_AttrTransform to be done after validation.
  54       * @type array
  55       */
  56      public $attr_transform_post = array();
  57  
  58      /**
  59       * HTMLPurifier_ChildDef of this tag.
  60       * @type HTMLPurifier_ChildDef
  61       */
  62      public $child;
  63  
  64      /**
  65       * Abstract string representation of internal ChildDef rules.
  66       * @see HTMLPurifier_ContentSets for how this is parsed and then transformed
  67       * into an HTMLPurifier_ChildDef.
  68       * @warning This is a temporary variable that is not available after
  69       *      being processed by HTMLDefinition
  70       * @type string
  71       */
  72      public $content_model;
  73  
  74      /**
  75       * Value of $child->type, used to determine which ChildDef to use,
  76       * used in combination with $content_model.
  77       * @warning This must be lowercase
  78       * @warning This is a temporary variable that is not available after
  79       *      being processed by HTMLDefinition
  80       * @type string
  81       */
  82      public $content_model_type;
  83  
  84      /**
  85       * Does the element have a content model (#PCDATA | Inline)*? This
  86       * is important for chameleon ins and del processing in
  87       * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
  88       * have to worry about this one.
  89       * @type bool
  90       */
  91      public $descendants_are_inline = false;
  92  
  93      /**
  94       * List of the names of required attributes this element has.
  95       * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement()
  96       * @type array
  97       */
  98      public $required_attr = array();
  99  
 100      /**
 101       * Lookup table of tags excluded from all descendants of this tag.
 102       * @type array
 103       * @note SGML permits exclusions for all descendants, but this is
 104       *       not possible with DTDs or XML Schemas. W3C has elected to
 105       *       use complicated compositions of content_models to simulate
 106       *       exclusion for children, but we go the simpler, SGML-style
 107       *       route of flat-out exclusions, which correctly apply to
 108       *       all descendants and not just children. Note that the XHTML
 109       *       Modularization Abstract Modules are blithely unaware of such
 110       *       distinctions.
 111       */
 112      public $excludes = array();
 113  
 114      /**
 115       * This tag is explicitly auto-closed by the following tags.
 116       * @type array
 117       */
 118      public $autoclose = array();
 119  
 120      /**
 121       * If a foreign element is found in this element, test if it is
 122       * allowed by this sub-element; if it is, instead of closing the
 123       * current element, place it inside this element.
 124       * @type string
 125       */
 126      public $wrap;
 127  
 128      /**
 129       * Whether or not this is a formatting element affected by the
 130       * "Active Formatting Elements" algorithm.
 131       * @type bool
 132       */
 133      public $formatting;
 134  
 135      /**
 136       * Low-level factory constructor for creating new standalone element defs
 137       */
 138      public static function create($content_model, $content_model_type, $attr)
 139      {
 140          $def = new HTMLPurifier_ElementDef();
 141          $def->content_model = $content_model;
 142          $def->content_model_type = $content_model_type;
 143          $def->attr = $attr;
 144          return $def;
 145      }
 146  
 147      /**
 148       * Merges the values of another element definition into this one.
 149       * Values from the new element def take precedence if a value is
 150       * not mergeable.
 151       * @param HTMLPurifier_ElementDef $def
 152       */
 153      public function mergeIn($def)
 154      {
 155          // later keys takes precedence
 156          foreach ($def->attr as $k => $v) {
 157              if ($k === 0) {
 158                  // merge in the includes
 159                  // sorry, no way to override an include
 160                  foreach ($v as $v2) {
 161                      $this->attr[0][] = $v2;
 162                  }
 163                  continue;
 164              }
 165              if ($v === false) {
 166                  if (isset($this->attr[$k])) {
 167                      unset($this->attr[$k]);
 168                  }
 169                  continue;
 170              }
 171              $this->attr[$k] = $v;
 172          }
 173          $this->_mergeAssocArray($this->excludes, $def->excludes);
 174          $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre);
 175          $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post);
 176  
 177          if (!empty($def->content_model)) {
 178              $this->content_model =
 179                  str_replace("#SUPER", (string)$this->content_model, $def->content_model);
 180              $this->child = false;
 181          }
 182          if (!empty($def->content_model_type)) {
 183              $this->content_model_type = $def->content_model_type;
 184              $this->child = false;
 185          }
 186          if (!is_null($def->child)) {
 187              $this->child = $def->child;
 188          }
 189          if (!is_null($def->formatting)) {
 190              $this->formatting = $def->formatting;
 191          }
 192          if ($def->descendants_are_inline) {
 193              $this->descendants_are_inline = $def->descendants_are_inline;
 194          }
 195      }
 196  
 197      /**
 198       * Merges one array into another, removes values which equal false
 199       * @param $a1 Array by reference that is merged into
 200       * @param $a2 Array that merges into $a1
 201       */
 202      private function _mergeAssocArray(&$a1, $a2)
 203      {
 204          foreach ($a2 as $k => $v) {
 205              if ($v === false) {
 206                  if (isset($a1[$k])) {
 207                      unset($a1[$k]);
 208                  }
 209                  continue;
 210              }
 211              $a1[$k] = $v;
 212          }
 213      }
 214  }
 215  
 216  // vim: et sw=4 sts=4