0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2024-11-09 15:28:40 +00:00

[3.1.0] De-crudify the ConfigSchema space; we're starting over again

- Optimize ConfigSchema by removing non-essential runtime data. We can probably optimize even more by collapsing object structures to arrays.
- Removed validation data from ConfigSchema; this will be reimplemented on Interchange
- Implement a sane Interchange composite hierarchy that doesn't use arrays
- Implement StringHash -> Interchange -> ConfigSchema, and rewrite maintenance file to account for this

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1615 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang 2008-03-22 03:55:59 +00:00
parent 93babf0a88
commit ec59062a9d
42 changed files with 440 additions and 1528 deletions

1
NEWS
View File

@ -73,6 +73,7 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
. HTMLPurifier_ConfigSchema->validate() deprecated in favor of . HTMLPurifier_ConfigSchema->validate() deprecated in favor of
HTMLPurifier_VarParser->parse() HTMLPurifier_VarParser->parse()
. Integers auto-cast into float type by VarParser. . Integers auto-cast into float type by VarParser.
. HTMLPURIFIER_STRICT
3.0.0, released 2008-01-06 3.0.0, released 2008-01-06
# HTML Purifier is PHP 5 only! The 2.1.x branch will be maintained # HTML Purifier is PHP 5 only! The 2.1.x branch will be maintained

View File

@ -127,22 +127,14 @@ require 'HTMLPurifier/ConfigDef/DirectiveAlias.php';
require 'HTMLPurifier/ConfigDef/Namespace.php'; require 'HTMLPurifier/ConfigDef/Namespace.php';
require 'HTMLPurifier/ConfigSchema/Exception.php'; require 'HTMLPurifier/ConfigSchema/Exception.php';
require 'HTMLPurifier/ConfigSchema/Interchange.php'; require 'HTMLPurifier/ConfigSchema/Interchange.php';
require 'HTMLPurifier/ConfigSchema/InterchangeBuilder.php';
require 'HTMLPurifier/ConfigSchema/InterchangeValidator.php'; require 'HTMLPurifier/ConfigSchema/InterchangeValidator.php';
require 'HTMLPurifier/ConfigSchema/StringHash.php'; require 'HTMLPurifier/ConfigSchema/StringHash.php';
require 'HTMLPurifier/ConfigSchema/StringHashAdapter.php';
require 'HTMLPurifier/ConfigSchema/StringHashParser.php'; require 'HTMLPurifier/ConfigSchema/StringHashParser.php';
require 'HTMLPurifier/ConfigSchema/StringHashReverseAdapter.php'; require 'HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php';
require 'HTMLPurifier/ConfigSchema/Validator.php'; require 'HTMLPurifier/ConfigSchema/Interchange/Directive.php';
require 'HTMLPurifier/ConfigSchema/Validator/Alnum.php'; require 'HTMLPurifier/ConfigSchema/Interchange/Id.php';
require 'HTMLPurifier/ConfigSchema/Validator/Composite.php'; require 'HTMLPurifier/ConfigSchema/Interchange/Namespace.php';
require 'HTMLPurifier/ConfigSchema/Validator/Exists.php';
require 'HTMLPurifier/ConfigSchema/Validator/If.php';
require 'HTMLPurifier/ConfigSchema/Validator/NamespaceExists.php';
require 'HTMLPurifier/ConfigSchema/Validator/Or.php';
require 'HTMLPurifier/ConfigSchema/Validator/ParseDefault.php';
require 'HTMLPurifier/ConfigSchema/Validator/ParseId.php';
require 'HTMLPurifier/ConfigSchema/Validator/ParseType.php';
require 'HTMLPurifier/ConfigSchema/Validator/Unique.php';
require 'HTMLPurifier/DefinitionCache/Decorator.php'; require 'HTMLPurifier/DefinitionCache/Decorator.php';
require 'HTMLPurifier/DefinitionCache/Null.php'; require 'HTMLPurifier/DefinitionCache/Null.php';
require 'HTMLPurifier/DefinitionCache/Serializer.php'; require 'HTMLPurifier/DefinitionCache/Serializer.php';

View File

