0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2024-12-22 08:21:52 +00:00

[3.1.0] Document Config Schema, also, fix bug with null defaults

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1651 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang 2008-04-05 18:37:08 +00:00
parent 9f1e678b48
commit 27ba8f2192
11 changed files with 395 additions and 7 deletions

2
TODO
View File

@ -19,8 +19,6 @@ IMPORTANT
- Release candidate, because of the major changes
DOCUMENTATION
- Document new ConfigSchema setup and format; dev-includes.txt is a base
but we need it in HTML
- Update French translation of README
IMPORTANT FEATURES

View File

@ -3,7 +3,7 @@
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Functional specification for HTML Purifier's advanced API for defining custom filtering behavior." />
<meta name="description" content="Specification for HTML Purifier's advanced API for defining custom filtering behavior." />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>Advanced API - HTML Purifier</title>

368
docs/dev-config-schema.html Normal file
View File

@ -0,0 +1,368 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Describes config schema framework in HTML Purifier." />
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Config Schema - HTML Purifier</title>
</head>
<body>
<h1>Config Schema</h1>
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://htmlpurifier.org/">HTML Purifier</a> End-User Documentation</div>
<p>
HTML Purifier has a fairly complex system for configuration. Users
interact with a <code>HTMLPurifier_Config</code> object to
set configuration directives. The values they set are validated according
to a configuration schema, <code>HTMLPurifier_ConfigSchema</code>.
</p>
<p>
The schema is mostly transparent to end-users, but if you're doing development
work for HTML Purifier and need to define a new configuration directive,
you'll need to interact with it. We'll also talk about how to define
userspace configuration directives at the very end.
</p>
<h2>Write a directive file</h2>
<p>
Directive files define configuration directives to be used by
HTML Purifier. They are placed in <code>library/HTMLPurifier/ConfigSchema/schema/</code>
in the form <code><em>Namespace</em>.<em>Directive</em>.txt</code> (I
couldn't think of a more descriptive file extension.)
Directive files are actually what we call <code>StringHash</code>es,
i.e. associative arrays represented in a string form reminiscent of
<a href="http://qa.php.net/write-test.php">PHPT</a> tests. Here's a
sample directive file, <code>Test.Sample.txt</code>:
</p>
<pre>Test.Sample
TYPE: string/null
DEFAULT: NULL
ALLOWED: 'foo', 'bar'
VALUE-ALIASES: 'baz' => 'bar'
VERSION: 3.1.0
--DESCRIPTION--
This is a sample configuration directive for the purposes of the
&lt;code&gt;dev-config-schema.html&lt;code&gt; documentation.
--ALIASES--
Test.Example</pre>
<p>
Each of these segments has a specific meaning:
</p>
<table class="table">
<thead>
<tr>
<th>Key</th>
<th>Example</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>ID</td>
<td>Test.Sample</td>
<td>The name of the directive, in the form Namespace.Directive
(implicitly the first line)</td>
</tr>
<tr>
<td>TYPE</td>
<td>string/null</td>
<td>The type of variable this directive accepts. See below for
details. You can also add <code>/null</code> to the end of
any basic type to allow null values too.</td>
</tr>
<tr>
<td>DEFAULT</td>
<td>NULL</td>
<td>A parseable PHP expression of the default value.</td>
</tr>
<tr>
<td>DESCRIPTION</td>
<td>This is a...</td>
<td>An HTML description of what this directive does.</td>
</tr>
<tr>
<td>VERSION</td>
<td>3.1.0</td>
<td><em>Recommended</em>. The version of HTML Purifier this directive was added.
Directives that have been around since 1.0.0 don't have this,
but any new ones should.</td>
</tr>
<tr>
<td>ALIASES</td>
<td>Test.Example</td>
<td><em>Optional</em>. A comma separated list of aliases for this directive.
This is most useful for backwards compatibility and should
not be used otherwise.</td>
</tr>
<tr>
<td>ALLOWED</td>
<td>'foo', 'bar'</td>
<td><em>Optional</em>. Set of allowed value for a directive,
a comma separated list of parseable PHP expressions. This
is only allowed string, istring, text and itext TYPEs.</td>
</tr>
<tr>
<td>VALUE-ALIASES</td>
<td>'baz' => 'bar'</td>
<td><em>Optional</em>. Mapping of one value to another, and
should be a comma separated list of keypair duples. This
is only allowed string, istring, text and itext TYPEs.</td>
</tr>
<tr>
<td>DEPRECATED-VERSION</td>
<td>3.1.0</td>
<td><em>Not shown</em>. Indicates that the directive was
deprecated this version.</td>
</tr>
<tr>
<td>DEPRECATED-USE</td>
<td>Test.NewDirective</td>
<td><em>Not shown</em>. Indicates what new directive should be
used instead. Note that the directives will functionally be
different, although they should offer the same functionality.
If they are identical, use an alias instead.</td>
</tr>
</tbody>
</table>
<p>
Some notes on format and style:
</p>
<ul>
<li>
Each of these keys can be expressed in the short format
(<code>KEY: Value</code>) or the long format
(<code>--KEY--</code> with value beneath). You must use the
long format if multiple lines are needed, or if a long format
has been used already (that's why <code>ALIASES</code> in our
example is in the long format); otherwise, it's user preference.
</li>
<li>
The HTML descriptions should be wrapped at about 80 columns; do
not rely on editor word-wrapping.
</li>
</ul>
<p>
Also, as promised, here is the set of possible types:
</p>
<table class="table">
<thead>
<tr>
<th>Type</th>
<th>Example</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>string</td>
<td>'Foo'</td>
<td><a href="http://docs.php.net/manual/en/language.types.string.php">String</a> without newlines</td>
</tr>
<tr>
<td>istring</td>
<td>'foo'</td>
<td>Case insensitive ASCII string without newlines</td>
</tr>
<tr>
<td>text</td>
<td>"A<em>\n</em>b"</td>
<td>String with newlines</td>
</tr>
<tr>
<td>itext</td>
<td>"a<em>\n</em>b"</td>
<td>Case insensitive ASCII string without newlines</td>
</tr>
<tr>
<td>int</td>
<td>23</td>
<td>Integer</td>
</tr>
<tr>
<td>float</td>
<td>3.0</td>
<td>Floating point number</td>
</tr>
<tr>
<td>bool</td>
<td>true</td>
<td>Boolean</td>
</tr>
<tr>
<td>lookup</td>
<td>array('key' => true)</td>
<td>Lookup array, used with <code>isset($var[$key])</code></td>
</tr>
<tr>
<td>list</td>
<td>array('f', 'b')</td>
<td>List array, with ordered numerical indexes</td>
</tr>
<tr>
<td>hash</td>
<td>array('key' => 'val')</td>
<td>Associative array of keys to values</td>
</tr>
<tr>
<td>mixed</td>
<td>new stdclass</td>
<td>Any PHP variable is fine</td>
</tr>
</tbody>
</table>
<p>
The examples represent what will be returned out of the configuration
object; users have a little bit of leeway when setting configuration
values (for example, a lookup value can be specified as a list;
HTML Purifier will flip it as necessary.) These types are defined
in <a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/VarParser.php">
library/HTMLPurifier/VarParser.php</a>.
</p>
<p>
For more information on what values are allowed, and how they are parsed,
consult <a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php">
library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php</a>, as well
as <a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/ConfigSchema/Interchange/Directive.php">
library/HTMLPurifier/ConfigSchema/Interchange/Directive.php</a> for
the semantics of the parsed values.
</p>
<h2>Refreshing the cache</h2>
<p>
You may have noticed that your directive file isn't doing anything
yet. That's because it hasn't been added to the runtime
<code>HTMLPurifier_ConfigSchema</code> instance. Run
<code>maintenance/generate-schema-cache.php</code> to fix this.
If there were no errors, you're good to go! Don't forget to add
some unit tests for your functionality!
</p>
<p>
If you ever make changes to your configuration directives, you
will need to run this script again.
</p>
<h2>Errors</h2>
<p>
All directive files go through a rigorous validation process
through <a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/ConfigSchema/">
library/HTMLPurifier/ConfigSchema/Validator.php</a>, as well
as some basic checks during building. While
listing every error out here is out-of-scope for this document, we
can give some general tips for interpreting error messages.
There are two types of errors: builder errors and validation errors.
</p>
<h3>Builder errors</h3>
<blockquote>
<p>
<strong>Exception:</strong> Expected type string, got
integer in DEFAULT in directive hash 'Ns.Dir'
</p>
</blockquote>
<p>
You can identify a builder error by the keyword "directive hash."
These are the easiest to deal with, because they directly correspond
with your directive file. Find the offending directive file (which
is the directive hash plus the .txt extension), find the
offending index ("in DEFAULT" means the DEFAULT key) and fix the error.
This particular error would occur if your default value is not the same
type as TYPE.
</p>
<h3>Validation errors</h3>
<blockquote>
<p>
<strong>Exception:</strong> Alias 3 in valueAliases in directive
'Ns.Dir' must be a string
</p>
</blockquote>
<p>
These are a little trickier, because we're not actually validating
your directive file, or even the direct string hash representation.
We're validating an Interchange object, and the error messages do
not mention any string hash keys.
</p>
<p>
Nevertheless, it's not difficult to figure out what went wrong.
Read the "context" statements in reverse:
</p>
<dl>
<dt>in directive 'Ns.Dir'</dt>
<dd>This means we need to look at the directive file <code>Ns.Dir.txt</code></dd>
<dt>in valueAliases</dt>
<dd>There's no key actually called this, but there's one that's close:
VALUE-ALIASES. Indeed, that's where to look.</dd>
<dt>Alias 3</dt>
<dd>The value alias that is equal to 3 is the culprit.</dd>
</dl>
<p>
In this particular case, you're not allowed to alias integers values to
strings values.
</p>
<p>
The most difficult part is translating the Interchange member variable (valueAliases)
into a directive file key (VALUE-ALIASES), but there's a one-to-one
correspondence currently. If the two formats diverge, any discrepancies
will be described in <a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php">
library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php</a>.
</p>
<h2>Internals</h2>
<p>
Much of the configuration schema framework's codebase deals with
shuffling data from one format to another, and doing validation on this
data.
The keystone of all of this is the <code>HTMLPurifier_ConfigSchema_Interchange</code>
class, which represents the purest, parsed representation of the schema.
</p>
<p>
Hand-writing this data is unwieldy, however, so we write directive files.
These directive files are parsed by <code>HTMLPurifier_StringHashParser</code>
into <code>HTMLPurifier_StringHash</code>es, which then
are run through <code>HTMLPurifier_ConfigSchema_InterchangeBuilder</code>
to construct the interchange object.
</p>
<p>
From the interchange object, the data can be siphoned into other forms
using <code>HTMLPurifier_ConfigSchema_Builder</code> subclasses.
For example, <code>HTMLPurifier_ConfigSchema_Builder_ConfigSchema</code>
generates a runtime <code>HTMLPurifier_ConfigSchema</code> object,
which <code>HTMLPurifier_Config</code> uses to validate its incoming
data. There is also a planned documentation builder.
</p>
<div id="version">$Id$</div>
</body>
</html>

View File

@ -275,3 +275,5 @@ New stuff
VERSION: Version number directive was introduced
DEPRECATED-VERSION: If the directive was deprecated, when was it deprecated?
DEPRECATED-USE: If the directive was deprecated, what should the user use now?
REQUIRES: What classes does this configuration directive require, but are
not part of the HTML Purifier core?

View File

@ -64,9 +64,12 @@ conventions.</p>
<dd>Discusses when to flush HTML Purifier's various caches.</dd>
<dt><a href="dev-advanced-api.html">Advanced API</a></dt>
<dd>Functional specification for HTML Purifier's advanced API for defining
<dd>Specification for HTML Purifier's advanced API for defining
custom filtering behavior.</dd>
<dt><a href="dev-config-schema.html">Config Schema</a></dt>
<dd>Describes config schema framework in HTML Purifier.</dd>
</dl>
<h2>Proposals</h2>

View File

@ -1,6 +1,9 @@
The Modularization of HTMLDefinition in HTML Purifier
WARNING: This document was drafted before the implementation of this
system, and some implementation details may have evolved over time.
HTML Purifier uses the modularization of XHTML
<http://www.w3.org/TR/xhtml-modularization/> to organize the internals
of HTMLDefinition into a more manageable and extensible fashion. Rather

View File

@ -60,7 +60,7 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
try {
$directive->default = $this->varParser->parse($hash->offsetGet('DEFAULT'), $directive->type, $directive->typeAllowsNull);
} catch (HTMLPurifier_VarParserException $e) {
throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in TYPE/DEFAULT in directive hash '$id'");
throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'");
}
}

View File

@ -131,7 +131,7 @@ class HTMLPurifier_ConfigSchema_Validator
$this->with($d, 'allowed')
->assertNotEmpty()
->assertIsLookup(); // handled by InterchangeBuilder
if (!isset($d->allowed[$d->default])) {
if (is_string($d->default) && !isset($d->allowed[$d->default])) {
$this->error('default', 'must be an allowed value');
}
$this->context[] = 'allowed';

View File

@ -19,11 +19,17 @@ $FS = new FSTools();
$exclude_dirs = array(
'HTMLPurifier/Language/',
'HTMLPurifier/ConfigSchema/',
'HTMLPurifier/Filter/',
'HTMLPurifier/Printer/',
/* These should be excluded, but need to have ConfigSchema support first
*/
);
$exclude_files = array(
'HTMLPurifier/Lexer/PEARSax3.php',
'HTMLPurifier/Lexer/PH5P.php',
'HTMLPurifier/Printer.php',
);
// Determine what files need to be included:

View File

@ -0,0 +1,8 @@
Ns
DESCRIPTION: Namespace
----
Ns.Dir
DESCRIPTION: Directive
TYPE: string/null
DEFAULT: null
ALLOWED: 'a'

View File

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