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.
   1  <?php
   2  
   3  /**
   4   * Validates the attributes of a token. Doesn't manage required attributes
   5   * very well. The only reason we factored this out was because RemoveForeignElements
   6   * also needed it besides ValidateAttributes.
   7   */
   8  class HTMLPurifier_AttrValidator
   9  {
  10  
  11      /**
  12       * Validates the attributes of a token, mutating it as necessary.
  13       * that has valid tokens
  14       * @param HTMLPurifier_Token $token Token to validate.
  15       * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
  16       * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
  17       */
  18      public function validateToken($token, $config, $context)
  19      {
  20          $definition = $config->getHTMLDefinition();
  21          $e =& $context->get('ErrorCollector', true);
  22  
  23          // initialize IDAccumulator if necessary
  24          $ok =& $context->get('IDAccumulator', true);
  25          if (!$ok) {
  26              $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
  27              $context->register('IDAccumulator', $id_accumulator);
  28          }
  29  
  30          // initialize CurrentToken if necessary
  31          $current_token =& $context->get('CurrentToken', true);
  32          if (!$current_token) {
  33              $context->register('CurrentToken', $token);
  34          }
  35  
  36          if (!$token instanceof HTMLPurifier_Token_Start &&
  37              !$token instanceof HTMLPurifier_Token_Empty
  38          ) {
  39              return;
  40          }
  41  
  42          // create alias to global definition array, see also $defs
  43          // DEFINITION CALL
  44          $d_defs = $definition->info_global_attr;
  45  
  46          // don't update token until the very end, to ensure an atomic update
  47          $attr = $token->attr;
  48  
  49          // do global transformations (pre)
  50          // nothing currently utilizes this
  51          foreach ($definition->info_attr_transform_pre as $transform) {
  52              $attr = $transform->transform($o = $attr, $config, $context);
  53              if ($e) {
  54                  if ($attr != $o) {
  55                      $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  56                  }
  57              }
  58          }
  59  
  60          // do local transformations only applicable to this element (pre)
  61          // ex. <p align="right"> to <p style="text-align:right;">
  62          foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
  63              $attr = $transform->transform($o = $attr, $config, $context);
  64              if ($e) {
  65                  if ($attr != $o) {
  66                      $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
  67                  }
  68              }
  69          }
  70  
  71          // create alias to this element's attribute definition array, see
  72          // also $d_defs (global attribute definition array)
  73          // DEFINITION CALL
  74          $defs = $definition->info[$token->name]->attr;
  75  
  76          $attr_key = false;
  77          $context->register('CurrentAttr', $attr_key);
  78  
  79          // iterate through all the attribute keypairs
  80          // Watch out for name collisions: $key has previously been used
  81          foreach ($attr as $attr_key => $value) {
  82  
  83              // call the definition
  84              if (isset($defs[$attr_key])) {
  85                  // there is a local definition defined
  86                  if ($defs[$attr_key] === false) {
  87                      // We've explicitly been told not to allow this element.
  88                      // This is usually when there's a global definition
  89                      // that must be overridden.
  90                      // Theoretically speaking, we could have a
  91                      // AttrDef_DenyAll, but this is faster!
  92                      $result = false;
  93                  } else {
  94                      // validate according to the element's definition
  95                      $result = $defs[$attr_key]->validate(
  96                          $value,
  97                          $config,
  98                          $context
  99                      );
 100                  }
 101              } elseif (isset($d_defs[$attr_key])) {
 102                  // there is a global definition defined, validate according
 103                  // to the global definition
 104                  $result = $d_defs[$attr_key]->validate(
 105                      $value,
 106                      $config,
 107                      $context
 108                  );
 109              } else {
 110                  // system never heard of the attribute? DELETE!
 111                  $result = false;
 112              }
 113  
 114              // put the results into effect
 115              if ($result === false || $result === null) {
 116                  // this is a generic error message that should replaced
 117                  // with more specific ones when possible
 118                  if ($e) {
 119                      $e->send(E_ERROR, 'AttrValidator: Attribute removed');
 120                  }
 121  
 122                  // remove the attribute
 123                  unset($attr[$attr_key]);
 124              } elseif (is_string($result)) {
 125                  // generally, if a substitution is happening, there
 126                  // was some sort of implicit correction going on. We'll
 127                  // delegate it to the attribute classes to say exactly what.
 128  
 129                  // simple substitution
 130                  $attr[$attr_key] = $result;
 131              } else {
 132                  // nothing happens
 133              }
 134  
 135              // we'd also want slightly more complicated substitution
 136              // involving an array as the return value,
 137              // although we're not sure how colliding attributes would
 138              // resolve (certain ones would be completely overriden,
 139              // others would prepend themselves).
 140          }
 141  
 142          $context->destroy('CurrentAttr');
 143  
 144          // post transforms
 145  
 146          // global (error reporting untested)
 147          foreach ($definition->info_attr_transform_post as $transform) {
 148              $attr = $transform->transform($o = $attr, $config, $context);
 149              if ($e) {
 150                  if ($attr != $o) {
 151                      $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
 152                  }
 153              }
 154          }
 155  
 156          // local (error reporting untested)
 157          foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
 158              $attr = $transform->transform($o = $attr, $config, $context);
 159              if ($e) {
 160                  if ($attr != $o) {
 161                      $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
 162                  }
 163              }
 164          }
 165  
 166          $token->attr = $attr;
 167  
 168          // destroy CurrentToken if we made it ourselves
 169          if (!$current_token) {
 170              $context->destroy('CurrentToken');
 171          }
 172  
 173      }
 174  
 175  
 176  }
 177  
 178  // vim: et sw=4 sts=4