@ -11,13 +11,11 @@ class HTMLPurifier_ConfigDef_Directive extends HTMLPurifier_ConfigDef
public function __construct( public function __construct(
$type = null, $type = null,
$description = null,
$allow_null = null, $allow_null = null,
$allowed = null, $allowed = null,
$aliases = null $aliases = null
) { ) {
if ( $type !== null) $this->type = $type; if ( $type !== null) $this->type = $type;
if ($description !== null) $this->description = $description;
if ( $allow_null !== null) $this->allow_null = $allow_null; if ( $allow_null !== null) $this->allow_null = $allow_null;
if ( $allowed !== null) $this->allowed = $allowed; if ( $allowed !== null) $this->allowed = $allowed;
if ( $aliases !== null) $this->aliases = $aliases; if ( $aliases !== null) $this->aliases = $aliases;
@ -37,11 +35,6 @@ class HTMLPurifier_ConfigDef_Directive extends HTMLPurifier_ConfigDef
*/ */
public $type = 'mixed'; public $type = 'mixed';
/**
* Plaintext description of the configuration entity is.
*/
public $description = null;
/** /**
* Is null allowed? Has no effect for mixed type. * Is null allowed? Has no effect for mixed type.
* @bool * @bool

View File

@ -3,18 +3,8 @@
/** /**
* Structure object describing of a namespace * Structure object describing of a namespace
*/ */
class HTMLPurifier_ConfigDef_Namespace extends HTMLPurifier_ConfigDef { class HTMLPurifier_ConfigDef_Namespace extends HTMLPurifier_ConfigDef
{
public function HTMLPurifier_ConfigDef_Namespace($description = null) {
$this->description = $description;
}
public $class = 'namespace'; public $class = 'namespace';
/**
* String description of what kinds of directives go in this namespace.
*/
public $description;
} }

View File

@ -1,7 +1,5 @@
<?php <?php
if (!defined('HTMLPURIFIER_SCHEMA_STRICT')) define('HTMLPURIFIER_SCHEMA_STRICT', false);
/** /**
* Configuration definition, defines directives and their defaults. * Configuration definition, defines directives and their defaults.
*/ */
@ -18,11 +16,6 @@ class HTMLPurifier_ConfigSchema {
*/ */
public $info = array(); public $info = array();
/**
* Definition of namespaces.
*/
public $info_namespace = array();
/** /**
* Application-wide singleton * Application-wide singleton
*/ */
@ -33,23 +26,6 @@ class HTMLPurifier_ConfigSchema {
*/ */
protected $parser; protected $parser;
/**
* Lookup table of allowed types.
*/
public $types = array(
'string' => 'String',
'istring' => 'Case-insensitive string',
'text' => 'Text',
'itext' => 'Case-insensitive text',
'int' => 'Integer',
'float' => 'Float',
'bool' => 'Boolean',
'lookup' => 'Lookup array',
'list' => 'Array list',
'hash' => 'Associative array',
'mixed' => 'Mixed'
);
public function __construct() { public function __construct() {
$this->parser = new HTMLPurifier_VarParser_Flexible(); $this->parser = new HTMLPurifier_VarParser_Flexible();
} }
@ -73,114 +49,37 @@ class HTMLPurifier_ConfigSchema {
return HTMLPurifier_ConfigSchema::$singleton; return HTMLPurifier_ConfigSchema::$singleton;
} }
/**
* Throws an E_USER_NOTICE stating that a method is deprecated.
*/
private static function deprecated($method) {
trigger_error("Static HTMLPurifier_ConfigSchema::$method deprecated, use add*() method instead", E_USER_NOTICE);
}
/** @see HTMLPurifier_ConfigSchema->set() */
public static function define($namespace, $name, $default, $type, $description) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance();
$def->add($namespace, $name, $default, $type, $description);
}
/** /**
* Defines a directive for configuration * Defines a directive for configuration
* @warning Will fail of directive's namespace is defined * @warning Will fail of directive's namespace is defined.
* @warning This method's signature is slightly different from the legacy
* define() static method! Beware!
* @param $namespace Namespace the directive is in * @param $namespace Namespace the directive is in
* @param $name Key of directive * @param $name Key of directive
* @param $default Default value of directive * @param $default Default value of directive
* @param $type Allowed type of the directive. See * @param $type Allowed type of the directive. See
* HTMLPurifier_DirectiveDef::$type for allowed values * HTMLPurifier_DirectiveDef::$type for allowed values
* @param $description Description of directive for documentation * @param $allow_null Whether or not to allow null values
*/ */
public function add($namespace, $name, $default, $type, $description) { public function add($namespace, $name, $default, $type, $allow_null) {
// basic sanity checks $default = $this->parser->parse($default, $type, $allow_null);
if (HTMLPURIFIER_SCHEMA_STRICT) { $this->info[$namespace][$name] = new HTMLPurifier_ConfigDef_Directive();
if (!isset($this->info[$namespace])) { $this->info[$namespace][$name]->type = $type;
trigger_error('Cannot define directive for undefined namespace', $this->info[$namespace][$name]->allow_null = $allow_null;
E_USER_ERROR); $this->defaults[$namespace][$name] = $default;
return;
}
if (!ctype_alnum($name)) {
trigger_error('Directive name must be alphanumeric',
E_USER_ERROR);
return;
}
if (empty($description)) {
trigger_error('Description must be non-empty',
E_USER_ERROR);
return;
}
}
if (isset($this->info[$namespace][$name])) {
// already defined
trigger_error('Cannot redefine directive');
return;
} else {
// needs defining
// process modifiers (OPTIMIZE!)
$type_values = explode('/', $type, 2);
$type = $type_values[0];
$modifier = isset($type_values[1]) ? $type_values[1] : false;
$allow_null = ($modifier === 'null');
if (HTMLPURIFIER_SCHEMA_STRICT) {
if (!isset($this->types[$type])) {
trigger_error('Invalid type for configuration directive',
E_USER_ERROR);
return;
}
try {
$default = $this->parser->parse($default, $type, $allow_null);
} catch (HTMLPurifier_VarParserException $e) {
trigger_error('Default value does not match directive type',
E_USER_ERROR);
return;
}
}
$this->info[$namespace][$name] =
new HTMLPurifier_ConfigDef_Directive();
$this->info[$namespace][$name]->type = $type;
$this->info[$namespace][$name]->allow_null = $allow_null;
$this->defaults[$namespace][$name] = $default;
}
if (!HTMLPURIFIER_SCHEMA_STRICT) return;
$this->info[$namespace][$name]->description = $description;
}
/** @see HTMLPurifier_ConfigSchema->addNamespace() */
public static function defineNamespace($namespace, $description) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance();
$def->addNamespace($namespace, $description);
} }
/** /**
* Defines a namespace for directives to be put into. * Defines a namespace for directives to be put into.
* @warning This is slightly different from the corresponding static
* method.
* @param $namespace Namespace's name * @param $namespace Namespace's name
* @param $description Description of the namespace
*/ */
public function addNamespace($namespace, $description) { public function addNamespace($namespace) {
$this->info[$namespace] = array(); $this->info[$namespace] = array();
$this->info_namespace[$namespace] = new HTMLPurifier_ConfigDef_Namespace();
$this->info_namespace[$namespace]->description = $description;
$this->defaults[$namespace] = array(); $this->defaults[$namespace] = array();
} }
/** @see HTMLPurifier_ConfigSchema->addValueAliases() */
public static function defineValueAliases($namespace, $name, $aliases) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance();
$def->addValueAliases($namespace, $name, $aliases);
}
/** /**
* Defines a directive value alias. * Defines a directive value alias.
* *
@ -188,83 +87,26 @@ class HTMLPurifier_ConfigSchema {
* them set a directive to several values and get the same result. * them set a directive to several values and get the same result.
* @param $namespace Directive's namespace * @param $namespace Directive's namespace
* @param $name Name of Directive * @param $name Name of Directive
* @param $alias Name of aliased value * @param $aliases Hash of aliased values to the real alias
* @param $real Value aliased value will be converted into
*/ */
public function addValueAliases($namespace, $name, $aliases) { public function addValueAliases($namespace, $name, $aliases) {
if (HTMLPURIFIER_SCHEMA_STRICT && !isset($this->info[$namespace][$name])) {
trigger_error('Cannot set value alias for non-existant directive',
E_USER_ERROR);
return;
}
foreach ($aliases as $alias => $real) { foreach ($aliases as $alias => $real) {
if (HTMLPURIFIER_SCHEMA_STRICT) {
if (!$this->info[$namespace][$name] !== true &&
!isset($this->info[$namespace][$name]->allowed[$real])
) {
trigger_error('Cannot define alias to value that is not allowed',
E_USER_ERROR);
return;
}
if (isset($this->info[$namespace][$name]->allowed[$alias])) {
trigger_error('Cannot define alias over allowed value',
E_USER_ERROR);
return;
}
}
$this->info[$namespace][$name]->aliases[$alias] = $real; $this->info[$namespace][$name]->aliases[$alias] = $real;
} }
} }
/** @see HTMLPurifier_ConfigSchema->addAllowedValues() */
public static function defineAllowedValues($namespace, $name, $allowed_values) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance();
$def->addAllowedValues($namespace, $name, $allowed_values);
}
/** /**
* Defines a set of allowed values for a directive. * Defines a set of allowed values for a directive.
* @warning This is slightly different from the corresponding static
* method definition.
* @param $namespace Namespace of directive * @param $namespace Namespace of directive
* @param $name Name of directive * @param $name Name of directive
* @param $allowed_values Arraylist of allowed values * @param $allowed Lookup array of allowed values
*/ */
public function addAllowedValues($namespace, $name, $allowed_values) { public function addAllowedValues($namespace, $name, $allowed) {
if (HTMLPURIFIER_SCHEMA_STRICT && !isset($this->info[$namespace][$name])) {
trigger_error('Cannot define allowed values for undefined directive',
E_USER_ERROR);
return;
}
$directive =& $this->info[$namespace][$name]; $directive =& $this->info[$namespace][$name];
$type = $directive->type; $type = $directive->type;
if (HTMLPURIFIER_SCHEMA_STRICT && $type != 'string' && $type != 'istring') { $directive->allowed = $allowed;
trigger_error('Cannot define allowed values for directive whose type is not string',
E_USER_ERROR);
return;
}
if ($directive->allowed === true) {
$directive->allowed = array();
}
foreach ($allowed_values as $value) {
$directive->allowed[$value] = true;
}
if (
HTMLPURIFIER_SCHEMA_STRICT &&
$this->defaults[$namespace][$name] !== null &&
!isset($directive->allowed[$this->defaults[$namespace][$name]])
) {
trigger_error('Default value must be in allowed range of variables',
E_USER_ERROR);
$directive->allowed = true; // undo undo!
return;
}
}
/** @see HTMLPurifier_ConfigSchema->addAlias() */
public static function defineAlias($namespace, $name, $new_namespace, $new_name) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance();
$def->addAlias($namespace, $name, $new_namespace, $new_name);
} }
/** /**
@ -275,61 +117,54 @@ class HTMLPurifier_ConfigSchema {
* @param $new_name Directive that the alias will be to * @param $new_name Directive that the alias will be to
*/ */
public function addAlias($namespace, $name, $new_namespace, $new_name) { public function addAlias($namespace, $name, $new_namespace, $new_name) {
if (HTMLPURIFIER_SCHEMA_STRICT) { $this->info[$namespace][$name] = new HTMLPurifier_ConfigDef_DirectiveAlias($new_namespace, $new_name);
if (!isset($this->info[$namespace])) {
trigger_error('Cannot define directive alias in undefined namespace',
E_USER_ERROR);
return;
}
if (!ctype_alnum($name)) {
trigger_error('Directive name must be alphanumeric',
E_USER_ERROR);
return;
}
if (isset($this->info[$namespace][$name])) {
trigger_error('Cannot define alias over directive',
E_USER_ERROR);
return;
}
if (!isset($this->info[$new_namespace][$new_name])) {
trigger_error('Cannot define alias to undefined directive',
E_USER_ERROR);
return;
}
if ($this->info[$new_namespace][$new_name]->class == 'alias') {
trigger_error('Cannot define alias to alias',
E_USER_ERROR);
return;
}
}
$this->info[$namespace][$name] =
new HTMLPurifier_ConfigDef_DirectiveAlias(
$new_namespace, $new_name);
$this->info[$new_namespace][$new_name]->directiveAliases[] = "$namespace.$name"; $this->info[$new_namespace][$new_name]->directiveAliases[] = "$namespace.$name";
} }
/** // DEPRECATED METHODS
* Takes an absolute path and munges it into a more manageable relative path
* @todo Consider making protected /** @see HTMLPurifier_ConfigSchema->set() */
* @param $filename Filename to check public static function define($namespace, $name, $default, $type, $description) {
* @return string munged filename HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
*/ // process modifiers (OPTIMIZE!)
public function mungeFilename($filename) { $type_values = explode('/', $type, 2);
if (!HTMLPURIFIER_SCHEMA_STRICT) return $filename; $type = $type_values[0];
$offset = strrpos($filename, 'HTMLPurifier'); $modifier = isset($type_values[1]) ? $type_values[1] : false;
$filename = substr($filename, $offset); $allow_null = ($modifier === 'null');
$filename = str_replace('\\', '/', $filename); $def =& HTMLPurifier_ConfigSchema::instance();
return $filename; $def->add($namespace, $name, $default, $type, $allow_null);
} }
/** /** @see HTMLPurifier_ConfigSchema->addNamespace() */
* Checks if var is an HTMLPurifier_Error object public static function defineNamespace($namespace, $description) {
* @todo Consider making protected HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
*/ $def =& HTMLPurifier_ConfigSchema::instance();
public function isError($var) { $def->addNamespace($namespace);
if (!is_object($var)) return false; }
if (!($var instanceof HTMLPurifier_Error)) return false;
return true; /** @see HTMLPurifier_ConfigSchema->addValueAliases() */
public static function defineValueAliases($namespace, $name, $aliases) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance();
$def->addValueAliases($namespace, $name, $aliases);
}
/** @see HTMLPurifier_ConfigSchema->addAllowedValues() */
public static function defineAllowedValues($namespace, $name, $allowed_values) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$allowed = array();
foreach ($allowed_values as $value) {
$allowed[$value] = true;
}
$def =& HTMLPurifier_ConfigSchema::instance();
$def->addAllowedValues($namespace, $name, $allowed);
}
/** @see HTMLPurifier_ConfigSchema->addAlias() */
public static function defineAlias($namespace, $name, $new_namespace, $new_name) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def =& HTMLPurifier_ConfigSchema::instance();
$def->addAlias($namespace, $name, $new_namespace, $new_name);
} }
/** @deprecated, use HTMLPurifier_VarParser->parse() */ /** @deprecated, use HTMLPurifier_VarParser->parse() */
@ -338,6 +173,13 @@ class HTMLPurifier_ConfigSchema {
return $this->parser->parse($a, $b, $c); return $this->parser->parse($a, $b, $c);
} }
/**
* Throws an E_USER_NOTICE stating that a method is deprecated.
*/
private static function deprecated($method) {
trigger_error("Static HTMLPurifier_ConfigSchema::$method deprecated, use add*() method instead", E_USER_NOTICE);
}
} }

View File

@ -0,0 +1,48 @@
<?php
/**
* Converts HTMLPurifier_ConfigSchema_Interchange to our runtime
* representation used to perform checks on user configuration.
*/
class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
{
public function build($interchange) {
$schema = new HTMLPurifier_ConfigSchema();
foreach ($this->namespaces as $n) {
$schema->addNamespace($n->namespace);
}
foreach ($this->directives as $d) {
$schema->add(
$d->id->namespace,
$d->id->directive,
$d->default,
$d->type,
$d->typeAllowsNull
);
if ($d->allowed !== null) {
$schema->addAllowedValues(
$d->id->namespace,
$d->id->directive,
$d->allowed
);
}
foreach ($d->aliases as $alias) {
$schema->addAlias(
$alias->id->namespace,
$alias->id->directive,
$d->id->namespace,
$d->id->directive
);
}
if ($d->valueAliases !== null) {
$schema->addValueAliases(
$d->id->namespace,
$d->id->directive,
$d->valueAliases
);
}
}
}
}

View File

