Fix E_NOTICE from indexing into empty string.
[htmlpurifier.git] / docs / dev-config-schema.html
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3   "">
4 <html xmlns="" xml:lang="en" lang="en">
5   <head>
6     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7     <meta name="description" content="Describes config schema framework in HTML Purifier." />
8     <link rel="stylesheet" type="text/css" href="./style.css" />
9     <title>Config Schema - HTML Purifier</title>
10   </head>
11   <body>
13     <h1>Config Schema</h1>
15     <div id="filing">Filed under Development</div>
16     <div id="index">Return to the <a href="index.html">index</a>.</div>
17     <div id="home"><a href="">HTML Purifier</a> End-User Documentation</div>
19     <p>
20       HTML Purifier has a fairly complex system for configuration. Users
21       interact with a <code>HTMLPurifier_Config</code> object to
22       set configuration directives. The values they set are validated according
23       to a configuration schema, <code>HTMLPurifier_ConfigSchema</code>.
24     </p>
26     <p>
27       The schema is mostly transparent to end-users, but if you're doing development
28       work for HTML Purifier and need to define a new configuration directive,
29       you'll need to interact with it. We'll also talk about how to define
30       userspace configuration directives at the very end.
31     </p>
33     <h2>Write a directive file</h2>
35     <p>
36       Directive files define configuration directives to be used by
37       HTML Purifier. They are placed in <code>library/HTMLPurifier/ConfigSchema/schema/</code>
38       in the form <code><em>Namespace</em>.<em>Directive</em>.txt</code> (I
39       couldn't think of a more descriptive file extension.)
40       Directive files are actually what we call <code>StringHash</code>es,
41       i.e. associative arrays represented in a string form reminiscent of
42       <a href="">PHPT</a> tests. Here's a
43       sample directive file, <code>Test.Sample.txt</code>:
44     </p>
46     <pre>Test.Sample
47 TYPE: string/null
49 ALLOWED: 'foo', 'bar'
50 VALUE-ALIASES: 'baz' => 'bar'
51 VERSION: 3.1.0
53 This is a sample configuration directive for the purposes of the
54 &lt;code&gt;dev-config-schema.html&lt;code&gt; documentation.
55 --ALIASES--
56 Test.Example</pre>
58     <p>
59       Each of these segments has a specific meaning:
60     </p>
62     <table class="table">
63       <thead>
64         <tr>
65           <th>Key</th>
66           <th>Example</th>
67           <th>Description</th>
68         </tr>
69       </thead>
70       <tbody>
71         <tr>
72           <td>ID</td>
73           <td>Test.Sample</td>
74           <td>The name of the directive, in the form Namespace.Directive
75           (implicitly the first line)</td>
76         </tr>
77         <tr>
78           <td>TYPE</td>
79           <td>string/null</td>
80           <td>The type of variable this directive accepts. See below for
81           details. You can also add <code>/null</code> to the end of
82           any basic type to allow null values too.</td>
83         </tr>
84         <tr>
85           <td>DEFAULT</td>
86           <td>NULL</td>
87           <td>A parseable PHP expression of the default value.</td>
88         </tr>
89         <tr>
90           <td>DESCRIPTION</td>
91           <td>This is a...</td>
92           <td>An HTML description of what this directive does.</td>
93         </tr>
94         <tr>
95           <td>VERSION</td>
96           <td>3.1.0</td>
97           <td><em>Recommended</em>. The version of HTML Purifier this directive was added.
98           Directives that have been around since 1.0.0 don't have this,
99           but any new ones should.</td>
100         </tr>
101         <tr>
102           <td>ALIASES</td>
103           <td>Test.Example</td>
104           <td><em>Optional</em>. A comma separated list of aliases for this directive.
105           This is most useful for backwards compatibility and should
106           not be used otherwise.</td>
107         </tr>
108         <tr>
109           <td>ALLOWED</td>
110           <td>'foo', 'bar'</td>
111           <td><em>Optional</em>. Set of allowed value for a directive,
112           a comma separated list of parseable PHP expressions. This
113           is only allowed string, istring, text and itext TYPEs.</td>
114         </tr>
115         <tr>
116           <td>VALUE-ALIASES</td>
117           <td>'baz' =&gt; 'bar'</td>
118           <td><em>Optional</em>. Mapping of one value to another, and
119           should be a comma separated list of keypair duples. This
120           is only allowed string, istring, text and itext TYPEs.</td>
121         </tr>
122         <tr>
123           <td>DEPRECATED-VERSION</td>
124           <td>3.1.0</td>
125           <td><em>Not shown</em>. Indicates that the directive was
126           deprecated this version.</td>
127         </tr>
128         <tr>
129           <td>DEPRECATED-USE</td>
130           <td>Test.NewDirective</td>
131           <td><em>Not shown</em>. Indicates what new directive should be
132           used instead. Note that the directives will functionally be
133           different, although they should offer the same functionality.
134           If they are identical, use an alias instead.</td>
135         </tr>
136         <tr>
137           <td>EXTERNAL</td>
138           <td>CSSTidy</td>
139           <td><em>Not shown</em>. Indicates if there is an external library
140           the user will need to download and install to use this configuration
141           directive. As of right now, this is merely a Google-able name; future
142           versions may also provide links and instructions.</td>
143         </tr>
144       </tbody>
145     </table>
147     <p>
148       Some notes on format and style:
149     </p>
151     <ul>
152       <li>
153         Each of these keys can be expressed in the short format
154         (<code>KEY: Value</code>) or the long format
155         (<code>--KEY--</code> with value beneath). You must use the
156         long format if multiple lines are needed, or if a long format
157         has been used already (that's why <code>ALIASES</code> in our
158         example is in the long format); otherwise, it's user preference.
159       </li>
160       <li>
161         The HTML descriptions should be wrapped at about 80 columns; do
162         not rely on editor word-wrapping.
163       </li>
164     </ul>
166     <p>
167       Also, as promised, here is the set of possible types:
168     </p>
170     <table class="table">
171       <thead>
172         <tr>
173           <th>Type</th>
174           <th>Example</th>
175           <th>Description</th>
176         </tr>
177       </thead>
178       <tbody>
179         <tr>
180           <td>string</td>
181           <td>'Foo'</td>
182           <td><a href="">String</a> without newlines</td>
183         </tr>
184         <tr>
185           <td>istring</td>
186           <td>'foo'</td>
187           <td>Case insensitive ASCII string without newlines</td>
188         </tr>
189         <tr>
190           <td>text</td>
191           <td>"A<em>\n</em>b"</td>
192           <td>String with newlines</td>
193         </tr>
194         <tr>
195           <td>itext</td>
196           <td>"a<em>\n</em>b"</td>
197           <td>Case insensitive ASCII string without newlines</td>
198         </tr>
199         <tr>
200           <td>int</td>
201           <td>23</td>
202           <td>Integer</td>
203         </tr>
204         <tr>
205           <td>float</td>
206           <td>3.0</td>
207           <td>Floating point number</td>
208         </tr>
209         <tr>
210           <td>bool</td>
211           <td>true</td>
212           <td>Boolean</td>
213         </tr>
214         <tr>
215           <td>lookup</td>
216           <td>array('key' =&gt; true)</td>
217           <td>Lookup array, used with <code>isset($var[$key])</code></td>
218         </tr>
219         <tr>
220           <td>list</td>
221           <td>array('f', 'b')</td>
222           <td>List array, with ordered numerical indexes</td>
223         </tr>
224         <tr>
225           <td>hash</td>
226           <td>array('key' =&gt; 'val')</td>
227           <td>Associative array of keys to values</td>
228         </tr>
229         <tr>
230           <td>mixed</td>
231           <td>new stdclass</td>
232           <td>Any PHP variable is fine</td>
233         </tr>
234       </tbody>
235     </table>
237     <p>
238       The examples represent what will be returned out of the configuration
239       object; users have a little bit of leeway when setting configuration
240       values (for example, a lookup value can be specified as a list;
241       HTML Purifier will flip it as necessary.) These types are defined
242       in <a href=";hb=HEAD;f=library/HTMLPurifier/VarParser.php">
243       library/HTMLPurifier/VarParser.php</a>.
244     </p>
246     <p>
247       For more information on what values are allowed, and how they are parsed,
248       consult <a href=";hb=HEAD;f=library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php">
249       library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php</a>, as well
250       as <a href=";hb=HEAD;f=library/HTMLPurifier/ConfigSchema/Interchange/Directive.php">
251       library/HTMLPurifier/ConfigSchema/Interchange/Directive.php</a> for
252       the semantics of the parsed values.
253     </p>
255     <h2>Refreshing the cache</h2>
257     <p>
258       You may have noticed that your directive file isn't doing anything
259       yet. That's because it hasn't been added to the runtime
260       <code>HTMLPurifier_ConfigSchema</code> instance. Run
261       <code>maintenance/generate-schema-cache.php</code> to fix this.
262       If there were no errors, you're good to go! Don't forget to add
263       some unit tests for your functionality!
264     </p>
266     <p>
267       If you ever make changes to your configuration directives, you
268       will need to run this script again.
269     </p>
270     <h2>Adding in-house schema definitions</h2>
272     <p>
273       Placing stuff directly in HTML Purifier's source tree is generally not a
274       good idea, so HTML Purifier 4.0.0+ has some facilities in place to make your
275       life easier.
276     </p>
278     <p>
279       The first is to pass an extra parameter to <code>maintenance/generate-schema-cache.php</code>
280       with the location of your directory (relative or absolute path will do). For example,
281       if I'm storing my custom definitions in <em>/var/htmlpurifier/myschema</em>, run:
282       <code>php maintenance/generate-schema-cache.php /var/htmlpurifier/myschema</code>.
283     </p>
285     <p>
286       Alternatively, you can create a small loader PHP file in the HTML Purifier base
287       directory named <code>config-schema.php</code> (this is the same directory
288       you would place a <code>test-settings.php</code> file).  In this file, add
289       the following line for each directory you want to load:
290     </p>
292 <pre>$builder-&gt;buildDir($interchange, '/var/htmlpurifier/myschema');</pre>
294     <p>You can even load a single file using:</p>
296 <pre>$builder-&gt;buildFile($interchange, '/var/htmlpurifier/myschema/MyApp.Directive.txt');</pre>
298     <p>Storing custom definitions that you don't plan on sending back upstream in
299     a separate directory is <em>definitely</em> a good idea! Additionally, picking
300     a good namespace can go a long way to saving you grief if you want to use
301     someone else's change, but they picked the same name, or if HTML Purifier
302     decides to add support for a configuration directive that has the same name.</p>
304     <!-- TODO: how to name directives that rely on naming conventions -->
306     <h2>Errors</h2>
308     <p>
309       All directive files go through a rigorous validation process
310       through <a href=";hb=HEAD;f=library/HTMLPurifier/ConfigSchema/Validator.php">
311       library/HTMLPurifier/ConfigSchema/Validator.php</a>, as well
312       as some basic checks during building. While
313       listing every error out here is out-of-scope for this document, we
314       can give some general tips for interpreting error messages.
315       There are two types of errors: builder errors and validation errors.
316     </p>
318     <h3>Builder errors</h3>
320     <blockquote>
321       <p>
322         <strong>Exception:</strong> Expected type string, got
323         integer in DEFAULT in directive hash 'Ns.Dir'
324       </p>
325     </blockquote>
327     <p>
328       You can identify a builder error by the keyword "directive hash."
329       These are the easiest to deal with, because they directly correspond
330       with your directive file. Find the offending directive file (which
331       is the directive hash plus the .txt extension), find the
332       offending index ("in DEFAULT" means the DEFAULT key) and fix the error.
333       This particular error would occur if your default value is not the same
334       type as TYPE.
335     </p>
337     <h3>Validation errors</h3>
339     <blockquote>
340       <p>
341         <strong>Exception:</strong> Alias 3 in valueAliases in directive
342         'Ns.Dir' must be a string
343       </p>
344     </blockquote>
346     <p>
347       These are a little trickier, because we're not actually validating
348       your directive file, or even the direct string hash representation.
349       We're validating an Interchange object, and the error messages do
350       not mention any string hash keys.
351     </p>
353     <p>
354       Nevertheless, it's not difficult to figure out what went wrong.
355       Read the "context" statements in reverse:
356     </p>
358     <dl>
359       <dt>in directive 'Ns.Dir'</dt>
360         <dd>This means we need to look at the directive file <code>Ns.Dir.txt</code></dd>
361       <dt>in valueAliases</dt>
362         <dd>There's no key actually called this, but there's one that's close:
363           VALUE-ALIASES. Indeed, that's where to look.</dd>
364       <dt>Alias 3</dt>
365         <dd>The value alias that is equal to 3 is the culprit.</dd>
366     </dl>
368     <p>
369       In this particular case, you're not allowed to alias integers values to
370       strings values.
371     </p>
373     <p>
374       The most difficult part is translating the Interchange member variable (valueAliases)
375       into a directive file key (VALUE-ALIASES), but there's a one-to-one
376       correspondence currently. If the two formats diverge, any discrepancies
377       will be described in <a href=";hb=HEAD;f=library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php">
378       library/HTMLPurifier/ConfigSchema/InterchangeBuilder.php</a>.
379     </p>
381     <h2>Internals</h2>
383     <p>
384       Much of the configuration schema framework's codebase deals with
385       shuffling data from one format to another, and doing validation on this
386       data.
387       The keystone of all of this is the <code>HTMLPurifier_ConfigSchema_Interchange</code>
388       class, which represents the purest, parsed representation of the schema.
389     </p>
391     <p>
392       Hand-writing this data is unwieldy, however, so we write directive files.
393       These directive files are parsed by <code>HTMLPurifier_StringHashParser</code>
394       into <code>HTMLPurifier_StringHash</code>es, which then
395       are run through <code>HTMLPurifier_ConfigSchema_InterchangeBuilder</code>
396       to construct the interchange object.
397     </p>
399     <p>
400       From the interchange object, the data can be siphoned into other forms
401       using <code>HTMLPurifier_ConfigSchema_Builder</code> subclasses.
402       For example, <code>HTMLPurifier_ConfigSchema_Builder_ConfigSchema</code>
403       generates a runtime <code>HTMLPurifier_ConfigSchema</code> object,
404       which <code>HTMLPurifier_Config</code> uses to validate its incoming
405       data. There is also an XML serializer, which is used to build documentation.
406     </p>
408   </body>
409 </html>
411 <!-- vim: et sw=4 sts=4