0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2024-09-18 18:25:18 +00:00

Revamp configuration backend.

Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
This commit is contained in:
Edward Z. Yang 2009-02-07 02:53:20 -05:00
parent fcbf724e6e
commit b107eec452
57 changed files with 483 additions and 529 deletions

3
NEWS
View File

@ -10,6 +10,9 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
==========================
4.0.0, unknown release date
# APIs for ConfigSchema subsystem have substantially changed. See
docs/dev-config-bcbreaks.txt for details; in essence, anything that
had both namespace and directive now have a single unified key.
! More robust support for name="" and id=""
3.3.0, released 2009-02-16

View File

@ -0,0 +1,79 @@
Configuration Backwards-Compatibility Breaks
In version 3.3.0, the configuration subsystem (composed of the outwards
facing Config class, as well as the ConfigSchema and ConfigSchema_Interchange
subsystems), was significantly revamped to make use of property lists.
While most of the changes are internal, some internal APIs were changed for the
sake of clarity. HTMLPurifier_Config was kept completely backwards compatible,
although some of the functions were retrofitted with an unambiguous alternate
syntax. Both of these changes are discussed in this document.
1. Outwards Facing Changes
--------------------------------------------------------------------------------
The HTMLPurifier_Config class now takes an alternate syntax. The general rule
is:
If you passed $namespace, $directive, pass "$namespace.$directive"
instead.
An example:
$config->set('HTML', 'Allowed', 'p');
becomes:
$config->set('HTML.Allowed', 'p');
New configuration options may have more than one namespace, they might
look something like %Filter.YouTube.Blacklist. While you could technically
set it with ('HTML', 'YouTube.Blacklist'), the logical extension
('HTML', 'YouTube', 'Blacklist') does not work.
[more changes coming]
2. Internal API Changes
--------------------------------------------------------------------------------
Some overarching notes: we've completely eliminated the notion of namespace;
it's now an informal construct for organizing related configuration directives.
Also, the validation routines for keys (formerly "$namespace.$directive")
have been completely relaxed. I don't think it really should be necessary.
2.1 HTMLPurifier_ConfigSchema
First off, if you're interfacing with this class, you really shouldn't.
HTMLPurifier_ConfigSchema_Builder_ConfigSchema is really the only class that
should ever be creating HTMLPurifier_ConfigSchema, and HTMLPurifier_Config the
only class that should be reading it.
All namespace related methods were removed; they are completely unnecessary
now. Any $namespace, $name arguments must be replaced with $key (where
$key == "$namespace.$name"), including for addAlias().
The $info and $defaults member variables are no longer indexed as
[$namespace][$name]; they are now indexed as ["$namespace.$name"].
All deprecated methods were finally removed, after having yelled at you as
an E_USER_NOTICE for a while now.
2.2 HTMLPurifier_ConfigSchema_Interchange
Member variable $namespaces was removed.
2.3 HTMLPurifier_ConfigSchema_Interchange_Id
Member variable $namespace and $directive removed; member variable $key added.
Any method that took $namespace, $directive now takes $key.
2.4 HTMLPurifier_ConfigSchema_Interchange_Namespace
Removed.

217
docs/proposal-plists.txt Normal file
View File