@ -21,69 +21,15 @@ class HTMLPurifier_ConfigSchema_Interchange
/** /**
* Adds a namespace array to $namespaces * Adds a namespace array to $namespaces
*/ */
public function addNamespace($arr) { public function addNamespace($namespace) {
$this->namespaces[$arr['ID']] = $arr; $this->namespaces[$namespace->namespace] = $namespace;
} }
/** /**
* Adds a directive array to $directives * Adds a directive array to $directives
*/ */
public function addDirective($arr) { public function addDirective($directive) {
$this->directives[$arr['ID']] = $arr; $this->directives[(string) $directive->id] = $directive;
}
/**
* Retrieves a version of this object wrapped in the validator adapter
* to be used for data-input.
*/
public function getValidatorAdapter() {
$validator = new HTMLPurifier_ConfigSchema_InterchangeValidator($this);
// Validators should be defined in the order they are to be called.
$namespace = $validator->namespace;
$directive = $validator->directive;
// ID tests
$validator->addValidator($this->make('Exists', 'ID'));
$validator->addValidator($this->make('Unique'));
// ID: Namespace test
$namespace->addValidator($this->make('Alnum', 'ID'));
// ID: Common tests
$validator->addValidator($this->make('ParseId'));
$validator->addValidator($this->make('Exists', '_NAMESPACE'));
$validator->addValidator($this->make('Alnum', '_NAMESPACE'));
// ID: Directive tests
$directive->addValidator($this->make('Exists', '_DIRECTIVE'));
$directive->addValidator($this->make('Alnum', '_DIRECTIVE'));
$directive->addValidator($this->make('NamespaceExists'));
// Directive: Type and Default tests
$directive->addValidator($this->make('Exists', 'TYPE'));
$directive->addValidator($this->make('ParseType'));
$directive->addValidator($this->make('Exists', '_TYPE'));
$directive->addValidator($this->make('Exists', '_NULL'));
$directive->addValidator($this->make('Exists', 'DEFAULT'));
$directive->addValidator($this->make('ParseDefault'));
// Common tests
$validator->addValidator($this->make('Exists', 'DESCRIPTION'));
return $validator;
}
/**
* Creates a validator.
* @warning
* Only *one* argument is supported; multiple args shouldn't use
* this function.
*/
protected function make($name, $arg = null) {
$class = "HTMLPurifier_ConfigSchema_Validator_$name";
if ($arg === null) return new $class();
else return new $class($arg);
} }
} }

View File

@ -0,0 +1,70 @@
<?php
/**
* Interchange component class describing configuration directives.
*/
class HTMLPurifier_ConfigSchema_Interchange_Directive
{
/**
* ID of directive, instance of HTMLPurifier_ConfigSchema_Interchange_Id.
*/
public $id;
/**
* String type, e.g. 'integer' or 'istring'.
*/
public $type;
/**
* Default value, e.g. 3 or 'DefaultVal'.
*/
public $default;
/**
* HTML description.
*/
public $description;
/**
* Boolean whether or not null is allowed as a value.
*/
public $typeAllowsNull = false;
/**
* Lookup table of allowed scalar values, e.g. array('allowed' => true).
* Null if all values are allowed.
*/
public $allowed;
/**
* List of aliases for the directive, e.g. array('Alt.Directive').
*/
public $aliases = array();
/**
* Hash of value aliases, e.g. array('alt' => 'real'). Null if value
* aliasing is disabled (necessary for non-scalar types).
*/
public $valueAliases;
/**
* Version of HTML Purifier the directive was introduced, e.g. '1.3.1'.
* Null if the directive has always existed.
*/
public $version;
/**
* ID of directive that supercedes this old directive, is an instance
* of HTMLPurifier_ConfigSchema_Interchange_Id. Null if not deprecated.
*/
public $deprecatedUse;
/**
* Version of HTML Purifier this directive was deprecated. Null if not
* deprecated.
*/
public $deprecatedVersion;
}

View File

@ -0,0 +1,25 @@
<?php
/**
* Represents a directive ID in the interchange format.
*/
class HTMLPurifier_ConfigSchema_Interchange_Id
{
public $namespace, $directive;
public function __construct($namespace, $directive) {
$this->namespace = $namespace;
$this->directive = $directive;
}
public function __toString() {
return $this->namespace . '.' . $this->directive;
}
public static function make($id) {
list($namespace, $directive) = explode('.', $id);
return new HTMLPurifier_ConfigSchema_Interchange_Id($namespace, $directive);
}
}

View File

@ -0,0 +1,19 @@
<?php
/**
* Interchange component class describing namespaces.
*/
class HTMLPurifier_ConfigSchema_Interchange_Namespace
{
/**
* Name of namespace defined.
*/
public $namespace;
/**
* HTML description.
*/
public $description;
}

View File

@ -0,0 +1,109 @@
<?php
class HTMLPurifier_ConfigSchema_InterchangeBuilder
{
protected $varParser;
public function __construct($varParser = null) {
$this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
}
/**
* Builds an interchange object based on a hash.
* @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build
* @param $hash HTMLPurifier_ConfigSchema_StringHash source data
*/
public function build($interchange, $hash) {
if (strpos($hash['ID'], '.') === false) {
$this->buildNamespace($interchange, $hash);
} else {
$this->buildDirective($interchange, $hash);
}
$this->_findUnused($hash);
}
public function buildNamespace($interchange, $hash) {
$namespace = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
$namespace->namespace = $hash->offsetGet('ID');
$namespace->description = $hash->offsetGet('DESCRIPTION');
$interchange->addNamespace($namespace);
}
public function buildDirective($interchange, $hash) {
$directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
// These are required elements:
$directive->id = $this->id($hash->offsetGet('ID'));
$type = explode('/', $hash->offsetGet('TYPE'));
if (isset($type[1])) $directive->typeAllowsNull = true;
$directive->type = $type[0];
$directive->description = $directive->offsetGet('DESCRIPTION');
// These are extras:
if (isset($directive['ALLOWED'])) {
$directive->allowed = $this->lookup($this->evalArray($directive->offsetGet('ALLOWED')));
}
if (isset($directive['VALUE-ALIASES'])) {
$directive->valueAliases = $this->evalArray($directive->offsetGet('VALUE-ALIASES'));
}
if (isset($directive['ALIASES'])) {
$raw_aliases = trim($hash->offsetGet('ALIASES'));
$aliases = preg_split('/\s*,\s*/', $raw_aliases);
foreach ($aliases as $alias) {
$this->aliases[] = $this->id($alias);
}
}
if (isset($directive['VERSION'])) {
$directive->version = $directive->offsetGet('VERSION');
}
if (isset($directive['DEPRECATED-USE'])) {
$directive->deprecatedUse = $this->id($directive->offsetGet('DEPRECATED-USE'));
}
if (isset($directive['DEPRECATED-VERSION'])) {
$directive->deprecatedVersion = $directive->offsetGet('DEPRECATED-VERSION');
}
$interchange->addDirective($directive);
}
/**
* Evaluates an array PHP code string without array() wrapper
*/
protected function evalArray($contents) {
return eval('return array('. $contents .');');
}
/**
* Converts an array list into a lookup array.
*/
protected function lookup($array) {
$ret = array();
foreach ($array as $val) $ret[$val] = true;
return $ret;
}
/**
* Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id
* object based on a string Id.
*/
protected function id($id) {
return HTMLPurifier_ConfigSchema_Interchange_Id::make($id);
}
/**
* Triggers errors for any unused keys passed in the hash; such keys
* may indicate typos, missing values, etc.
* @param $hash Instance of ConfigSchema_StringHash to check.
*/
protected function _findUnused($hash) {
$accessed = $hash->getAccessed();
foreach ($hash as $k => $v) {
if (!isset($accessed[$k])) {
trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE);
}
}
}
}

View File

@ -1,89 +0,0 @@
<?php
/**
* Takes an array of keys to strings, probably generated by
* HTMLPurifier_ConfigSchema_StringHashParser
*/
class HTMLPurifier_ConfigSchema_StringHashAdapter
{
/**
* Takes a string hash and calls the appropriate functions in $schema
* based on its values.
*/
public function adapt($hash, $schema) {
if (! $hash instanceof HTMLPurifier_ConfigSchema_StringHash) {
$hash = new HTMLPurifier_ConfigSchema_StringHash($hash);
}
if (!isset($hash['ID'])) {
trigger_error('Missing key ID in string hash');
return;
}
// Check namespace:
if (strpos($hash['ID'], '.') === false) {
// This will cause problems if we decide to support nested
// namespaces, but for now it's ok.
$schema->addNamespace($hash->offsetGet('ID'), $hash->offsetGet('DESCRIPTION'));
$this->_findUnused($hash);
return;
}
list($ns, $directive) = explode('.', $hash->offsetGet('ID'), 2);
if (isset($hash['TYPE'], $hash['DEFAULT'], $hash['DESCRIPTION'])) {
$type = $hash->offsetGet('TYPE');
$raw_default = $hash->offsetGet('DEFAULT');
$default = eval("return $raw_default;");
$description = $hash->offsetGet('DESCRIPTION');
$schema->add($ns, $directive, $default, $type, $description);
}
if (isset($hash['ALLOWED'])) {
$raw_allowed = $hash->offsetGet('ALLOWED');
$allowed = eval("return array($raw_allowed);");
$schema->addAllowedValues($ns, $directive, $allowed);
}
// This must be after ALLOWED
if (isset($hash['VALUE-ALIASES'])) {
$raw_value_aliases = $hash->offsetGet('VALUE-ALIASES');
$value_aliases = eval("return array($raw_value_aliases);");
$schema->addValueAliases($ns, $directive, $value_aliases);
}
if (isset($hash['ALIASES'])) {
$raw_aliases = trim($hash->offsetGet('ALIASES'));
$aliases = preg_split('/\s*,\s*/', $raw_aliases);
foreach ($aliases as $alias) {
list($alias_ns, $alias_directive) = explode('.', $alias, 2);
$schema->addAlias($alias_ns, $alias_directive, $ns, $directive);
}
}
// We don't use these yet, but they're specified
if (isset($hash['VERSION'])) $hash->offsetGet('VERSION');
if (isset($hash['DEPRECATED-USE'])) $hash->offsetGet('DEPRECATED-USE');
if (isset($hash['DEPRECATED-VERSION'])) $hash->offsetGet('DEPRECATED-VERSION');
$this->_findUnused($hash);
}
/**
* Triggers errors for any unused keys passed in the hash; such keys
* may indicate typos, missing values, etc.
* @param $hash Instance of ConfigSchema_StringHash to check.
*/
protected function _findUnused($hash) {
$accessed = $hash->getAccessed();
foreach ($hash as $k => $v) {
if (!isset($accessed[$k])) {
trigger_error("String hash key '$k' not used by adapter", E_USER_NOTICE);
}
}
}
}

