[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/lib/htmlpurifier/HTMLPurifier/ -> ConfigSchema.php (source)

   1  <?php
   2  
   3  require_once 'HTMLPurifier/Error.php';
   4  require_once 'HTMLPurifier/ConfigDef.php';
   5  require_once 'HTMLPurifier/ConfigDef/Namespace.php';
   6  require_once 'HTMLPurifier/ConfigDef/Directive.php';
   7  require_once 'HTMLPurifier/ConfigDef/DirectiveAlias.php';
   8  
   9  if (!defined('HTMLPURIFIER_SCHEMA_STRICT')) define('HTMLPURIFIER_SCHEMA_STRICT', false);
  10  
  11  /**
  12   * Configuration definition, defines directives and their defaults.
  13   * @note If you update this, please update Printer_ConfigForm
  14   * @todo The ability to define things multiple times is confusing and should
  15   *       be factored out to its own function named registerDependency() or 
  16   *       addNote(), where only the namespace.name and an extra descriptions
  17   *       documenting the nature of the dependency are needed.  Since it's
  18   *       possible that the dependency is registered before the configuration
  19   *       is defined, deferring it to some sort of cache until it actually
  20   *       gets defined would be wise, keeping it opaque until it does get
  21   *       defined. We could add a finalize() method which would cause it to
  22   *       error out if we get a dangling dependency.  It's difficult, however,
  23   *       to know whether or not it's a dependency, or a codependency, that is
  24   *       neither of them fully depends on it. Where does the configuration go
  25   *       then?  This could be partially resolved by allowing blanket definitions
  26   *       and then splitting them up into finer-grained versions, however, there
  27   *       might be implementation difficulties in ini files regarding order of
  28   *       execution.
  29   */
  30  class HTMLPurifier_ConfigSchema {
  31      
  32      /**
  33       * Defaults of the directives and namespaces.
  34       * @note This shares the exact same structure as HTMLPurifier_Config::$conf
  35       */
  36      var $defaults = array();
  37      
  38      /**
  39       * Definition of the directives.
  40       */
  41      var $info = array();
  42      
  43      /**
  44       * Definition of namespaces.
  45       */
  46      var $info_namespace = array();
  47      
  48      /**
  49       * Lookup table of allowed types.
  50       */
  51      var $types = array(
  52          'string'    => 'String',
  53          'istring'   => 'Case-insensitive string',
  54          'text'      => 'Text',
  55          'itext'      => 'Case-insensitive text',
  56          'int'       => 'Integer',
  57          'float'     => 'Float',
  58          'bool'      => 'Boolean',
  59          'lookup'    => 'Lookup array',
  60          'list'      => 'Array list',
  61          'hash'      => 'Associative array',
  62          'mixed'     => 'Mixed'
  63      );
  64      
  65      /**
  66       * Initializes the default namespaces.
  67       */
  68      function initialize() {
  69          $this->defineNamespace('Core', 'Core features that are always available.');
  70          $this->defineNamespace('Attr', 'Features regarding attribute validation.');
  71          $this->defineNamespace('URI', 'Features regarding Uniform Resource Identifiers.');
  72          $this->defineNamespace('HTML', 'Configuration regarding allowed HTML.');
  73          $this->defineNamespace('CSS', 'Configuration regarding allowed CSS.');
  74          $this->defineNamespace('AutoFormat', 'Configuration for activating auto-formatting functionality (also known as <code>Injector</code>s)');
  75          $this->defineNamespace('AutoFormatParam', 'Configuration for customizing auto-formatting functionality');
  76          $this->defineNamespace('Output', 'Configuration relating to the generation of (X)HTML.');
  77          $this->defineNamespace('Cache', 'Configuration for DefinitionCache and related subclasses.');
  78          $this->defineNamespace('Test', 'Developer testing configuration for our unit tests.');
  79      }
  80      
  81      /**
  82       * Retrieves an instance of the application-wide configuration definition.
  83       * @static
  84       */
  85      function &instance($prototype = null) {
  86          static $instance;
  87          if ($prototype !== null) {
  88              $instance = $prototype;
  89          } elseif ($instance === null || $prototype === true) {
  90              $instance = new HTMLPurifier_ConfigSchema();
  91              $instance->initialize();
  92          }
  93          return $instance;
  94      }
  95      
  96      /**
  97       * Defines a directive for configuration
  98       * @static
  99       * @warning Will fail of directive's namespace is defined
 100       * @param $namespace Namespace the directive is in
 101       * @param $name Key of directive
 102       * @param $default Default value of directive
 103       * @param $type Allowed type of the directive. See
 104       *      HTMLPurifier_DirectiveDef::$type for allowed values
 105       * @param $description Description of directive for documentation
 106       */
 107      function define($namespace, $name, $default, $type, $description) {
 108          $def =& HTMLPurifier_ConfigSchema::instance();
 109          
 110          // basic sanity checks
 111          if (HTMLPURIFIER_SCHEMA_STRICT) {
 112              if (!isset($def->info[$namespace])) {
 113                  trigger_error('Cannot define directive for undefined namespace',
 114                      E_USER_ERROR);
 115                  return;
 116              }
 117              if (!ctype_alnum($name)) {
 118                  trigger_error('Directive name must be alphanumeric',
 119                      E_USER_ERROR);
 120                  return;
 121              }
 122              if (empty($description)) {
 123                  trigger_error('Description must be non-empty',
 124                      E_USER_ERROR);
 125                  return;
 126              }
 127          }
 128          
 129          if (isset($def->info[$namespace][$name])) {
 130              // already defined
 131              if (
 132                  $def->info[$namespace][$name]->type !== $type ||
 133                  $def->defaults[$namespace][$name]   !== $default
 134              ) {
 135                  trigger_error('Inconsistent default or type, cannot redefine');
 136                  return;
 137              }
 138          } else {
 139              // needs defining
 140              
 141              // process modifiers (OPTIMIZE!)
 142              $type_values = explode('/', $type, 2);
 143              $type = $type_values[0];
 144              $modifier = isset($type_values[1]) ? $type_values[1] : false;
 145              $allow_null = ($modifier === 'null');
 146              
 147              if (HTMLPURIFIER_SCHEMA_STRICT) {
 148                  if (!isset($def->types[$type])) {
 149                      trigger_error('Invalid type for configuration directive',
 150                          E_USER_ERROR);
 151                      return;
 152                  }
 153                  $default = $def->validate($default, $type, $allow_null);
 154                  if ($def->isError($default)) {
 155                      trigger_error('Default value does not match directive type',
 156                          E_USER_ERROR);
 157                      return;
 158                  }
 159              }
 160              
 161              $def->info[$namespace][$name] =
 162                  new HTMLPurifier_ConfigDef_Directive();
 163              $def->info[$namespace][$name]->type = $type;
 164              $def->info[$namespace][$name]->allow_null = $allow_null;
 165              $def->defaults[$namespace][$name]   = $default;
 166          }
 167          if (!HTMLPURIFIER_SCHEMA_STRICT) return;
 168          $backtrace = debug_backtrace();
 169          $file = $def->mungeFilename($backtrace[0]['file']);
 170          $line = $backtrace[0]['line'];
 171          $def->info[$namespace][$name]->addDescription($file,$line,$description);
 172      }
 173      
 174      /**
 175       * Defines a namespace for directives to be put into.
 176       * @static
 177       * @param $namespace Namespace's name
 178       * @param $description Description of the namespace
 179       */
 180      function defineNamespace($namespace, $description) {
 181          $def =& HTMLPurifier_ConfigSchema::instance();
 182          if (HTMLPURIFIER_SCHEMA_STRICT) {
 183              if (isset($def->info[$namespace])) {
 184                  trigger_error('Cannot redefine namespace', E_USER_ERROR);
 185                  return;
 186              }
 187              if (!ctype_alnum($namespace)) {
 188                  trigger_error('Namespace name must be alphanumeric',
 189                      E_USER_ERROR);
 190                  return;
 191              }
 192              if (empty($description)) {
 193                  trigger_error('Description must be non-empty',
 194                      E_USER_ERROR);
 195                  return;
 196              }
 197          }
 198          $def->info[$namespace] = array();
 199          $def->info_namespace[$namespace] = new HTMLPurifier_ConfigDef_Namespace();
 200          $def->info_namespace[$namespace]->description = $description;
 201          $def->defaults[$namespace] = array();
 202      }
 203      
 204      /**
 205       * Defines a directive value alias.
 206       * 
 207       * Directive value aliases are convenient for developers because it lets
 208       * them set a directive to several values and get the same result.
 209       * @static
 210       * @param $namespace Directive's namespace
 211       * @param $name Name of Directive
 212       * @param $alias Name of aliased value
 213       * @param $real Value aliased value will be converted into
 214       */
 215      function defineValueAliases($namespace, $name, $aliases) {
 216          $def =& HTMLPurifier_ConfigSchema::instance();
 217          if (HTMLPURIFIER_SCHEMA_STRICT && !isset($def->info[$namespace][$name])) {
 218              trigger_error('Cannot set value alias for non-existant directive',
 219                  E_USER_ERROR);
 220              return;
 221          }
 222          foreach ($aliases as $alias => $real) {
 223              if (HTMLPURIFIER_SCHEMA_STRICT) {
 224                  if (!$def->info[$namespace][$name] !== true &&
 225                      !isset($def->info[$namespace][$name]->allowed[$real])
 226                  ) {
 227                      trigger_error('Cannot define alias to value that is not allowed',
 228                          E_USER_ERROR);
 229                      return;
 230                  }
 231                  if (isset($def->info[$namespace][$name]->allowed[$alias])) {
 232                      trigger_error('Cannot define alias over allowed value',
 233                          E_USER_ERROR);
 234                      return;
 235                  }
 236              }
 237              $def->info[$namespace][$name]->aliases[$alias] = $real;
 238          }
 239      }
 240      
 241      /**
 242       * Defines a set of allowed values for a directive.
 243       * @static
 244       * @param $namespace Namespace of directive
 245       * @param $name Name of directive
 246       * @param $allowed_values Arraylist of allowed values
 247       */
 248      function defineAllowedValues($namespace, $name, $allowed_values) {
 249          $def =& HTMLPurifier_ConfigSchema::instance();
 250          if (HTMLPURIFIER_SCHEMA_STRICT && !isset($def->info[$namespace][$name])) {
 251              trigger_error('Cannot define allowed values for undefined directive',
 252                  E_USER_ERROR);
 253              return;
 254          }
 255          $directive =& $def->info[$namespace][$name];
 256          $type = $directive->type;
 257          if (HTMLPURIFIER_SCHEMA_STRICT && $type != 'string' && $type != 'istring') {
 258              trigger_error('Cannot define allowed values for directive whose type is not string',
 259                  E_USER_ERROR);
 260              return;
 261          }
 262          if ($directive->allowed === true) {
 263              $directive->allowed = array();
 264          }
 265          foreach ($allowed_values as $value) {
 266              $directive->allowed[$value] = true;
 267          }
 268          if (
 269              HTMLPURIFIER_SCHEMA_STRICT &&
 270              $def->defaults[$namespace][$name] !== null &&
 271              !isset($directive->allowed[$def->defaults[$namespace][$name]])
 272          ) {
 273              trigger_error('Default value must be in allowed range of variables',
 274                  E_USER_ERROR);
 275              $directive->allowed = true; // undo undo!
 276              return;
 277          }
 278      }
 279      
 280      /**
 281       * Defines a directive alias for backwards compatibility
 282       * @static
 283       * @param $namespace
 284       * @param $name Directive that will be aliased
 285       * @param $new_namespace
 286       * @param $new_name Directive that the alias will be to
 287       */
 288      function defineAlias($namespace, $name, $new_namespace, $new_name) {
 289          $def =& HTMLPurifier_ConfigSchema::instance();
 290          if (HTMLPURIFIER_SCHEMA_STRICT) {
 291              if (!isset($def->info[$namespace])) {
 292                  trigger_error('Cannot define directive alias in undefined namespace',
 293                      E_USER_ERROR);
 294                  return;
 295              }
 296              if (!ctype_alnum($name)) {
 297                  trigger_error('Directive name must be alphanumeric',
 298                      E_USER_ERROR);
 299                  return;
 300              }
 301              if (isset($def->info[$namespace][$name])) {
 302                  trigger_error('Cannot define alias over directive',
 303                      E_USER_ERROR);
 304                  return;
 305              }
 306              if (!isset($def->info[$new_namespace][$new_name])) {
 307                  trigger_error('Cannot define alias to undefined directive',
 308                      E_USER_ERROR);
 309                  return;
 310              }
 311              if ($def->info[$new_namespace][$new_name]->class == 'alias') {
 312                  trigger_error('Cannot define alias to alias',
 313                      E_USER_ERROR);
 314                  return;
 315              }
 316          }
 317          $def->info[$namespace][$name] =
 318              new HTMLPurifier_ConfigDef_DirectiveAlias(
 319                  $new_namespace, $new_name);
 320          $def->info[$new_namespace][$new_name]->directiveAliases[] = "$namespace.$name";
 321      }
 322      
 323      /**
 324       * Validate a variable according to type. Return null if invalid.
 325       */
 326      function validate($var, $type, $allow_null = false) {
 327          if (!isset($this->types[$type])) {
 328              trigger_error('Invalid type', E_USER_ERROR);
 329              return;
 330          }
 331          if ($allow_null && $var === null) return null;
 332          switch ($type) {
 333              case 'mixed':
 334                  //if (is_string($var)) $var = unserialize($var);
 335                  return $var;
 336              case 'istring':
 337              case 'string':
 338              case 'text': // no difference, just is longer/multiple line string
 339              case 'itext':
 340                  if (!is_string($var)) break;
 341                  if ($type === 'istring' || $type === 'itext') $var = strtolower($var);
 342                  return $var;
 343              case 'int':
 344                  if (is_string($var) && ctype_digit($var)) $var = (int) $var;
 345                  elseif (!is_int($var)) break;
 346                  return $var;
 347              case 'float':
 348                  if (is_string($var) && is_numeric($var)) $var = (float) $var;
 349                  elseif (!is_float($var)) break;
 350                  return $var;
 351              case 'bool':
 352                  if (is_int($var) && ($var === 0 || $var === 1)) {
 353                      $var = (bool) $var;
 354                  } elseif (is_string($var)) {
 355                      if ($var == 'on' || $var == 'true' || $var == '1') {
 356                          $var = true;
 357                      } elseif ($var == 'off' || $var == 'false' || $var == '0') {
 358                          $var = false;
 359                      } else {
 360                          break;
 361                      }
 362                  } elseif (!is_bool($var)) break;
 363                  return $var;
 364              case 'list':
 365              case 'hash':
 366              case 'lookup':
 367                  if (is_string($var)) {
 368                      // special case: technically, this is an array with
 369                      // a single empty string item, but having an empty
 370                      // array is more intuitive
 371                      if ($var == '') return array();
 372                      if (strpos($var, "\n") === false && strpos($var, "\r") === false) {
 373                          // simplistic string to array method that only works
 374                          // for simple lists of tag names or alphanumeric characters
 375                          $var = explode(',',$var);
 376                      } else {
 377                          $var = preg_split('/(,|[\n\r]+)/', $var);
 378                      }
 379                      // remove spaces
 380                      foreach ($var as $i => $j) $var[$i] = trim($j);
 381                      if ($type === 'hash') {
 382                          // key:value,key2:value2
 383                          $nvar = array();
 384                          foreach ($var as $keypair) {
 385                              $c = explode(':', $keypair, 2);
 386                              if (!isset($c[1])) continue;
 387                              $nvar[$c[0]] = $c[1];
 388                          }
 389                          $var = $nvar;
 390                      }
 391                  }
 392                  if (!is_array($var)) break;
 393                  $keys = array_keys($var);
 394                  if ($keys === array_keys($keys)) {
 395                      if ($type == 'list') return $var;
 396                      elseif ($type == 'lookup') {
 397                          $new = array();
 398                          foreach ($var as $key) {
 399                              $new[$key] = true;
 400                          }
 401                          return $new;
 402                      } else break;
 403                  }
 404                  if ($type === 'lookup') {
 405                      foreach ($var as $key => $value) {
 406                          $var[$key] = true;
 407                      }
 408                  }
 409                  return $var;
 410          }
 411          $error = new HTMLPurifier_Error();
 412          return $error;
 413      }
 414      
 415      /**
 416       * Takes an absolute path and munges it into a more manageable relative path
 417       */
 418      function mungeFilename($filename) {
 419          if (!HTMLPURIFIER_SCHEMA_STRICT) return $filename;
 420          $offset = strrpos($filename, 'HTMLPurifier');
 421          $filename = substr($filename, $offset);
 422          $filename = str_replace('\\', '/', $filename);
 423          return $filename;
 424      }
 425      
 426      /**
 427       * Checks if var is an HTMLPurifier_Error object
 428       */
 429      function isError($var) {
 430          if (!is_object($var)) return false;
 431          if (!is_a($var, 'HTMLPurifier_Error')) return false;
 432          return true;
 433      }
 434  }
 435  
 436  


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7