@ -0,0 +1,217 @@
THE UNIVERSAL DESIGN PATTERN: PROPERTIES
Steve Yegge
Implementation:
get(name)
put(name, value)
has(name)
remove(name)
iteration, with filtering [this will be our namespaces]
parent
Representations:
- Keys are strings
- It's nice to not need to quote keys (if we formulate our own language,
consider this)
- Property not present representation (key missing)
- Frequent removal/re-add may have null help. If null is valid, use
another value. (PHP semantics are weird here)
Data structures:
- LinkedHashMap is wonderful (O(1) access and maintains order)
- Using a special property that points to the parent is usual
- Multiple inheritance possible, need rules for which to lookup first
- Iterative inheritance is best
- Consider performance!
Deletion
- Tricky problem with inheritance
- Distinguish between "not found" and "look in my parent for the property"
[Maybe HTML Purifier won't allow deletion]
Read/write asymmetry (it's correct!)
Read-only plists
- Allow ability to freeze [this is what we have already]
- Don't overuse it
Performance:
- Intern strings (PHP does this already)
- Don't be case-insensitive
- If all properties in a plist are known a-priori, you can use a "perfect"
hash function. Often overkill.
- Copy-on-read caching "plundering" reduces lookup, but uses memory and can
grow stale. Use as last resort.
- Refactoring to fields. Watch for API compatibility, system complexity,
and lack of flexibility.
- Refrigerator: external data-structure to hold plists
Transient properties:
[Don't need to worry about this]
- Use a separate plist for transient properties
- Non-numeric override; numeric should ADD
- Deletion: removeTransientProperty() and transientlyRemoveProperty()
Persistence:
- XML/JSON are good
- Text-based is good for readability, maintainability and bootstrapping
- Compressed binary format for network transport [not necessary]
- RDBMS or XML database
Querying: [not relevant]
- XML database is nice for XPath/XQuery
- jQuery for JSON
- Just load it all into a program
Backfills/Data integrity:
- Use usual methods
- Lazy backfill is a nice hack
Type systems:
- Flags: ReadOnly, Permanent, DontEnum
- Typed properties isn't that useful [It's also Not-PHP]
- Seperate meta-list of directive properties IS useful
- Duck typing is useful for systems designed fully around properties pattern
Trade-off:
+ Flexibility
+ Extensibility
+ Unit-testing/prototype-speed
- Performance
- Data integrity
- Navagability/Query-ability
- Reversability (hard to go back)
HTML Purifier
We are not happy with our current system of defining configuration directives,
because it has become clear that things will get a lot nicer if we allow
multiple namespaces, and there are some features that naturally lend themselves
to inheritance, which we do not really support well.
One of the considered implementation changes would be to go from a structure
like:
array(
'Namespace' => array(
'Directive' => 'val1',
'Directive2' => 'val2',
)
)
to:
array(
'Namespace.Directive' => 'val1',
'Namespace.Directive2' => 'val2',
)
The below implementation takes more memory, however, and it makes it a bit
complicated to grab all values from a namespace.
The alternate implementation choice is to allow nested plists. This keeps
iteration easy, but is problematic for inheritance (it would be difficult
to distinguish a plist from an array) and retrieval (when specifying multiple
namespaces we would need some multiple de-referencing).
----
We can bite the performance hit, and just do iteration with filter
(the strncmp call should be relatively cheap). Then, users should be able
to optimize doing something like:
$config = HTMLPurifier_Config::createDefault();
if (!file_exists('config.php')) {
// set up $config
$config->save('config.php');
} else {
$config->load('config.php');
}
Or maybe memcache, or something. This means that "// set up $config" must
not have any dynamic parts, or the user has to invalidate the cache when
they do update it. We have to think about this a little more carefully; the
file call might be more expensive.
----
This might get expensive, however, when we actually care about iterating
over the configuration and want the actual values. So what about nesting the
lists?
"ns.sub.directive" => values['ns']['sub']['directive']
We can distinguish between plists and arrays by using ArrayObjects for the
plists, and regular arrays for the arrays? Alternatively, use ArrayObjects
for the arrays, and regular arrays for the plists.
----
Implementation demands, and what has caused them:
1. DefinitionCache, the HTML, CSS and URI namespaces have caches attached to them
Results:
- getBatchSerial()
- getBatch() : in general, the ability to traverse just a namespace
2. AutoFormat/Filter, this is a plugin architecture, directives not hard-coded
- getBatch()
3. Configuration form
- Namespaces used to organize directives
Other than that, we have a pure plist. PERHAPS we should maintain separate things
for these different demands.
Issue 2: Directives for configuring the plugins are regular plists, but
when enabling them, while it's "plist-ish", what you're really doing is adding
them to an array of "autoformatters"/"filters" to enable. We can setup
magic BC as well as in the new interface, but there should also be an
add('AutoFormat', 'AutoParagraph'); which does the right thing.
One thing to consider is whether or not inheritance rules will apply to these.
I'd say yes. That means that they're still plisty, in fact, the underlying
implementation will probably be a plist. However, they will get their OWN
plists, and will NOT support nesting.
Issue 1: Our current implementation is generally not efficient; md5(serialize($foo))
is pretty expensive. So, I don't think there will be any problems if it
gets "less" efficient, as long as we give users a properly fast alternative;
DefinitionRev gives us a way to do this, by simply telling the user they must
update it whenever they update Configuration directives as well. (There are
obvious BC concerns here).
In such a case, we simply iterate over our plist (performing full retrievals
for each value), grab the entries we care about, and then serialize and hash.
It's going to be slow either way, due to the ability of plists to inherit.
If we ksort(), we don't have to traverse the entire array, however, the
cost of a ksort() call may not be worth it.
At this point, last time, I started worrying about the performance implications
of allowing inheritance, and wondering whether or not I wanted to squash
the plist. At first blush, our code might be under the assumption that
accessing properties is cheap; but actually we prefer to copy out the value
into a member variable if it's going to be used many times. With this is mind
I don't think CPU consumption from a few nested function calls is going to
be a problem. We *are* going to enforce a function only interface.
The next issue at hand is how we're going to manage the "special" plists,
which should still be able to be inherited. Basically, it means that multiple
plists would be attached to the configuration object, which is not the
best for memory performance. The alternative is to keep them all in one
big plist, and then eat the one-time cost of traversing the entire plist
to grab the appropriate values.
I think at this point we can write the generic interface, and then set up separate
plists if that ends up being necessary for performance (it probably won't.) Now
lets code our generic plist implementation.
----
Iterating over the plist presents some problems. The way we've chosen to solve
this is to squash all of the parents.
----
But I don't need iteration.

View File

@ -117,21 +117,22 @@ class HTMLPurifier_Config
* @param $namespace String namespace
* @param $key String key
*/
public function get($namespace, $key) {
public function get($namespace, $directive) {
$key = "$namespace.$directive";
if (!$this->finalized) $this->autoFinalize ? $this->finalize() : $this->plist->squash(true);
if (!isset($this->def->info[$namespace][$key])) {
if (!isset($this->def->info[$key])) {
// can't add % due to SimpleTest bug
trigger_error('Cannot retrieve value of undefined directive ' . htmlspecialchars("$namespace.$key"),
trigger_error('Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
E_USER_WARNING);
return;
}
if (isset($this->def->info[$namespace][$key]->isAlias)) {
$d = $this->def->info[$namespace][$key];
trigger_error('Cannot get value from aliased directive, use real name ' . $d->namespace . '.' . $d->name,
if (isset($this->def->info[$key]->isAlias)) {
$d = $this->def->info[$key];
trigger_error('Cannot get value from aliased directive, use real name ' . $d->key,
E_USER_ERROR);
return;
}
return $this->plist->get("$namespace.$key");
return $this->plist->get($key);
}
/**
@ -140,12 +141,12 @@ class HTMLPurifier_Config
*/
public function getBatch($namespace) {
if (!$this->finalized) $this->autoFinalize ? $this->finalize() : $this->plist->squash(true);
if (!isset($this->def->info[$namespace])) {
$full = $this->getAll();
if (!isset($full[$namespace])) {
trigger_error('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace),
E_USER_WARNING);
return;
}
$full = $this->getAll();
return $full[$namespace];
}
@ -195,25 +196,26 @@ class HTMLPurifier_Config
* @param $key String key
* @param $value Mixed value
*/
public function set($namespace, $key, $value, $from_alias = false) {
public function set($namespace, $directive, $value, $from_alias = false) {
$key = "$namespace.$directive";
if ($this->isFinalized('Cannot set directive after finalization')) return;
if (!isset($this->def->info[$namespace][$key])) {
trigger_error('Cannot set undefined directive ' . htmlspecialchars("$namespace.$key") . ' to value',
if (!isset($this->def->info[$key])) {
trigger_error('Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
E_USER_WARNING);
return;
}
$def = $this->def->info[$namespace][$key];
$def = $this->def->info[$key];
if (isset($def->isAlias)) {
if ($from_alias) {
trigger_error('Double-aliases not allowed, please fix '.
'ConfigSchema bug with' . "$namespace.$key", E_USER_ERROR);
'ConfigSchema bug with' . $key, E_USER_ERROR);
return;
}
$this->set($new_ns = $def->namespace,
$new_dir = $def->name,
list($alias_namespace, $alias_directive) = explode('.', $def->key, 2);
$this->set($alias_namespace, $alias_directive,
$value, true);
trigger_error("$namespace.$key is an alias, preferred directive name is $new_ns.$new_dir", E_USER_NOTICE);
trigger_error("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
return;
}
@ -231,7 +233,7 @@ class HTMLPurifier_Config
try {
$value = $this->parser->parse($value, $type, $allow_null);
} catch (HTMLPurifier_VarParserException $e) {
trigger_error('Value for ' . "$namespace.$key" . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING);
trigger_error('Value for ' . $key . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING);
return;
}
if (is_string($value) && is_object($def)) {
@ -246,7 +248,7 @@ class HTMLPurifier_Config
return;
}
}
$this->plist->set("$namespace.$key", $value);
$this->plist->set($key, $value);
// reset definitions if the directives they depend on changed
// this is a very costly process, so it's discouraged
@ -351,8 +353,7 @@ class HTMLPurifier_Config
foreach ($config_array as $key => $value) {
$key = str_replace('_', '.', $key);
if (strpos($key, '.') !== false) {
// condensed form
list($namespace, $directive) = explode('.', $key);
list($namespace, $directive) = explode(".", $key, 2);
$this->set($namespace, $directive, $value);
} else {
$namespace = $key;
@ -394,16 +395,15 @@ class HTMLPurifier_Config
}
}
$ret = array();
foreach ($schema->info as $ns => $keypairs) {
foreach ($keypairs as $directive => $def) {
if ($allowed !== true) {
if (isset($blacklisted_directives["$ns.$directive"])) continue;
if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue;
}
if (isset($def->isAlias)) continue;
if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
$ret[] = array($ns, $directive);
foreach ($schema->info as $key => $def) {
list($ns, $directive) = explode('.', $key, 2);
if ($allowed !== true) {
if (isset($blacklisted_directives["$ns.$directive"])) continue;
if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue;
}
if (isset($def->isAlias)) continue;
if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
$ret[] = array($ns, $directive);
}
return $ret;
}

View File

@ -87,24 +87,13 @@ class HTMLPurifier_ConfigSchema {
* HTMLPurifier_DirectiveDef::$type for allowed values
* @param $allow_null Whether or not to allow null values
*/
public function add($namespace, $name, $default, $type, $allow_null) {
public function add($key, $default, $type, $allow_null) {
$obj = new stdclass();
$obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
if ($allow_null) $obj->allow_null = true;
$this->info[$namespace][$name] = $obj;
$this->defaults[$namespace][$name] = $default;
$this->defaultPlist->set("$namespace.$name", $default);
}
/**
* Defines a namespace for directives to be put into.
* @warning This is slightly different from the corresponding static
* method.
* @param $namespace Namespace's name
*/
public function addNamespace($namespace) {
$this->info[$namespace] = array();
$this->defaults[$namespace] = array();
$this->info[$key] = $obj;
$this->defaults[$key] = $default;
$this->defaultPlist->set($key, $default);
}
/**
@ -116,12 +105,12 @@ class HTMLPurifier_ConfigSchema {
* @param $name Name of Directive
* @param $aliases Hash of aliased values to the real alias
*/
public function addValueAliases($namespace, $name, $aliases) {
if (!isset($this->info[$namespace][$name]->aliases)) {
$this->info[$namespace][$name]->aliases = array();
public function addValueAliases($key, $aliases) {
if (!isset($this->info[$key]->aliases)) {
$this->info[$key]->aliases = array();
}
foreach ($aliases as $alias => $real) {
$this->info[$namespace][$name]->aliases[$alias] = $real;
$this->info[$key]->aliases[$alias] = $real;
}
}
@ -133,8 +122,8 @@ class HTMLPurifier_ConfigSchema {
* @param $name Name of directive
* @param $allowed Lookup array of allowed values
*/
public function addAllowedValues($namespace, $name, $allowed) {
$this->info[$namespace][$name]->allowed = $allowed;
public function addAllowedValues($key, $allowed) {
$this->info[$key]->allowed = $allowed;
}
/**
@ -144,88 +133,26 @@ class HTMLPurifier_ConfigSchema {
* @param $new_namespace
* @param $new_name Directive that the alias will be to
*/
public function addAlias($namespace, $name, $new_namespace, $new_name) {
public function addAlias($key, $new_key) {
$obj = new stdclass;
$obj->namespace = $new_namespace;
$obj->name = $new_name;
$obj->key = $new_key;
$obj->isAlias = true;
$this->info[$namespace][$name] = $obj;
$this->info[$key] = $obj;
}
/**
* Replaces any stdclass that only has the type property with type integer.
*/
public function postProcess() {
foreach ($this->info as $namespace => $info) {
foreach ($info as $directive => $v) {
if (count((array) $v) == 1) {
$this->info[$namespace][$directive] = $v->type;
} elseif (count((array) $v) == 2 && isset($v->allow_null)) {
$this->info[$namespace][$directive] = -$v->type;
}
foreach ($this->info as $key => $v) {
if (count((array) $v) == 1) {
$this->info[$key] = $v->type;
} elseif (count((array) $v) == 2 && isset($v->allow_null)) {
$this->info[$key] = -$v->type;
}
}
}
// DEPRECATED METHODS
/** @see HTMLPurifier_ConfigSchema->set() */
public static function define($namespace, $name, $default, $type, $description) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$type_values = explode('/', $type, 2);
$type = $type_values[0];
$modifier = isset($type_values[1]) ? $type_values[1] : false;
$allow_null = ($modifier === 'null');
$def = HTMLPurifier_ConfigSchema::instance();
$def->add($namespace, $name, $default, $type, $allow_null);
}
/** @see HTMLPurifier_ConfigSchema->addNamespace() */
public static function defineNamespace($namespace, $description) {
HTMLPurifier_ConfigSchema::deprecated(__METHOD__);
$def = HTMLPurifier_ConfigSchema::instance();
$def->addNamespace($namespace);
}
/** @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() */
public function validate($a, $b, $c = false) {
trigger_error("HTMLPurifier_ConfigSchema->validate deprecated, use HTMLPurifier_VarParser->parse instead", E_USER_NOTICE);
$parser = new HTMLPurifier_VarParser();
return $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);
}
}
// vim: et sw=4 sts=4

View File

@ -9,36 +9,28 @@ class HTMLPurifier_ConfigSchema_Builder_ConfigSchema
public function build($interchange) {
$schema = new HTMLPurifier_ConfigSchema();
foreach ($interchange->namespaces as $n) {
$schema->addNamespace($n->namespace);
}
foreach ($interchange->directives as $d) {
$schema->add(
$d->id->namespace,
$d->id->directive,
$d->id->key,
$d->default,
$d->type,
$d->typeAllowsNull
);
if ($d->allowed !== null) {
$schema->addAllowedValues(
$d->id->namespace,
$d->id->directive,
$d->id->key,
$d->allowed
);
}
foreach ($d->aliases as $alias) {
$schema->addAlias(
$alias->namespace,
$alias->directive,
$d->id->namespace,
$d->id->directive
$alias->key,
$d->id->key
);
}
if ($d->valueAliases !== null) {
$schema->addValueAliases(
$d->id->namespace,
$d->id->directive,
$d->id->key,
$d->valueAliases
);
}

View File

@ -8,6 +8,7 @@ class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
{
protected $interchange;
private $namespace;
protected function writeHTMLDiv($html) {
$this->startElement('div');
@ -34,36 +35,33 @@ class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
$this->startElement('configdoc');
$this->writeElement('title', $interchange->name);
foreach ($interchange->namespaces as $namespace) {
$this->buildNamespace($namespace);
foreach ($interchange->directives as $directive) {
$this->buildDirective($directive);
}
if ($this->namespace) $this->endElement(); // namespace
$this->endElement(); // configdoc
$this->flush();
}
public function buildNamespace($namespace) {
$this->startElement('namespace');
$this->writeAttribute('id', $namespace->namespace);
public function buildDirective($directive) {
$this->writeElement('name', $namespace->namespace);
$this->startElement('description');
$this->writeHTMLDiv($namespace->description);
$this->endElement(); // description
foreach ($this->interchange->directives as $directive) {
if ($directive->id->namespace !== $namespace->namespace) continue;
$this->buildDirective($directive);
// Kludge, although I suppose having a notion of a "root namespace"
// certainly makes things look nicer when documentation is built.
// Depends on things being sorted.
if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) {
if ($this->namespace) $this->endElement(); // namespace
$this->namespace = $directive->id->getRootNamespace();
$this->startElement('namespace');
$this->writeAttribute('id', $this->namespace);
$this->writeElement('name', $this->namespace);
}
$this->endElement(); // namespace
}
public function buildDirective($directive) {
$this->startElement('directive');
$this->writeAttribute('id', $directive->id->toString());
$this->writeElement('name', $directive->id->directive);
$this->writeElement('name', $directive->id->getDirective());
$this->startElement('aliases');
foreach ($directive->aliases as $alias) $this->writeElement('alias', $alias->toString());

View File

@ -13,26 +13,11 @@ class HTMLPurifier_ConfigSchema_Interchange
*/
public $name;
/**
* Array of Namespace ID => array(namespace info)
*/
public $namespaces = array();
/**
* Array of Directive ID => array(directive info)
*/
public $directives = array();
/**
* Adds a namespace array to $namespaces
*/
public function addNamespace($namespace) {
if (isset($this->namespaces[$i = $namespace->namespace])) {
throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine namespace '$i'");
}
$this->namespaces[$i] = $namespace;
}
/**
* Adds a directive array to $directives
*/

View File

@ -6,11 +6,10 @@
class HTMLPurifier_ConfigSchema_Interchange_Id
{
public $namespace, $directive;
public $key;
public function __construct($namespace, $directive) {
$this->namespace = $namespace;
$this->directive = $directive;
public function __construct($key) {
$this->key = $key;
}
/**
@ -18,12 +17,19 @@ class HTMLPurifier_ConfigSchema_Interchange_Id
* cause problems for PHP 5.0 support.
*/
public function toString() {
return $this->namespace . '.' . $this->directive;
return $this->key;
}
public function getRootNamespace() {
return substr($this->key, 0, strpos($this->key, "."));
}
public function getDirective() {
return substr($this->key, strpos($this->key, ".") + 1);
}
public static function make($id) {
list($namespace, $directive) = explode('.', $id);
return new HTMLPurifier_ConfigSchema_Interchange_Id($namespace, $directive);
return new HTMLPurifier_ConfigSchema_Interchange_Id($id);
}
}

View File

@ -1,21 +0,0 @@
<?php
/**
* Interchange component class describing namespaces.
*/
class HTMLPurifier_ConfigSchema_Interchange_Namespace
{
/**
* Name of namespace defined.
*/
public $namespace;
/**
* HTML description.
*/
public $description;
}
// vim: et sw=4 sts=4

View File

@ -55,22 +55,17 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID');
}
if (strpos($hash['ID'], '.') === false) {
$this->buildNamespace($interchange, $hash);
if (count($hash) == 2 && isset($hash['DESCRIPTION'])) {
$hash->offsetGet('DESCRIPTION'); // prevent complaining
} else {
throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace');
}
} else {
$this->buildDirective($interchange, $hash);
}
$this->_findUnused($hash);
}
public function buildNamespace($interchange, $hash) {
$namespace = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
$namespace->namespace = $hash->offsetGet('ID');
if (isset($hash['DESCRIPTION'])) {
$namespace->description = $hash->offsetGet('DESCRIPTION');
}
$interchange->addNamespace($namespace);
}
public function buildDirective($interchange, $hash) {
$directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();

View File

@ -39,10 +39,6 @@ class HTMLPurifier_ConfigSchema_Validator
$this->aliases = array();
// PHP is a bit lax with integer <=> string conversions in
// arrays, so we don't use the identical !== comparison
foreach ($interchange->namespaces as $i => $namespace) {
if ($i != $namespace->namespace) $this->error(false, "Integrity violation: key '$i' does not match internal id '{$namespace->namespace}'");
$this->validateNamespace($namespace);
}
foreach ($interchange->directives as $i => $directive) {
$id = $directive->id->toString();
if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
@ -51,20 +47,6 @@ class HTMLPurifier_ConfigSchema_Validator
return true;
}
/**
* Validates a HTMLPurifier_ConfigSchema_Interchange_Namespace object.
*/
public function validateNamespace($n) {
$this->context[] = "namespace '{$n->namespace}'";
$this->with($n, 'namespace')
->assertNotEmpty()
->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
$this->with($n, 'description')
->assertNotEmpty()
->assertIsString(); // handled by InterchangeBuilder
array_pop($this->context);
}
/**
* Validates a HTMLPurifier_ConfigSchema_Interchange_Id object.
*/
@ -75,12 +57,11 @@ class HTMLPurifier_ConfigSchema_Validator
// handled by InterchangeBuilder
$this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
}
if (!isset($this->interchange->namespaces[$id->namespace])) {
$this->error('namespace', 'does not exist'); // assumes that the namespace was validated already
}
$this->with($id, 'directive')
// keys are now unconstrained (we might want to narrow down to A-Za-z0-9.)
// we probably should check that it has at least one namespace
$this->with($id, 'key')
->assertNotEmpty()
->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
->assertIsString(); // implicit assertIsString handled by InterchangeBuilder
array_pop($this->context);
}

View File

@ -1,3 +0,0 @@
Attr
DESCRIPTION: Features regarding attribute validation.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
AutoFormat
DESCRIPTION: Configuration for activating auto-formatting functionality (also known as <code>Injector</code>s)
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
AutoFormatParam
DESCRIPTION: Configuration for customizing auto-formatting functionality
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
CSS
DESCRIPTION: Configuration regarding allowed CSS.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
Cache
DESCRIPTION: Configuration for DefinitionCache and related subclasses.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
Core
DESCRIPTION: Core features that are always available.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
Filter
DESCRIPTION: Directives for turning filters on and off, or specifying custom filters.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
FilterParam
DESCRIPTION: Configuration for filters.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
HTML
DESCRIPTION: Configuration regarding allowed HTML.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
Output
DESCRIPTION: Configuration relating to the generation of (X)HTML.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
Test
DESCRIPTION: Developer testing configuration for our unit tests.
--# vim: et sw=4 sts=4

View File

@ -1,3 +0,0 @@
URI
DESCRIPTION: Features regarding Uniform Resource Identifiers.
--# vim: et sw=4 sts=4

View File

@ -159,7 +159,7 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
$ret .= $this->end('th');
$ret .= $this->start('td');
$def = $this->config->def->info[$ns][$directive];
$def = $this->config->def->info["$ns.$directive"];
if (is_int($def)) {
$allow_null = $def < 0;
$type = abs($def);
@ -248,7 +248,7 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer {
$this->prepareGenerator($gen_config);
// this should probably be split up a little
$ret = '';
$def = $config->def->info[$ns][$directive];
$def = $config->def->info["$ns.$directive"];
if (is_int($def)) {
$type = abs($def);
} else {

View File

@ -9,16 +9,9 @@ class HTMLPurifier_ConfigSchema_InterchangeTest extends UnitTestCase
$this->interchange = new HTMLPurifier_ConfigSchema_Interchange();
}
function testAddNamespace() {
$v = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
$v->namespace = 'Namespace';
$this->interchange->addNamespace($v);
$this->assertIdentical($v, $this->interchange->namespaces['Namespace']);
}
function testAddDirective() {
$v = new HTMLPurifier_ConfigSchema_Interchange_Directive();
$v->id = new HTMLPurifier_ConfigSchema_Interchange_Id('Namespace', 'Directive');
$v->id = new HTMLPurifier_ConfigSchema_Interchange_Id('Namespace.Directive');
$this->interchange->addDirective($v);
$this->assertIdentical($v, $this->interchange->directives['Namespace.Directive']);
}

View File

@ -1,8 +1,5 @@
ERROR: Alias 'Ns.BothWantThisName' in aliases in directive 'Ns.Dir2' collides with alias for directive 'Ns.Dir'
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: int

View File

@ -1,8 +1,5 @@
ERROR: Alias 'Ns.Innocent' in aliases in directive 'Ns.Dir' collides with another directive
----
Ns
DESCRIPTION: Namespace
----
Ns.Innocent
DESCRIPTION: Innocent directive
TYPE: int

View File

@ -1,10 +0,0 @@
ERROR: Directive in id 'Ns.R&D' in aliases in directive 'Ns.Dir' must be alphanumeric
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string
DEFAULT: 'a'
ALIASES: Ns.R&D

View File

@ -1,8 +1,5 @@
ERROR: Value 3 in allowed in directive 'Ns.Dir' must be a string
----
Ns
DESCRIPTION: Generic namespace.
----
ID: Ns.Dir
TYPE: string
DESCRIPTION: Description

View File

@ -1,8 +1,5 @@
ERROR: Allowed in directive 'Ns.Dir' must not be empty
----
Ns
DESCRIPTION: Generic namespace.
----
ID: Ns.Dir
TYPE: string
DESCRIPTION: Description

View File

@ -1,8 +1,5 @@
ERROR: Default in directive 'Ns.Dir' must be an allowed value
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string

View File

@ -1,6 +1,3 @@
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string/null

View File

@ -1,8 +1,5 @@
ERROR: Expected type string, got integer in DEFAULT in directive hash 'Ns.Dir'
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string

View File

@ -1,8 +1,5 @@
ERROR: Description in directive 'Ns.Dir' must not be empty
----
Ns
DESCRIPTION: Our namespace.
----
Ns.Dir
TYPE: int
DEFAULT: 0

View File

@ -1,9 +0,0 @@
ERROR: Directive in id 'Ns.+' in directive 'Ns.+' must be alphanumeric
----
Ns
DESCRIPTION: Generic namespace.
----
ID: Ns.+
TYPE: int
DESCRIPTION: Description
DEFAULT: 0

View File

@ -1,9 +0,0 @@
ERROR: Directive in id 'Ns.' in directive 'Ns.' must not be empty
----
Ns
DESCRIPTION: Our namespace
----
ID: Ns.
TYPE: int
DESCRIPTION: Description.
DEFAULT: 0

View File

@ -1,6 +0,0 @@
ERROR: Namespace in id 'Rd.Dir' in directive 'Rd.Dir' does not exist
----
ID: Rd.Dir
TYPE: int
DESCRIPTION: Description
DEFAULT: 0

View File

@ -0,0 +1,3 @@
Ns
DESCRIPTION: Namespace

View File

@ -1,8 +1,5 @@
ERROR: TYPE in directive hash 'Ns.Dir' not defined
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Notice that TYPE is missing
DEFAULT: 0

View File

@ -1,8 +1,5 @@
ERROR: Invalid type 'foobar' in DEFAULT in directive hash 'Ns.Dir'
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: foobar

View File

@ -1,8 +1,5 @@
ERROR: Type in directive 'Ns.Dir' must be a string type when used with allowed or value aliases
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: int

View File

@ -1,8 +1,5 @@
ERROR: Type in directive 'Ns.Dir' must be a string type when used with allowed or value aliases
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: int

View File

@ -1,8 +1,5 @@
ERROR: Alias 3 in valueAliases in directive 'Ns.Dir' must be a string
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string

View File

@ -1,8 +1,5 @@
ERROR: Alias 'b' in valueAliases in directive 'Ns.Dir' must not be an allowed value
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string

View File

@ -1,8 +1,5 @@
ERROR: Alias 'bar' in valueAliases in directive 'Ns.Dir' must not be an alias to itself
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string

View File

@ -1,8 +1,5 @@
ERROR: Alias 'c' in valueAliases in directive 'Ns.Dir' must be an alias to an allowed value
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string

View File

@ -1,8 +1,5 @@
ERROR: Alias target 3 from alias 'b' in valueAliases in directive 'Ns.Dir' must be a string
----
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string

View File

@ -1,3 +0,0 @@
ERROR: Description in namespace 'Ns' must not be empty
----
ID: Ns

View File

@ -1,4 +0,0 @@
ERROR: Namespace in namespace 'R&D' must be alphanumeric
----
ID: R&D
DESCRIPTION: ctype_alnum($ID) === false

View File

@ -1,4 +0,0 @@
ERROR: Namespace in namespace '0' must not be empty
----
ID: 0
DESCRIPTION: empty($ID) === true

View File

@ -1,2 +0,0 @@
Namespace
DESCRIPTION: This is a generic namespace.

View File

@ -1,7 +0,0 @@
ERROR: Cannot redefine namespace 'Ns'
----
ID: Ns
DESCRIPTION: Version 1
----
ID: Ns
DESCRIPTION: Version 2

View File

@ -13,36 +13,15 @@ class HTMLPurifier_ConfigSchema_ValidatorTest extends UnitTestCase
$this->interchange = new HTMLPurifier_ConfigSchema_Interchange();
}
function testNamespaceIntegrityViolation() {
$ns = $this->makeNamespace('Ns');
$ns->namespace = 'AltNs';
$this->expectValidationException("Integrity violation: key 'Ns' does not match internal id 'AltNs'");
$this->validator->validate($this->interchange);
}
function testNamespaceNamespaceIsString() {
$this->makeNamespace(3);
$this->expectValidationException("Namespace in namespace '3' must be a string");
$this->validator->validate($this->interchange);
}
function testNamespaceDescriptionIsString() {
$ns = $this->makeNamespace('Ns');
$ns->description = 3;
$this->expectValidationException("Description in namespace 'Ns' must be a string");
$this->validator->validate($this->interchange);
}
function testDirectiveIntegrityViolation() {
$d = $this->makeDirective('Ns', 'Dir');
$d->id = new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir2');
$d = $this->makeDirective('Ns.Dir');
$d->id = new HTMLPurifier_ConfigSchema_Interchange_Id('Ns.Dir2');
$this->expectValidationException("Integrity violation: key 'Ns.Dir' does not match internal id 'Ns.Dir2'");
$this->validator->validate($this->interchange);
}
function testDirectiveTypeNotEmpty() {
$this->makeNamespace('Ns');
$d = $this->makeDirective('Ns', 'Dir');
$d = $this->makeDirective('Ns.Dir');
$d->default = 0;
$d->description = 'Description';
@ -51,8 +30,7 @@ class HTMLPurifier_ConfigSchema_ValidatorTest extends UnitTestCase
}
function testDirectiveDefaultInvalid() {
$this->makeNamespace('Ns');
$d = $this->makeDirective('Ns', 'Dir');
$d = $this->makeDirective('Ns.Dir');
$d->default = 'asdf';
$d->type = 'int';
$d->description = 'Description';
@ -61,20 +39,18 @@ class HTMLPurifier_ConfigSchema_ValidatorTest extends UnitTestCase
$this->validator->validate($this->interchange);
}
function testDirectiveIdDirectiveIsString() {
$this->makeNamespace('Ns');
$d = $this->makeDirective('Ns', 3);
function testDirectiveIdIsString() {
$d = $this->makeDirective(3);
$d->default = 0;
$d->type = 'int';
$d->description = 'Description';
$this->expectValidationException("Directive in id 'Ns.3' in directive 'Ns.3' must be a string");
$this->expectValidationException("Key in id '3' in directive '3' must be a string");
$this->validator->validate($this->interchange);
}
function testDirectiveTypeAllowsNullIsBool() {
$this->makeNamespace('Ns');
$d = $this->makeDirective('Ns', 'Dir');
$d = $this->makeDirective('Ns.Dir');
$d->default = 0;
$d->type = 'int';
$d->description = 'Description';
@ -85,8 +61,7 @@ class HTMLPurifier_ConfigSchema_ValidatorTest extends UnitTestCase
}
function testDirectiveValueAliasesIsArray() {
$this->makeNamespace('Ns');
$d = $this->makeDirective('Ns', 'Dir');
$d = $this->makeDirective('Ns.Dir');
$d->default = 'a';
$d->type = 'string';
$d->description = 'Description';
@ -97,8 +72,7 @@ class HTMLPurifier_ConfigSchema_ValidatorTest extends UnitTestCase
}
function testDirectiveAllowedIsLookup() {
$this->makeNamespace('Ns');
$d = $this->makeDirective('Ns', 'Dir');
$d = $this->makeDirective('Ns.Dir');
$d->default = 'foo';
$d->type = 'string';
$d->description = 'Description';
@ -110,17 +84,10 @@ class HTMLPurifier_ConfigSchema_ValidatorTest extends UnitTestCase
// helper functions
protected function makeNamespace($n) {
$namespace = new HTMLPurifier_ConfigSchema_Interchange_Namespace();
$namespace->namespace = $n;
$namespace->description = 'Description'; // non-essential, but we won't set it most of the time
$this->interchange->addNamespace($namespace);
return $namespace;
}
protected function makeDirective($n, $d) {
protected function makeDirective($key) {
$directive = new HTMLPurifier_ConfigSchema_Interchange_Directive();
$directive->id = new HTMLPurifier_ConfigSchema_Interchange_Id($n, $d);
$directive->id = new HTMLPurifier_ConfigSchema_Interchange_Id($key);
$this->interchange->addDirective($directive);
return $directive;
}

View File

@ -9,41 +9,32 @@ class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
$this->schema = new HTMLPurifier_ConfigSchema();
}
function test_defineNamespace() {
$this->schema->addNamespace('http');
$this->assertIdentical($this->schema->info['http'], array());
$this->assertIdentical($this->schema->defaults['http'], array());
}
function test_define() {
$this->schema->addNamespace('Car');
$this->schema->add('Car.Seats', 5, 'int', false);
$this->schema->add('Car', 'Seats', 5, 'int', false);
$this->assertIdentical($this->schema->defaults['Car.Seats'], 5);
$this->assertIdentical($this->schema->info['Car.Seats']->type, HTMLPurifier_VarParser::INT);
$this->assertIdentical($this->schema->defaults['Car']['Seats'], 5);
$this->assertIdentical($this->schema->info['Car']['Seats']->type, HTMLPurifier_VarParser::INT);
$this->schema->add('Car.Age', null, 'int', true);
$this->schema->add('Car', 'Age', null, 'int', true);
$this->assertIdentical($this->schema->defaults['Car']['Age'], null);
$this->assertIdentical($this->schema->info['Car']['Age']->type, HTMLPurifier_VarParser::INT);
$this->assertIdentical($this->schema->defaults['Car.Age'], null);
$this->assertIdentical($this->schema->info['Car.Age']->type, HTMLPurifier_VarParser::INT);
}
function test_defineAllowedValues() {
$this->schema->addNamespace('QuantumNumber', 'D');
$this->schema->add('QuantumNumber', 'Spin', 0.5, 'float', false);
$this->schema->add('QuantumNumber', 'Current', 's', 'string', false);
$this->schema->add('QuantumNumber', 'Difficulty', null, 'string', true);
$this->schema->add('QuantumNumber.Spin', 0.5, 'float', false);
$this->schema->add('QuantumNumber.Current', 's', 'string', false);
$this->schema->add('QuantumNumber.Difficulty', null, 'string', true);
$this->schema->addAllowedValues( // okay, since default is null
'QuantumNumber', 'Difficulty', array('easy' => true, 'medium' => true, 'hard' => true)
'QuantumNumber.Difficulty', array('easy' => true, 'medium' => true, 'hard' => true)
);
$this->assertIdentical($this->schema->defaults['QuantumNumber']['Difficulty'], null);
$this->assertIdentical($this->schema->info['QuantumNumber']['Difficulty']->type, HTMLPurifier_VarParser::STRING);
$this->assertIdentical($this->schema->info['QuantumNumber']['Difficulty']->allow_null, true);
$this->assertIdentical($this->schema->info['QuantumNumber']['Difficulty']->allowed,
$this->assertIdentical($this->schema->defaults['QuantumNumber.Difficulty'], null);
$this->assertIdentical($this->schema->info['QuantumNumber.Difficulty']->type, HTMLPurifier_VarParser::STRING);
$this->assertIdentical($this->schema->info['QuantumNumber.Difficulty']->allow_null, true);
$this->assertIdentical($this->schema->info['QuantumNumber.Difficulty']->allowed,
array(
'easy' => true,
'medium' => true,
@ -54,37 +45,36 @@ class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
}
function test_defineValueAliases() {
$this->schema->addNamespace('Abbrev', 'Stuff on abbreviations.');
$this->schema->add('Abbrev', 'HTH', 'Happy to Help', 'string', false);
$this->schema->add('Abbrev.HTH', 'Happy to Help', 'string', false);
$this->schema->addAllowedValues(
'Abbrev', 'HTH', array(
'Abbrev.HTH', array(
'Happy to Help' => true,
'Hope that Helps' => true,
'HAIL THE HAND!' => true,
)
);
$this->schema->addValueAliases(
'Abbrev', 'HTH', array(
'Abbrev.HTH', array(
'happy' => 'Happy to Help',
'hope' => 'Hope that Helps'
)
);
$this->schema->addValueAliases( // delayed addition
'Abbrev', 'HTH', array(
'Abbrev.HTH', array(
'hail' => 'HAIL THE HAND!'
)
);
$this->assertIdentical($this->schema->defaults['Abbrev']['HTH'], 'Happy to Help');
$this->assertIdentical($this->schema->info['Abbrev']['HTH']->type, HTMLPurifier_VarParser::STRING);
$this->assertIdentical($this->schema->info['Abbrev']['HTH']->allowed,
$this->assertIdentical($this->schema->defaults['Abbrev.HTH'], 'Happy to Help');
$this->assertIdentical($this->schema->info['Abbrev.HTH']->type, HTMLPurifier_VarParser::STRING);
$this->assertIdentical($this->schema->info['Abbrev.HTH']->allowed,
array(
'Happy to Help' => true,
'Hope that Helps' => true,
'HAIL THE HAND!' => true
)
);
$this->assertIdentical($this->schema->info['Abbrev']['HTH']->aliases,
$this->assertIdentical($this->schema->info['Abbrev.HTH']->aliases,
array(
'happy' => 'Happy to Help',
'hope' => 'Hope that Helps',
@ -95,14 +85,12 @@ class HTMLPurifier_ConfigSchemaTest extends HTMLPurifier_Harness
}
function testAlias() {
$this->schema->addNamespace('Home');
$this->schema->add('Home', 'Rug', 3, 'int', false);
$this->schema->addAlias('Home', 'Carpet', 'Home', 'Rug');
$this->schema->add('Home.Rug', 3, 'int', false);
$this->schema->addAlias('Home.Carpet', 'Home.Rug');
$this->assertTrue(!isset($this->schema->defaults['Home']['Carpet']));
$this->assertIdentical($this->schema->info['Home']['Carpet']->namespace, 'Home');
$this->assertIdentical($this->schema->info['Home']['Carpet']->name, 'Rug');
$this->assertIdentical($this->schema->info['Home']['Carpet']->isAlias, true);
$this->assertTrue(!isset($this->schema->defaults['Home.Carpet']));
$this->assertIdentical($this->schema->info['Home.Carpet']->key, 'Home.Rug');
$this->assertIdentical($this->schema->info['Home.Carpet']->isAlias, true);
}

View File

@ -13,17 +13,15 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
// test functionality based on ConfigSchema
function testNormal() {
$this->schema->addNamespace('Element');
$this->schema->add('Element', 'Abbr', 'H', 'string', false);
$this->schema->add('Element', 'Name', 'hydrogen', 'istring', false);
$this->schema->add('Element', 'Number', 1, 'int', false);
$this->schema->add('Element', 'Mass', 1.00794, 'float', false);
$this->schema->add('Element', 'Radioactive', false, 'bool', false);
$this->schema->add('Element', 'Isotopes', array(1 => true, 2 => true, 3 => true), 'lookup', false);
$this->schema->add('Element', 'Traits', array('nonmetallic', 'odorless', 'flammable'), 'list', false);
$this->schema->add('Element', 'IsotopeNames', array(1 => 'protium', 2 => 'deuterium', 3 => 'tritium'), 'hash', false);
$this->schema->add('Element', 'Object', new stdClass(), 'mixed', false);
$this->schema->add('Element.Abbr', 'H', 'string', false);
$this->schema->add('Element.Name', 'hydrogen', 'istring', false);
$this->schema->add('Element.Number', 1, 'int', false);
$this->schema->add('Element.Mass', 1.00794, 'float', false);
$this->schema->add('Element.Radioactive', false, 'bool', false);
$this->schema->add('Element.Isotopes', array(1 => true, 2 => true, 3 => true), 'lookup', false);
$this->schema->add('Element.Traits', array('nonmetallic', 'odorless', 'flammable'), 'list', false);
$this->schema->add('Element.IsotopeNames', array(1 => 'protium', 2 => 'deuterium', 3 => 'tritium'), 'hash', false);
$this->schema->add('Element.Object', new stdClass(), 'mixed', false);
$config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false;
@ -74,22 +72,20 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function testEnumerated() {
$this->schema->addNamespace('Instrument', 'Of the musical type.');
// case sensitive
$this->schema->add('Instrument', 'Manufacturer', 'Yamaha', 'string', false);
$this->schema->addAllowedValues('Instrument', 'Manufacturer', array(
$this->schema->add('Instrument.Manufacturer', 'Yamaha', 'string', false);
$this->schema->addAllowedValues('Instrument.Manufacturer', array(
'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'));
// case insensitive
$this->schema->add('Instrument', 'Family', 'woodwind', 'istring', false);
$this->schema->addAllowedValues('Instrument', 'Family', array(
$this->schema->add('Instrument.Family', 'woodwind', 'istring', false);
$this->schema->addAllowedValues('Instrument.Family', array(
'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'));
$config = new HTMLPurifier_Config($this->schema);
@ -124,9 +120,8 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function testNull() {
$this->schema->addNamespace('ReportCard');
$this->schema->add('ReportCard', 'English', null, 'string', true);
$this->schema->add('ReportCard', 'Absences', 0, 'int', false);
$this->schema->add('ReportCard.English', null, 'string', true);
$this->schema->add('ReportCard.Absences', 0, 'int', false);
$config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false;
@ -145,9 +140,8 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function testAliases() {
$this->schema->addNamespace('Home');
$this->schema->add('Home', 'Rug', 3, 'int', false);
$this->schema->addAlias('Home', 'Carpet', 'Home', 'Rug');
$this->schema->add('Home.Rug', 3, 'int', false);
$this->schema->addAlias('Home.Carpet', 'Home.Rug');
$config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false;
@ -167,9 +161,8 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_getBatch() {
$this->schema->addNamespace('Variables');
$this->schema->add('Variables', 'TangentialAcceleration', 'a_tan', 'string', false);
$this->schema->add('Variables', 'AngularAcceleration', 'alpha', 'string', false);
$this->schema->add('Variables.TangentialAcceleration', 'a_tan', 'string', false);
$this->schema->add('Variables.AngularAcceleration', 'alpha', 'string', false);
$config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false;
@ -191,10 +184,9 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_loadIni() {
$this->schema->addNamespace('Shortcut', 'Keyboard shortcuts for commands');
$this->schema->add('Shortcut', 'Copy', 'c', 'istring', false);
$this->schema->add('Shortcut', 'Paste', 'v', 'istring', false);
$this->schema->add('Shortcut', 'Cut', 'x', 'istring', false);
$this->schema->add('Shortcut.Copy', 'c', 'istring', false);
$this->schema->add('Shortcut.Paste', 'v', 'istring', false);
$this->schema->add('Shortcut.Cut', 'x', 'istring', false);
$config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false;
@ -259,9 +251,7 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
}
function test_getDefinition() {
$this->schema->addNamespace('Cache', 'Cache stuff');
$this->schema->add('Cache', 'DefinitionImpl', null, 'string', true);
$this->schema->addNamespace('Crust', 'Krusty Krabs');
$this->schema->add('Cache.DefinitionImpl', null, 'string', true);
$config = new HTMLPurifier_Config($this->schema);
$this->expectException(new HTMLPurifier_Exception("Definition of Crust type not supported"));
$config->getDefinition('Crust');
@ -269,11 +259,10 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_loadArray() {
// setup a few dummy namespaces/directives for our testing
$this->schema->addNamespace('Zoo');
$this->schema->add('Zoo', 'Aadvark', 0, 'int', false);
$this->schema->add('Zoo', 'Boar', 0, 'int', false);
$this->schema->add('Zoo', 'Camel', 0, 'int', false);
$this->schema->add('Zoo', 'Others', array(), 'list', false);
$this->schema->add('Zoo.Aadvark', 0, 'int', false);
$this->schema->add('Zoo.Boar', 0, 'int', false);
$this->schema->add('Zoo.Camel', 0, 'int', false);
$this->schema->add('Zoo.Others', array(), 'list', false);
$config_manual = new HTMLPurifier_Config($this->schema);
$config_loadabbr = new HTMLPurifier_Config($this->schema);
@ -309,9 +298,8 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_create() {
$this->schema->addNamespace('Cake');
$this->schema->add('Cake', 'Sprinkles', 666, 'int', false);
$this->schema->add('Cake', 'Flavor', 'vanilla', 'string', false);
$this->schema->add('Cake.Sprinkles', 666, 'int', false);
$this->schema->add('Cake.Flavor', 'vanilla', 'string', false);
$config = new HTMLPurifier_Config($this->schema);
$config->set('Cake', 'Sprinkles', 42);
@ -334,8 +322,7 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
// test finalization
$this->schema->addNamespace('Poem');
$this->schema->add('Poem', 'Meter', 'iambic', 'string', false);
$this->schema->add('Poem.Meter', 'iambic', 'string', false);
$config = new HTMLPurifier_Config($this->schema);
$config->autoFinalize = false;
@ -357,17 +344,15 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
function test_loadArrayFromForm() {
$this->schema->addNamespace('Pancake');
$this->schema->add('Pancake', 'Mix', 'buttermilk', 'string', false);
$this->schema->add('Pancake', 'Served', true, 'bool', false);
$this->schema->addNamespace('Toppings', false);
$this->schema->add('Toppings', 'Syrup', true, 'bool', false);
$this->schema->add('Toppings', 'Flavor', 'maple', 'string', false);
$this->schema->add('Toppings', 'Strawberries', 3, 'int', false);
$this->schema->add('Toppings', 'Calories', 2000, 'int', true);
$this->schema->add('Toppings', 'DefinitionID', null, 'string', true);
$this->schema->add('Toppings', 'DefinitionRev', 1, 'int', false);
$this->schema->add('Toppings', 'Protected', 1, 'int', false);
$this->schema->add('Pancake.Mix', 'buttermilk', 'string', false);
$this->schema->add('Pancake.Served', true, 'bool', false);
$this->schema->add('Toppings.Syrup', true, 'bool', false);
$this->schema->add('Toppings.Flavor', 'maple', 'string', false);
$this->schema->add('Toppings.Strawberries', 3, 'int', false);
$this->schema->add('Toppings.Calories', 2000, 'int', true);
$this->schema->add('Toppings.DefinitionID', null, 'string', true);
$this->schema->add('Toppings.DefinitionRev', 1, 'int', false);
$this->schema->add('Toppings.Protected', 1, 'int', false);
$get = array(
'breakfast' => array(
@ -418,16 +403,13 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
}
function test_getAllowedDirectivesForForm() {
$this->schema->addNamespace('Unused');
$this->schema->add('Unused', 'Unused', 'Foobar', 'string', false);
$this->schema->addNamespace('Partial');
$this->schema->add('Partial', 'Allowed', true, 'bool', false);
$this->schema->add('Partial', 'Unused', 'Foobar', 'string', false);
$this->schema->addNamespace('All');
$this->schema->add('All', 'Allowed', true, 'bool', false);
$this->schema->add('All', 'Blacklisted', 'Foobar', 'string', false); // explicitly blacklisted
$this->schema->add('All', 'DefinitionID', 'Foobar', 'string', true); // auto-blacklisted
$this->schema->add('All', 'DefinitionRev', 2, 'int', false); // auto-blacklisted
$this->schema->add('Unused.Unused', 'Foobar', 'string', false);
$this->schema->add('Partial.Allowed', true, 'bool', false);
$this->schema->add('Partial.Unused', 'Foobar', 'string', false);
$this->schema->add('All.Allowed', true, 'bool', false);
$this->schema->add('All.Blacklisted', 'Foobar', 'string', false); // explicitly blacklisted
$this->schema->add('All.DefinitionID', 'Foobar', 'string', true); // auto-blacklisted
$this->schema->add('All.DefinitionRev', 2, 'int', false); // auto-blacklisted
$input = array('Partial.Allowed', 'All', '-All.Blacklisted');
$output = HTMLPurifier_Config::getAllowedDirectivesForForm($input, $this->schema);