View File

@ -1,127 +0,0 @@
<?php
/**
* Converts HTMLPurifier_ConfigSchema into a StringHash which can be
* easily saved to a file.
*/
class HTMLPurifier_ConfigSchema_StringHashReverseAdapter
{
protected $schema;
/**
* @param $schema Instance of HTMLPurifier_ConfigSchema to generate
* string hashes from.
*/
public function __construct($schema) {
$this->schema = $schema;
}
/**
* Retrieves a string hash from a specific ID, could be a directive
* or a namespace.
* @param $ns string namespace
* @param $directive string directive name
*/
public function get($ns, $directive = null) {
$ret = array();
if ($directive === null) {
if (!isset($this->schema->info_namespace[$ns])) {
trigger_error("Namespace '$ns' doesn't exist in schema");
return;
}
$def = $this->schema->info_namespace[$ns];
$ret['ID'] = $ns;
$ret['DESCRIPTION'] = $def->description;
return $ret;
}
if (!isset($this->schema->info[$ns][$directive])) {
trigger_error("Directive '$ns.$directive' doesn't exist in schema");
return;
}
$def = $this->schema->info[$ns][$directive];
if ($def instanceof HTMLPurifier_ConfigDef_DirectiveAlias) {
return false;
}
$ret['ID'] = "$ns.$directive";
$ret['TYPE'] = $def->type;
// Attempt to extract version information from description.
$description = $this->normalize($def->description);
list($description, $version) = $this->extractVersion($description);
if ($version) $ret['VERSION'] = $version;
$ret['DEFAULT'] = $this->export($this->schema->defaults[$ns][$directive]);
$ret['DESCRIPTION'] = wordwrap($description, 75, "\n");
if ($def->allowed !== true) {
$ret['ALLOWED'] = $this->exportLookup($def->allowed);
}
if (!empty($def->aliases)) {
$ret['VALUE-ALIASES'] = $this->exportHash($def->aliases);
}
if (!empty($def->directiveAliases)) {
$ret['ALIASES'] = implode(', ', $def->directiveAliases);
}
return $ret;
}
/**
* Exports a variable into a PHP-readable format
*/
public function export($var) {
if ($var === array()) return 'array()'; // single-line format
return var_export($var, true);
}
/**
* Exports a lookup array into the form 'key1', 'key2', ...
*/
public function exportLookup($lookup) {
if (!is_array($lookup)) return $this->export($lookup);
if (empty($lookup)) return '';
$keys = array_map(array($this, 'export'), array_keys($lookup));
return implode(', ', $keys);
}
/**
* Exports a hash into the form 'key' => 'val',\n ...
*/
public function exportHash($hash) {
if (!is_array($hash)) return $this->export($hash);
if (empty($hash)) return '';
$code = $this->export($hash);
$lines = explode("\n", $code);
$ret = '';
foreach ($lines as $line) {
if ($line == 'array (') continue;
if ($line == ')') continue;
$ret .= substr($line, 2) . "\n";
}
return $ret;
}
/**
* Normalizes a string to Unix style newlines
*/
protected function normalize($string) {
return str_replace(array("\r\n", "\r"), "\n", $string);
}
public function extractVersion($description) {
$regex = '/This directive (?:has been|was) available since (\d+\.\d+\.\d+)\./';
$regex = str_replace(' ', '\s+', $regex); // allow any number of spaces between statements
$ok = preg_match($regex, $description, $matches);
if ($ok) {
$version = $matches[1];
} else {
$version = false;
}
$description = preg_replace($regex, '', $description, 1);
return array($description, $version);
}
}

View File

@ -1,26 +0,0 @@
<?php
/**
* Base validator for HTMLPurifier_ConfigSchema_Interchange
*/
class HTMLPurifier_ConfigSchema_Validator
{
/**
* Common validator, throwing an exception on error. It can
* also performing filtering or evaluation functions.
*
* @param $arr Array to validate.
* @param $interchange HTMLPurifier_ConfigSchema_Interchange object
* that is being processed.
*/
public function validate(&$arr, $interchange) {}
/**
* Throws a HTMLPurifier_ConfigSchema_Exception
*/
protected function error($msg) {
throw new HTMLPurifier_ConfigSchema_Exception($msg);
}
}

View File

@ -1,22 +0,0 @@
<?php
/**
* Validates that a field is alphanumeric in the array. Expects $index
* to exist.
*/
class HTMLPurifier_ConfigSchema_Validator_Alnum extends HTMLPurifier_ConfigSchema_Validator
{
protected $index;
public function __construct($index) {
$this->index = $index;
}
public function validate(&$arr, $interchange) {
if (!ctype_alnum($arr[$this->index])) {
$this->error($arr[$this->index] . ' in '. $this->index .' must be alphanumeric');
}
}
}

View File

@ -1,21 +0,0 @@
<?php
/**
* Groups several validators together, which can be used with logical validators
*/
class HTMLPurifier_ConfigSchema_Validator_Composite extends HTMLPurifier_ConfigSchema_Validator
{
protected $validators = array();
public function addValidator($validator) {
$this->validators[] = $validator;
}
public function validate(&$arr, $interchange) {
foreach ($this->validators as $validator) {
$validator->validate($arr, $interchange);
}
}
}

View File

@ -1,21 +0,0 @@
<?php
/**
* Validates that an field exists in the array
*/
class HTMLPurifier_ConfigSchema_Validator_Exists extends HTMLPurifier_ConfigSchema_Validator
{
protected $index;
public function __construct($index) {
$this->index = $index;
}
public function validate(&$arr, $interchange) {
if (empty($arr[$this->index])) {
$this->error($this->index . ' must exist');
}
}
}

View File

@ -1,49 +0,0 @@
<?php
/**
* If a validator passes, run another validator.
*/
class HTMLPurifier_ConfigSchema_Validator_If extends HTMLPurifier_ConfigSchema_Validator
{
protected $condition;
protected $then;
protected $else;
public function __construct($cond = null) {
$this->setCondition($cond);
}
/**
* @param $validator Validator to run as a condition. Exceptions thrown by it
* do not bubble up.
*/
public function setCondition($validator) {
$this->condition = $validator;
}
/**
* @param $validator Validator to run if condition is true
*/
public function setThen($validator) {
$this->then = $validator;
}
/**
* @param $validator Validator to run if condition is false
*/
public function setElse($validator) {
$this->else = $validator;
}
public function validate(&$arr, $interchange) {
try {
$this->condition->validate($arr, $interchange);
} catch (HTMLPurifier_ConfigSchema_Exception $e) {
if ($this->else) $this->else->validate($arr, $interchange);
return;
}
if ($this->then) $this->then->validate($arr, $interchange);
}
}

View File

@ -1,16 +0,0 @@
<?php
/**
* Validates that the directive's namespace exists. Expects _NAMESPACE
* to have been created via HTMLPurifier_ConfigSchema_Validator_ParseId
*/
class HTMLPurifier_ConfigSchema_Validator_NamespaceExists extends HTMLPurifier_ConfigSchema_Validator
{
public function validate(&$arr, $interchange) {
if (!isset($interchange->namespaces[$arr['_NAMESPACE']])) {
$this->error('Cannot define directive for undefined namespace ' . $arr['_NAMESPACE']);
}
}
}

View File

@ -1,39 +0,0 @@
<?php
/**
* Groups several validators together, but as an 'or': if the first
* one passes, we abort; if it throws an exception, we try the next validator,
* and the next. If all validators fail, we throw an exception.
*
* @note If no validators are registered, this validator automatically
* "passes".
*/
class HTMLPurifier_ConfigSchema_Validator_Or extends HTMLPurifier_ConfigSchema_Validator
{
protected $validators = array();
public function addValidator($validator) {
$this->validators[] = $validator;
}
public function validate(&$arr, $interchange) {
$exceptions = array();
$pass = false;
foreach ($this->validators as $validator) {
try {
$validator->validate($arr, $interchange);
} catch (HTMLPurifier_ConfigSchema_Exception $e) {
$exceptions[] = $e;
continue;
}
$exceptions = array();
break;
}
if ($exceptions) {
// I wonder how we can make the exceptions "lossless"
throw new HTMLPurifier_ConfigSchema_Exception('All validators failed: ' . implode(";\n", $exceptions));
}
}
}

View File

@ -1,18 +0,0 @@
<?php
/**
* Parses DEFAULT into _DEFAULT. Expects DEFAULT, _TYPE, _NULL and ID to exist.
*/
class HTMLPurifier_ConfigSchema_Validator_ParseDefault extends HTMLPurifier_ConfigSchema_Validator
{
public function validate(&$arr, $interchange) {
$parser = new HTMLPurifier_VarParser_Native(); // not configurable yet
try {
$arr['_DEFAULT'] = $parser->parse($arr['DEFAULT'], $arr['_TYPE'], $arr['_NULL']);
} catch (HTMLPurifier_VarParserException $e) {
throw new HTMLPurifier_ConfigSchema_Exception('Invalid type for default value in '. $arr['ID'] .': ' . $e->getMessage());
}
}
}

View File

@ -1,15 +0,0 @@
<?php
/**
* Parses ID into NAMESPACE and, if appropriate, DIRECTIVE. Expects ID to exist.
*/
class HTMLPurifier_ConfigSchema_Validator_ParseId extends HTMLPurifier_ConfigSchema_Validator
{
public function validate(&$arr, $interchange) {
$r = explode('.', $arr['ID'], 2);
$arr['_NAMESPACE'] = $r[0];
if (isset($r[1])) $arr['_DIRECTIVE'] = $r[1];
}
}

View File

@ -1,19 +0,0 @@
<?php
/**
* Parses TYPE into _TYPE and _NULL. Expects TYPE and ID to exist.
*/
class HTMLPurifier_ConfigSchema_Validator_ParseType extends HTMLPurifier_ConfigSchema_Validator
{
public function validate(&$arr, $interchange) {
$r = explode('/', $arr['TYPE'], 2);
if (!isset(HTMLPurifier_VarParser::$types[$r[0]])) {
$this->error('Invalid type ' . $r[0] . ' for configuration directive ' . $arr['ID']);
}
$arr['_TYPE'] = $r[0];
$arr['_NULL'] = (isset($r[1]) && $r[1] === 'null');
}
}

View File

@ -1,23 +0,0 @@
<?php
/**
* Validates that this ID does not exist already in the interchange object.
* Expects ID to exist.
*
* @note
* Although this tests both possible values, in practice the ID
* will only be in one or the other. We do this to keep things simple.
*/
class HTMLPurifier_ConfigSchema_Validator_Unique extends HTMLPurifier_ConfigSchema_Validator
{
public function validate(&$arr, $interchange) {
if (isset($interchange->namespaces[$arr['ID']])) {
$this->error('Cannot redefine namespace');
}
if (isset($interchange->directives[$arr['ID']])) {
$this->error('Cannot redefine directive');
}
}
}

View File

@ -17,25 +17,15 @@ $FS = new FSTools();
$files = $FS->globr('../library/HTMLPurifier/ConfigSchema/schema', '*.txt'); $files = $FS->globr('../library/HTMLPurifier/ConfigSchema/schema', '*.txt');
$namespaces = array(); $parser = new HTMLPurifier_ConfigSchema_StringHashParser();
$directives = array(); $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
$interchange = new HTMLPurifier_ConfigSchema_Interchange();
// Generate string hashes
$parser = new HTMLPurifier_ConfigSchema_StringHashParser();
foreach ($files as $file) { foreach ($files as $file) {
$hash = $parser->parseFile($file); $builder->build($interchange, $parser->parseFile($file));
if (strpos($hash['ID'], '.') === false) {
$namespaces[] = $hash;
} else {
$directives[] = $hash;
}
} }
$adapter = new HTMLPurifier_ConfigSchema_StringHashAdapter(); $schema_builder = new HTMLPurifier_ConfigSchema_Builder_ConfigSchema();
$schema = new HTMLPurifier_ConfigSchema(); $schema = $schema_builder->build($interchange);
foreach ($namespaces as $hash) $adapter->adapt($hash, $schema);
foreach ($directives as $hash) $adapter->adapt($hash, $schema);
echo "Saving schema... "; echo "Saving schema... ";
file_put_contents($target, serialize($schema)); file_put_contents($target, serialize($schema));

View File

@ -10,25 +10,17 @@ class HTMLPurifier_ConfigSchema_InterchangeTest extends UnitTestCase
} }
public function testAddNamespace() { public function testAddNamespace() {
$this->interchange->addNamespace($v = array( $v = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
'ID' => 'Namespace', $v->namespace = 'Namespace';
'DESCRIPTION' => 'Bar', $this->interchange->addNamespace($v);
));
$this->assertIdentical($v, $this->interchange->namespaces['Namespace']); $this->assertIdentical($v, $this->interchange->namespaces['Namespace']);
} }
public function testAddDirective() { public function testAddDirective() {
$this->interchange->addDirective($v = array( $v = new HTMLPurifier_ConfigSchema_Interchange_Directive();
'ID' => 'Namespace.Directive', $v->id = new HTMLPurifier_ConfigSchema_Interchange_Id('Namespace', 'Directive');
'DESCRIPTION' => 'Bar', $this->interchange->addDirective($v);
));
$this->assertIdentical($v, $this->interchange->directives['Namespace.Directive']); $this->assertIdentical($v, $this->interchange->directives['Namespace.Directive']);
} }
public function testValidator() {
$adapter = $this->interchange->getValidatorAdapter();
$this->expectException(new HTMLPurifier_ConfigSchema_Exception('ID must exist'));
$adapter->addDirective(array());
}
} }

View File

@ -1,44 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_InterchangeValidatorTest extends UnitTestCase
{
public function setup() {
generate_mock_once('HTMLPurifier_ConfigSchema_Interchange');
$this->mock = new HTMLPurifier_ConfigSchema_InterchangeMock();
$this->validator = new HTMLPurifier_ConfigSchema_InterchangeValidator($this->mock);
}
protected function makeValidator($expect_params = null) {
generate_mock_once('HTMLPurifier_ConfigSchema_Validator');
$validator = new HTMLPurifier_ConfigSchema_ValidatorMock();
if ($expect_params !== null) $validator->expectOnce('validate', $expect_params);
else $validator->expectNever('validate');
return $validator;
}
public function testAddNamespaceNullValidator() {
$hash = array('ID' => 'Namespace');
$this->mock->expectOnce('addNamespace', array($hash));
$this->validator->addNamespace($hash);
}
public function testAddNamespaceWithValidators() {
$hash = array('ID' => 'Namespace');
$this->validator->addValidator($this->makeValidator(array($hash, $this->mock)));
$this->validator->namespace->addValidator($this->makeValidator(array($hash, $this->mock)));
$this->validator->directive->addValidator($this->makeValidator()); // not called
$this->mock->expectOnce('addNamespace', array($hash));
$this->validator->addNamespace($hash);
}
public function testAddDirectiveWithValidators() {
$hash = array('ID' => 'Namespace.Directive');
$this->validator->addValidator($this->makeValidator(array($hash, $this->mock)));
$this->validator->namespace->addValidator($this->makeValidator()); // not called
$this->validator->directive->addValidator($this->makeValidator(array($hash, $this->mock)));
$this->mock->expectOnce('addDirective', array($hash));
$this->validator->addDirective($hash);
}
}

View File

@ -1,137 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_StringHashAdapterTest extends UnitTestCase
{
function __construct() {
generate_mock_once('HTMLPurifier_ConfigSchema');
parent::UnitTestCase();
}
function assertAdapt($input, $calls = array()) {
$schema = new HTMLPurifier_ConfigSchemaMock();
$called = array();
foreach ($calls as $signature) {
list($func, $params) = $signature;
if (!isset($called[$func])) $called[$func] = 0;
$schema->expectAt($called[$func]++, $func, $params);
}
$adapter = new HTMLPurifier_ConfigSchema_StringHashAdapter();
$adapter->adapt($input, $schema);
}
function testBasic() {
$this->assertAdapt(
array(
'ID' => 'Namespace.Directive',
'DEFAULT' => "'default' . 'bar'",
'TYPE' => 'string',
'DESCRIPTION' => "Description of default.\n",
),
array(
array('add', array(
'Namespace', 'Directive', 'defaultbar', 'string',
"Description of default.\n"
)),
)
);
}
function testNamespace() {
$this->assertAdapt(
array(
'ID' => 'Namespace',
'DESCRIPTION' => 'Description of namespace',
),
array(
array('addNamespace', array('Namespace', 'Description of namespace')),
)
);
}
function testValueAliases() {
$this->assertAdapt(
array(
'ID' => 'Ns.Dir',
'VALUE-ALIASES' => "
'milk' => 'dairy',
'cheese' => 'dairy',
",
),
array(
array('addValueAliases', array('Ns', 'Dir', array('milk' => 'dairy', 'cheese' => 'dairy'))),
)
);
}
function testAllowedValues() {
$this->assertAdapt(
array(
'ID' => 'Ns.Dir',
'ALLOWED' => "'val1', 'val2'",
),
array(
array('addAllowedValues', array('Ns', 'Dir', array('val1', 'val2'))),
)
);
}
function testAlias() {
$this->assertAdapt(
array(
'ID' => 'Ns.Dir',
'ALIASES' => "Ns.Dir2, Ns2.Dir",
),
array(
array('addAlias', array('Ns', 'Dir2', 'Ns', 'Dir')),
array('addAlias', array('Ns2', 'Dir', 'Ns', 'Dir')),
)
);
}
function testCombo() {
$this->assertAdapt(
array(
'ID' => 'Ns.Dir',
'DEFAULT' => "'val' . '1'",
'TYPE' => 'string',
'DESCRIPTION' => "Description of default.\n",
'VALUE-ALIASES' => "
'milk' => 'val1',
'cheese' => 'val1',
",
'ALLOWED' => "'val1', 'val2'",
'ALIASES' => "Ns.Dir2, Ns2.Dir",
),
array(
array('add', array(
'Ns', 'Dir', 'val1', 'string',
"Description of default.\n"
)),
array('addAllowedValues', array('Ns', 'Dir', array('val1', 'val2'))),
array('addValueAliases', array('Ns', 'Dir', array('milk' => 'val1', 'cheese' => 'val1'))),
array('addAlias', array('Ns', 'Dir2', 'Ns', 'Dir')),
array('addAlias', array('Ns2', 'Dir', 'Ns', 'Dir')),
)
);
}
function testMissingIdError() {
$this->expectError('Missing key ID in string hash');
$this->assertAdapt(array());
}
function testExtraError() {
$this->expectError("String hash key 'FOOBAR' not used by adapter");
$this->assertAdapt(
array(
'ID' => 'Namespace',
'DESCRIPTION' => 'Description of namespace',
'FOOBAR' => 'Extra stuff',
),
array(
array('addNamespace', array('Namespace', 'Description of namespace')),
)
);
}
}

View File

@ -1,102 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_StringHashReverseAdapterTest extends UnitTestCase
{
function makeSchema() {
$schema = new HTMLPurifier_ConfigSchema();
$schema->addNamespace('Ns', 'Description of ns.');
$schema->addNamespace('Ns2', 'Description of ns2.');
$schema->add('Ns', 'Dir', 'dairy', 'string',
"Description of default.\nThis directive has been available since 1.2.0.");
$schema->addAllowedValues('Ns', 'Dir', array('dairy', 'meat'));
$schema->addValueAliases('Ns', 'Dir', array('milk' => 'dairy', 'cheese' => 'dairy'));
$schema->addAlias('Ns', 'Dir2', 'Ns', 'Dir');
$schema->addAlias('Ns2', 'Dir', 'Ns', 'Dir');
return $schema;
}
function testNamespace() {
$adapter = new HTMLPurifier_ConfigSchema_StringHashReverseAdapter($this->makeSchema());
$result = $adapter->get('Ns');
$expect = array(
'ID' => 'Ns',
'DESCRIPTION' => "Description of ns.",
);
$this->assertIdentical($result, $expect);
}
function testBadNamespace() {
$adapter = new HTMLPurifier_ConfigSchema_StringHashReverseAdapter($this->makeSchema());
$this->expectError("Namespace 'BadNs' doesn't exist in schema");
$adapter->get('BadNs');
}
function testDirective() {
$adapter = new HTMLPurifier_ConfigSchema_StringHashReverseAdapter($this->makeSchema());
$result = $adapter->get('Ns', 'Dir');
$expect = array(
'ID' => 'Ns.Dir',
'TYPE' => 'string',
'VERSION' => '1.2.0',
'DEFAULT' => "'dairy'",
'DESCRIPTION' => "Description of default.\n",
'ALLOWED' => "'dairy', 'meat'",
'VALUE-ALIASES' => "'milk' => 'dairy',\n'cheese' => 'dairy',\n",
'ALIASES' => "Ns.Dir2, Ns2.Dir",
);
$this->assertIdentical($result, $expect);
}
function testBadDirective() {
$adapter = new HTMLPurifier_ConfigSchema_StringHashReverseAdapter($this->makeSchema());
$this->expectError("Directive 'BadNs.BadDir' doesn't exist in schema");
$adapter->get('BadNs', 'BadDir');
}
function assertMethod($func, $input, $expect) {
$adapter = new HTMLPurifier_ConfigSchema_StringHashReverseAdapter($this->makeSchema());
$result = $adapter->$func($input);
$this->assertIdentical($result, $expect);
}
function testExportEmptyHash() {
$this->assertMethod('exportHash', array(), '');
}
function testExportHash() {
$this->assertMethod('exportHash', array('foo' => 'bar'), "'foo' => 'bar',\n");
}
function testExportEmptyLookup() {
$this->assertMethod('exportLookup', array(), '');
}
function testExportSingleLookup() {
$this->assertMethod('exportLookup', array('key' => true), "'key'");
}
function testExportLookup() {
$this->assertMethod('exportLookup', array('key' => true, 'key2' => true, 3 => true), "'key', 'key2', 3");
}
function assertExtraction($desc, $expect_desc, $expect_version) {
$adapter = new HTMLPurifier_ConfigSchema_StringHashReverseAdapter($this->makeSchema());
list($result_desc, $result_version) = $adapter->extractVersion($desc);
$this->assertIdentical($result_desc, $expect_desc);
$this->assertIdentical($result_version, $expect_version);
}
function testExtractSimple() {
$this->assertExtraction("Desc.\nThis directive has been available since 2.0.0.", "Desc.\n", '2.0.0');
}
function testExtractMultiline() {
$this->assertExtraction("Desc.\nThis directive was available\n since 23.4.333.", "Desc.\n", '23.4.333');
}
}

View File

@ -1,17 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_AlnumTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function setup() {
$this->validator = new HTMLPurifier_ConfigSchema_Validator_Alnum('ID');
parent::setup();
}
public function testValidate() {
$this->expectSchemaException('R&D in ID must be alphanumeric');
$arr = array('ID' => 'R&D');
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,20 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_CompositeTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function testValidate() {
$arr = array('ID' => 'RD');
generate_mock_once('HTMLPurifier_ConfigSchema_Validator');
$mock1 = new HTMLPurifier_ConfigSchema_ValidatorMock();
$mock2 = new HTMLPurifier_ConfigSchema_ValidatorMock();
$mock1->expectOnce('validate', array($arr, $this->interchange));
$mock2->expectOnce('validate', array($arr, $this->interchange));
$this->validator->addValidator($mock1);
$this->validator->addValidator($mock2);
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,17 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_ExistsTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function setup() {
$this->validator = new HTMLPurifier_ConfigSchema_Validator_Exists('ID');
parent::setup();
}
public function testValidate() {
$this->expectSchemaException('ID must exist');
$arr = array();
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,31 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_IfTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function setup() {
parent::setup();
generate_mock_once('HTMLPurifier_ConfigSchema_Validator');
}
public function testValidate() {
$arr = array('ID' => 'RD');
$this->validator->setCondition(new HTMLPurifier_ConfigSchema_Validator_Exists('ID'));
$this->validator->setThen($mock1 = new HTMLPurifier_ConfigSchema_ValidatorMock());
$mock1->expectOnce('validate', array($arr, $this->interchange));
$this->validator->setElse($mock2 = new HTMLPurifier_ConfigSchema_ValidatorMock());
$mock2->expectNever('validate');
$this->validator->validate($arr, $this->interchange);
}
public function testValidateConditionIsFalse() {
$arr = array('ID' => 'RD');
$this->validator->setCondition(new HTMLPurifier_ConfigSchema_Validator_Exists('ALTID'));
$this->validator->setThen($mock1 = new HTMLPurifier_ConfigSchema_ValidatorMock());
$mock1->expectNever('validate');
$this->validator->setElse($mock2 = new HTMLPurifier_ConfigSchema_ValidatorMock());
$mock2->expectOnce('validate', array($arr, $this->interchange));
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,18 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_NamespaceExistsTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function testValidateFail() {
$arr = array('_NAMESPACE' => 'Namespace');
$this->expectSchemaException('Cannot define directive for undefined namespace Namespace');
$this->validator->validate($arr, $this->interchange);
}
public function testValidatePass() {
$arr = array('_NAMESPACE' => 'Namespace');
$this->interchange->addNamespace(array('ID' => 'Namespace'));
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,31 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_OrTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function testValidatePass() {
$arr = array('ID' => 'RD');
$this->validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Alnum('ID'));
// Never called:
$this->validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Exists('ALT-ID'));
$this->validator->validate($arr, $this->interchange);
}
public function testValidatePassLater() {
$arr = array('ID' => 'RD');
// This one fails:
$this->validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Exists('ALT-ID'));
// But this one passes:
$this->validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Alnum('ID'));
$this->validator->validate($arr, $this->interchange);
}
public function testValidateFail() {
$arr = array('ID' => 'RD');
$this->validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Exists('ALT-ID'));
$this->validator->addValidator(new HTMLPurifier_ConfigSchema_Validator_Exists('FOOBAR'));
$this->expectException('HTMLPurifier_ConfigSchema_Exception');
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,34 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_ParseDefaultTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function testValidate() {
$arr = array(
'ID' => 'N.D',
'DEFAULT' => 'true',
'_TYPE' => 'bool',
'_NULL' => false,
);
$this->validator->validate($arr, $this->interchange);
$this->assertIdentical($arr, array(
'ID' => 'N.D',
'DEFAULT' => 'true',
'_TYPE' => 'bool',
'_NULL' => false,
'_DEFAULT' => true,
));
}
public function testValidateFail() {
$arr = array(
'ID' => 'N.D',
'DEFAULT' => '"asdf"',
'_TYPE' => 'int',
'_NULL' => true,
);
$this->expectSchemaException('Invalid type for default value in N.D: Expected type int, got string');
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,25 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_ParseIdTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function testValidateNamespace() {
$arr = array('ID' => 'Namespace');
$this->validator->validate($arr, $this->interchange);
$this->assertIdentical($arr, array(
'ID' => 'Namespace',
'_NAMESPACE' => 'Namespace'
));
}
public function testValidateDirective() {
$arr = array('ID' => 'Namespace.Directive');
$this->validator->validate($arr, $this->interchange);
$this->assertIdentical($arr, array(
'ID' => 'Namespace.Directive',
'_NAMESPACE' => 'Namespace',
'_DIRECTIVE' => 'Directive'
));
}
}

View File

@ -1,34 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_ParseTypeTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function testValidatePlain() {
$arr = array('ID' => 'N.D', 'TYPE' => 'string');
$this->validator->validate($arr, $this->interchange);
$this->assertIdentical($arr, array(
'ID' => 'N.D',
'TYPE' => 'string',
'_TYPE' => 'string',
'_NULL' => false,
));
}
public function testValidateWithNull() {
$arr = array('ID' => 'N.D', 'TYPE' => 'int/null');
$this->validator->validate($arr, $this->interchange);
$this->assertIdentical($arr, array(
'ID' => 'N.D',
'TYPE' => 'int/null',
'_TYPE' => 'int',
'_NULL' => true,
));
}
public function testValidateInvalidType() {
$arr = array('ID' => 'N.D', 'TYPE' => 'aint/null');
$this->expectSchemaException('Invalid type aint for configuration directive N.D');
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,20 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_Validator_UniqueTest extends HTMLPurifier_ConfigSchema_ValidatorHarness
{
public function testValidateNamespace() {
$this->interchange->addNamespace(array('ID' => 'Namespace'));
$this->expectSchemaException('Cannot redefine namespace');
$arr = array('ID' => 'Namespace');
$this->validator->validate($arr, $this->interchange);
}
public function testValidateDirective() {
$this->interchange->addDirective(array('ID' => 'Namespace.Directive'));
$this->expectSchemaException('Cannot redefine directive');
$arr = array('ID' => 'Namespace.Directive');
$this->validator->validate($arr, $this->interchange);
}
}

View File

@ -1,24 +0,0 @@
<?php
class HTMLPurifier_ConfigSchema_ValidatorHarness extends UnitTestCase
{
protected $interchange, $validator;
public function setup() {
$this->interchange = new HTMLPurifier_ConfigSchema_Interchange();
if (empty($this->validator)) {
$class_to_test = substr(get_class($this), 0, -4);
$this->validator = new $class_to_test;
}
}
public function teardown() {
unset($this->validator, $this->interchange);
}
protected function expectSchemaException($msg) {
$this->expectException(new HTMLPurifier_ConfigSchema_Exception($msg));
}
}

View File

@ -14,63 +14,44 @@ class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
} }
function test_defineNamespace() { function test_defineNamespace() {
$this->schema->addNamespace('http', $d = 'This is an internet protocol.'); $this->schema->addNamespace('http');
$this->assertIdentical($this->schema->info['http'], array());
$this->assertIdentical($this->schema->info_namespace, array( $this->assertIdentical($this->schema->defaults['http'], array());
'http' => new HTMLPurifier_ConfigDef_Namespace($d)
));
} }
function test_define() { function test_define() {
$this->schema->addNamespace('Car', 'Automobiles, those gas-guzzlers!'); $this->schema->addNamespace('Car');
$this->schema->add('Car', 'Seats', 5, 'int', $d = 'Standard issue.'); $this->schema->add('Car', 'Seats', 5, 'int', false);
$this->assertIdentical($this->schema->defaults['Car']['Seats'], 5); $this->assertIdentical($this->schema->defaults['Car']['Seats'], 5);
$this->assertIdentical($this->schema->info['Car']['Seats'], $this->assertIdentical($this->schema->info['Car']['Seats'],
new HTMLPurifier_ConfigDef_Directive('int', $d) new HTMLPurifier_ConfigDef_Directive('int')
); );
$this->schema->add('Car', 'Age', null, 'int/null', $d = 'Not always known.'); $this->schema->add('Car', 'Age', null, 'int', true);
$this->assertIdentical($this->schema->defaults['Car']['Age'], null); $this->assertIdentical($this->schema->defaults['Car']['Age'], null);
$this->assertIdentical($this->schema->info['Car']['Age'], $this->assertIdentical($this->schema->info['Car']['Age'],
new HTMLPurifier_ConfigDef_Directive('int', $d, true) new HTMLPurifier_ConfigDef_Directive('int', true)
); );
$this->expectError('Cannot define directive for undefined namespace');
$this->schema->add('Train', 'Cars', 10, 'int', 'Including the caboose.');
$this->expectError('Directive name must be alphanumeric');
$this->schema->add('Car', 'Is it shiny?', true, 'bool', 'Indicates regular waxing.');
$this->expectError('Invalid type for configuration directive');
$this->schema->add('Car', 'Efficiency', 50, 'mpg', 'The higher the better.');
$this->expectError('Default value does not match directive type');
$this->schema->add('Car', 'Producer', 'Ford', 'int', 'ID of the company that made the car.');
$this->expectError('Description must be non-empty');
$this->schema->add('Car', 'ComplexAttribute', 'lawyers', 'istring', null);
} }
function test_defineAllowedValues() { function test_defineAllowedValues() {
$this->schema->addNamespace('QuantumNumber', 'D'); $this->schema->addNamespace('QuantumNumber', 'D');
$this->schema->add('QuantumNumber', 'Spin', 0.5, 'float', $this->schema->add('QuantumNumber', 'Spin', 0.5, 'float', false);
'Spin of particle. Fourth quantum number, represented by s.'); $this->schema->add('QuantumNumber', 'Current', 's', 'string', false);
$this->schema->add('QuantumNumber', 'Current', 's', 'string', $this->schema->add('QuantumNumber', 'Difficulty', null, 'string', true);
'Currently selected quantum number.');
$this->schema->add('QuantumNumber', 'Difficulty', null, 'string/null', $d = 'How hard are the problems?');
$this->schema->addAllowedValues( // okay, since default is null $this->schema->addAllowedValues( // okay, since default is null
'QuantumNumber', 'Difficulty', array('easy', 'medium', 'hard') 'QuantumNumber', 'Difficulty', array('easy' => true, 'medium' => true, 'hard' => true)
); );
$this->assertIdentical($this->schema->defaults['QuantumNumber']['Difficulty'], null); $this->assertIdentical($this->schema->defaults['QuantumNumber']['Difficulty'], null);
$this->assertIdentical($this->schema->info['QuantumNumber']['Difficulty'], $this->assertIdentical($this->schema->info['QuantumNumber']['Difficulty'],
new HTMLPurifier_ConfigDef_Directive( new HTMLPurifier_ConfigDef_Directive(
'string', 'string',
$d,
true, true,
array( array(
'easy' => true, 'easy' => true,
@ -80,30 +61,16 @@ class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
) )
); );
$this->expectError('Cannot define allowed values for undefined directive');
$this->schema->addAllowedValues(
'SpaceTime', 'Symmetry', array('time', 'spatial', 'projective')
);
$this->expectError('Cannot define allowed values for directive whose type is not string');
$this->schema->addAllowedValues(
'QuantumNumber', 'Spin', array(0.5, -0.5)
);
$this->expectError('Default value must be in allowed range of variables');
$this->schema->addAllowedValues(
'QuantumNumber', 'Current', array('n', 'l', 'm') // forgot s!
);
} }
function test_defineValueAliases() { function test_defineValueAliases() {
$this->schema->addNamespace('Abbrev', 'Stuff on abbreviations.'); $this->schema->addNamespace('Abbrev', 'Stuff on abbreviations.');
$this->schema->add('Abbrev', 'HTH', 'Happy to Help', 'string', $d = 'Three-letters'); $this->schema->add('Abbrev', 'HTH', 'Happy to Help', 'string', false);
$this->schema->addAllowedValues( $this->schema->addAllowedValues(
'Abbrev', 'HTH', array( 'Abbrev', 'HTH', array(
'Happy to Help', 'Happy to Help' => true,
'Hope that Helps', 'Hope that Helps' => true,
'HAIL THE HAND!' 'HAIL THE HAND!' => true,
) )
); );
$this->schema->addValueAliases( $this->schema->addValueAliases(
@ -122,7 +89,6 @@ class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
$this->assertIdentical($this->schema->info['Abbrev']['HTH'], $this->assertIdentical($this->schema->info['Abbrev']['HTH'],
new HTMLPurifier_ConfigDef_Directive( new HTMLPurifier_ConfigDef_Directive(
'string', 'string',
$d,
false, false,
array( array(
'Happy to Help' => true, 'Happy to Help' => true,
@ -137,25 +103,11 @@ class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
) )
); );
$this->expectError('Cannot define alias to value that is not allowed');
$this->schema->addValueAliases(
'Abbrev', 'HTH', array(
'head' => 'Head to Head'
)
);
$this->expectError('Cannot define alias over allowed value');
$this->schema->addValueAliases(
'Abbrev', 'HTH', array(
'Hope that Helps' => 'Happy to Help'
)
);
} }
function testAlias() { function testAlias() {
$this->schema->addNamespace('Home', 'Sweet home.'); $this->schema->addNamespace('Home');
$this->schema->add('Home', 'Rug', 3, 'int', 'ID.'); $this->schema->add('Home', 'Rug', 3, 'int', false);
$this->schema->addAlias('Home', 'Carpet', 'Home', 'Rug'); $this->schema->addAlias('Home', 'Carpet', 'Home', 'Rug');
$this->assertTrue(!isset($this->schema->defaults['Home']['Carpet'])); $this->assertTrue(!isset($this->schema->defaults['Home']['Carpet']));
@ -163,21 +115,6 @@ class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
new HTMLPurifier_ConfigDef_DirectiveAlias('Home', 'Rug') new HTMLPurifier_ConfigDef_DirectiveAlias('Home', 'Rug')
); );
$this->expectError('Cannot define directive alias in undefined namespace');
$this->schema->addAlias('Store', 'Rug', 'Home', 'Rug');
$this->expectError('Directive name must be alphanumeric');
$this->schema->addAlias('Home', 'R.g', 'Home', 'Rug');
$this->schema->add('Home', 'Rugger', 'Bob Max', 'string', 'Name of.');
$this->expectError('Cannot define alias over directive');
$this->schema->addAlias('Home', 'Rugger', 'Home', 'Rug');
$this->expectError('Cannot define alias to undefined directive');
$this->schema->addAlias('Home', 'Rug2', 'Home', 'Rugavan');
$this->expectError('Cannot define alias to alias');
$this->schema->addAlias('Home', 'Rug2', 'Home', 'Carpet');
} }
} }

View File

@ -17,20 +17,17 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
// test functionality based on ConfigSchema // test functionality based on ConfigSchema
function testNormal() { function testNormal() {
$this->schema->addNamespace('Element', 'Chemical substances that cannot be further decomposed'); $this->schema->addNamespace('Element');
$this->schema->add('Element', 'Abbr', 'H', 'string', 'Abbreviation of element name.'); $this->schema->add('Element', 'Abbr', 'H', 'string', false);
$this->schema->add('Element', 'Name', 'hydrogen', 'istring', 'Full name of atoms.'); $this->schema->add('Element', 'Name', 'hydrogen', 'istring', false);
$this->schema->add('Element', 'Number', 1, 'int', 'Atomic number, is identity.'); $this->schema->add('Element', 'Number', 1, 'int', false);
$this->schema->add('Element', 'Mass', 1.00794, 'float', 'Atomic mass.'); $this->schema->add('Element', 'Mass', 1.00794, 'float', false);
$this->schema->add('Element', 'Radioactive', false, 'bool', 'Does it have rapid decay?'); $this->schema->add('Element', 'Radioactive', false, 'bool', false);
$this->schema->add('Element', 'Isotopes', array(1 => true, 2 => true, 3 => true), 'lookup', $this->schema->add('Element', 'Isotopes', array(1 => true, 2 => true, 3 => true), 'lookup', false);
'What numbers of neutrons for this element have been observed?'); $this->schema->add('Element', 'Traits', array('nonmetallic', 'odorless', 'flammable'), 'list', false);
$this->schema->add('Element', 'Traits', array('nonmetallic', 'odorless', 'flammable'), 'list', $this->schema->add('Element', 'IsotopeNames', array(1 => 'protium', 2 => 'deuterium', 3 => 'tritium'), 'hash', false);
'What are general properties of the element?'); $this->schema->add('Element', 'Object', new stdClass(), 'mixed', false);
$this->schema->add('Element', 'IsotopeNames', array(1 => 'protium', 2 => 'deuterium', 3 => 'tritium'), 'hash',
'Lookup hash of neutron counts to formal names.');
$this->schema->add('Element', 'Object', new stdClass(), 'mixed', 'Model representation.');
$config = new HTMLPurifier_Config($this->schema); $config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false; $config->autoFinalize = false;
@ -84,16 +81,18 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
$this->schema->addNamespace('Instrument', 'Of the musical type.'); $this->schema->addNamespace('Instrument', 'Of the musical type.');
// case sensitive // case sensitive
$this->schema->add('Instrument', 'Manufacturer', 'Yamaha', 'string', 'Who made it?'); $this->schema->add('Instrument', 'Manufacturer', 'Yamaha', 'string', false);
$this->schema->addAllowedValues('Instrument', 'Manufacturer', array( $this->schema->addAllowedValues('Instrument', 'Manufacturer', array(
'Yamaha', 'Conn-Selmer', 'Vandoren', 'Laubin', 'Buffet', 'other')); 'Yamaha' => true, 'Conn-Selmer' => true, 'Vandoren' => true,
'Laubin' => true, 'Buffet' => true, 'other' => true));
$this->schema->addValueAliases('Instrument', 'Manufacturer', array( $this->schema->addValueAliases('Instrument', 'Manufacturer', array(
'Selmer' => 'Conn-Selmer')); 'Selmer' => 'Conn-Selmer'));
// case insensitive // case insensitive
$this->schema->add('Instrument', 'Family', 'woodwind', 'istring', 'What family is it?'); $this->schema->add('Instrument', 'Family', 'woodwind', 'istring', false);
$this->schema->addAllowedValues('Instrument', 'Family', array( $this->schema->addAllowedValues('Instrument', 'Family', array(
'brass', 'woodwind', 'percussion', 'string', 'keyboard', 'electronic')); 'brass' => true, 'woodwind' => true, 'percussion' => true,
'string' => true, 'keyboard' => true, 'electronic' => true));
$this->schema->addValueAliases('Instrument', 'Family', array( $this->schema->addValueAliases('Instrument', 'Family', array(
'synth' => 'electronic')); 'synth' => 'electronic'));
@ -129,9 +128,9 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function testNull() { function testNull() {
$this->schema->addNamespace('ReportCard', 'It is for grades.'); $this->schema->addNamespace('ReportCard');
$this->schema->add('ReportCard', 'English', null, 'string/null', 'Grade from English class.'); $this->schema->add('ReportCard', 'English', null, 'string', false);
$this->schema->add('ReportCard', 'Absences', 0, 'int', 'How many times missing from school?'); $this->schema->add('ReportCard', 'Absences', 0, 'int', false);
$config = new HTMLPurifier_Config($this->schema); $config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false; $config->autoFinalize = false;
@ -150,8 +149,8 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function testAliases() { function testAliases() {
$this->schema->addNamespace('Home', 'Sweet home.'); $this->schema->addNamespace('Home');
$this->schema->add('Home', 'Rug', 3, 'int', 'ID.'); $this->schema->add('Home', 'Rug', 3, 'int', false);
$this->schema->addAlias('Home', 'Carpet', 'Home', 'Rug'); $this->schema->addAlias('Home', 'Carpet', 'Home', 'Rug');
$config = new HTMLPurifier_Config($this->schema); $config = new HTMLPurifier_Config($this->schema);
@ -171,9 +170,9 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_getBatch() { function test_getBatch() {
$this->schema->addNamespace('Variables', 'Changing quantities in equation.'); $this->schema->addNamespace('Variables');
$this->schema->add('Variables', 'TangentialAcceleration', 'a_tan', 'string', 'In m/s^2'); $this->schema->add('Variables', 'TangentialAcceleration', 'a_tan', 'string', false);
$this->schema->add('Variables', 'AngularAcceleration', 'alpha', 'string', 'In rad/s^2'); $this->schema->add('Variables', 'AngularAcceleration', 'alpha', 'string', false);
$config = new HTMLPurifier_Config($this->schema); $config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false; $config->autoFinalize = false;
@ -196,9 +195,9 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_loadIni() { function test_loadIni() {
$this->schema->addNamespace('Shortcut', 'Keyboard shortcuts for commands'); $this->schema->addNamespace('Shortcut', 'Keyboard shortcuts for commands');
$this->schema->add('Shortcut', 'Copy', 'c', 'istring', 'Copy text'); $this->schema->add('Shortcut', 'Copy', 'c', 'istring', false);
$this->schema->add('Shortcut', 'Paste', 'v', 'istring', 'Paste clipboard'); $this->schema->add('Shortcut', 'Paste', 'v', 'istring', false);
$this->schema->add('Shortcut', 'Cut', 'x', 'istring', 'Cut text'); $this->schema->add('Shortcut', 'Cut', 'x', 'istring', false);
$config = new HTMLPurifier_Config($this->schema); $config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false; $config->autoFinalize = false;
@ -275,13 +274,11 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_loadArray() { function test_loadArray() {
// setup a few dummy namespaces/directives for our testing // setup a few dummy namespaces/directives for our testing
$this->schema->addNamespace('Zoo', 'Animals we have.'); $this->schema->addNamespace('Zoo');
$this->schema->add('Zoo', 'Aadvark', 0, 'int', 'Have?'); $this->schema->add('Zoo', 'Aadvark', 0, 'int', false);
$this->schema->add('Zoo', 'Boar', 0, 'int', 'Have?'); $this->schema->add('Zoo', 'Boar', 0, 'int', false);
$this->schema->add('Zoo', 'Camel', 0, 'int', 'Have?'); $this->schema->add('Zoo', 'Camel', 0, 'int', false);
$this->schema->add( $this->schema->add('Zoo', 'Others', array(), 'list', false);
'Zoo', 'Others', array(), 'list', 'Other animals we have one of.'
);
$config_manual = new HTMLPurifier_Config($this->schema); $config_manual = new HTMLPurifier_Config($this->schema);
$config_loadabbr = new HTMLPurifier_Config($this->schema); $config_loadabbr = new HTMLPurifier_Config($this->schema);
@ -317,9 +314,9 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_create() { function test_create() {
$this->schema->addNamespace('Cake', 'Properties of it.'); $this->schema->addNamespace('Cake');
$this->schema->add('Cake', 'Sprinkles', 666, 'int', 'Number of.'); $this->schema->add('Cake', 'Sprinkles', 666, 'int', false);
$this->schema->add('Cake', 'Flavor', 'vanilla', 'string', 'Flavor of the batter.'); $this->schema->add('Cake', 'Flavor', 'vanilla', 'string', false);
$config = new HTMLPurifier_Config($this->schema); $config = new HTMLPurifier_Config($this->schema);
$config->set('Cake', 'Sprinkles', 42); $config->set('Cake', 'Sprinkles', 42);
@ -342,8 +339,8 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
// test finalization // test finalization
$this->schema->addNamespace('Poem', 'Violets are red, roses are blue...'); $this->schema->addNamespace('Poem');
$this->schema->add('Poem', 'Meter', 'iambic', 'string', 'Rhythm of poem.'); $this->schema->add('Poem', 'Meter', 'iambic', 'string', false);
$config = new HTMLPurifier_Config($this->schema); $config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false; $config->autoFinalize = false;
@ -365,17 +362,17 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function __onlytest_loadArrayFromForm() { function __onlytest_loadArrayFromForm() {
$this->schema->addNamespace('Pancake', 'This should not be user customizable'); $this->schema->addNamespace('Pancake');
$this->schema->add('Pancake', 'Mix', 'buttermilk', 'string', 'Type of pancake mix to use.'); $this->schema->add('Pancake', 'Mix', 'buttermilk', 'string', false);
$this->schema->add('Pancake', 'Served', true, 'bool', 'But this is customizable by user.'); $this->schema->add('Pancake', 'Served', true, 'bool', false);
$this->schema->addNamespace('Toppings', 'This is user customizable'); $this->schema->addNamespace('Toppings', false);
$this->schema->add('Toppings', 'Syrup', true, 'bool', 'Absolutely standard!'); $this->schema->add('Toppings', 'Syrup', true, 'bool', false);
$this->schema->add('Toppings', 'Flavor', 'maple', 'string', 'What flavor is the syrup?'); $this->schema->add('Toppings', 'Flavor', 'maple', 'string', false);
$this->schema->add('Toppings', 'Strawberries', 3, 'int', 'Quite delightful fruit.'); $this->schema->add('Toppings', 'Strawberries', 3, 'int', false);
$this->schema->add('Toppings', 'Calories', 2000, 'int/null', 'Some things are best left unknown.'); $this->schema->add('Toppings', 'Calories', 2000, 'int', true);
$this->schema->add('Toppings', 'DefinitionID', null, 'string/null', 'Do not let this be set'); $this->schema->add('Toppings', 'DefinitionID', null, 'string', true);
$this->schema->add('Toppings', 'DefinitionRev', 1, 'int', 'Do not let this be set'); $this->schema->add('Toppings', 'DefinitionRev', 1, 'int', false);
$this->schema->add('Toppings', 'Protected', 1, 'int', 'Do not let this be set'); $this->schema->add('Toppings', 'Protected', 1, 'int', false);
$get = array( $get = array(
'breakfast' => array( 'breakfast' => array(
@ -426,16 +423,16 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
} }
function test_getAllowedDirectivesForForm() { function test_getAllowedDirectivesForForm() {
$this->schema->addNamespace('Unused', 'Not mentioned, so deny'); $this->schema->addNamespace('Unused');
$this->schema->add('Unused', 'Unused', 'Foobar', 'string', 'Not mentioned, do not allow'); $this->schema->add('Unused', 'Unused', 'Foobar', 'string', false);
$this->schema->addNamespace('Partial', 'Some are mentioned, allow only those'); $this->schema->addNamespace('Partial');
$this->schema->add('Partial', 'Allowed', true, 'bool', 'Mentioned, allowed'); $this->schema->add('Partial', 'Allowed', true, 'bool', false);
$this->schema->add('Partial', 'Unused', 'Foobar', 'string', 'Not mentioned, do not allow'); $this->schema->add('Partial', 'Unused', 'Foobar', 'string', false);
$this->schema->addNamespace('All', 'Entire namespace allowed, allow all unless...'); $this->schema->addNamespace('All');
$this->schema->add('All', 'Allowed', true, 'bool', 'Not mentioned, allowed'); $this->schema->add('All', 'Allowed', true, 'bool', false);
$this->schema->add('All', 'Blacklisted', 'Foobar', 'string', 'Specifically blacklisted'); $this->schema->add('All', 'Blacklisted', 'Foobar', 'string', false); // explicitly blacklisted
$this->schema->add('All', 'DefinitionID', 'Foobar', 'string/null', 'Special case, auto-blacklisted'); $this->schema->add('All', 'DefinitionID', 'Foobar', 'string', true); // auto-blacklisted
$this->schema->add('All', 'DefinitionRev', 2, 'int', 'Special case, auto-blacklisted'); $this->schema->add('All', 'DefinitionRev', 2, 'int', false); // auto-blacklisted
$input = array('Partial.Allowed', 'All', '-All.Blacklisted'); $input = array('Partial.Allowed', 'All', '-All.Blacklisted');
$output = HTMLPurifier_Config::getAllowedDirectivesForForm($input, $this->schema); $output = HTMLPurifier_Config::getAllowedDirectivesForForm($input, $this->schema);