4 * Configuration definition, defines directives and their defaults.
5 * @todo Build documentation generation capabilities.
6 * @todo The ability to define things multiple times is confusing and should
7 * be factored out to its own function named registerDependency() or
8 * addNote(), where only the namespace.name and an extra descriptions
9 * documenting the nature of the dependency are needed. Since it's
10 * possible that the dependency is registered before the configuration
11 * is defined, deferring it to some sort of cache until it actually
12 * gets defined would be wise, keeping it opaque until it does get
13 * defined. We could add a finalize() method which would cause it to
14 * error out if we get a dangling dependency. It's difficult, however,
15 * to know whether or not it's a dependency, or a codependency, that is
16 * neither of them fully depends on it. Where does the configuration go
17 * then? This could be partially resolved by allowing blanket definitions
18 * and then splitting them up into finer-grained versions, however, there
19 * might be implementation difficulties in ini files regarding order of
22 class HTMLPurifier_ConfigDef
{
25 * Defaults of the directives and namespaces.
26 * @note This shares the exact same structure as HTMLPurifier_Config::$conf
28 var $defaults = array();
31 * Definition of the directives.
36 * Definition of namespaces.
38 var $info_namespace = array();
41 * Lookup table of allowed types.
56 * Initializes the default namespaces.
58 function initialize() {
59 $this->defineNamespace('Core', 'Core features that are always available.');
60 $this->defineNamespace('Attr', 'Features regarding attribute validation.');
61 $this->defineNamespace('URI', 'Features regarding Uniform Resource Identifiers.');
62 $this->defineNamespace('HTML', 'Configuration regarding allowed HTML.');
63 $this->defineNamespace('CSS', 'Configuration regarding allowed CSS.');
64 $this->defineNamespace('Test', 'Testing configuration for our unit tests.');
68 * Retrieves an instance of the application-wide configuration definition.
70 function &instance($prototype = null) {
72 if ($prototype !== null) {
73 $instance = $prototype;
74 } elseif ($instance === null ||
$prototype === true) {
75 $instance = new HTMLPurifier_ConfigDef();
76 $instance->initialize();
82 * Defines a directive for configuration
83 * @warning Will fail of directive's namespace is defined
84 * @todo Collect information on description and allow redefinition
85 * so that multiple files can register a dependency on a
86 * configuration directive.
87 * @param $namespace Namespace the directive is in
88 * @param $name Key of directive
89 * @param $default Default value of directive
90 * @param $type Allowed type of the directive. See
91 * HTMLPurifier_DirectiveDef::$type for allowed values
92 * @param $description Description of directive for documentation
95 $namespace, $name, $default, $type,
98 $def =& HTMLPurifier_ConfigDef
::instance();
99 if (!isset($def->info
[$namespace])) {
100 trigger_error('Cannot define directive for undefined namespace',
104 if (!ctype_alnum($name)) {
105 trigger_error('Directive name must be alphanumeric',
109 if (isset($def->info
[$namespace][$name])) {
111 $def->info
[$namespace][$name]->type
!== $type ||
112 $def->defaults
[$namespace][$name] !== $default
114 trigger_error('Inconsistent default or type, cannot redefine');
118 if (!isset($def->types
[$type])) {
119 trigger_error('Invalid type for configuration directive',
123 if ($def->validate($default, $type) === null) {
124 trigger_error('Default value does not match directive type',
128 $def->info
[$namespace][$name] =
129 new HTMLPurifier_ConfigEntity_Directive();
130 $def->info
[$namespace][$name]->type
= $type;
131 $def->defaults
[$namespace][$name] = $default;
133 $backtrace = debug_backtrace();
134 $file = $def->mungeFilename($backtrace[0]['file']);
135 $line = $backtrace[0]['line'];
136 $def->info
[$namespace][$name]->addDescription($file,$line,$description);
140 * Defines a namespace for directives to be put into.
141 * @param $namespace Namespace's name
142 * @param $description Description of the namespace
144 function defineNamespace($namespace, $description) {
145 $def =& HTMLPurifier_ConfigDef
::instance();
146 if (isset($def->info
[$namespace])) {
147 trigger_error('Cannot redefine namespace', E_USER_ERROR
);
150 if (!ctype_alnum($namespace)) {
151 trigger_error('Namespace name must be alphanumeric',
155 $def->info
[$namespace] = array();
156 $def->info_namespace
[$namespace] = new HTMLPurifier_ConfigEntity_Namespace();
157 $backtrace = debug_backtrace();
158 $file = $def->mungeFilename($backtrace[0]['file']);
159 $line = $backtrace[0]['line'];
160 $def->info_namespace
[$namespace]->addDescription($file,$line,$description);
161 $def->defaults
[$namespace] = array();
165 * Defines a directive value alias.
167 * Directive value aliases are convenient for developers because it lets
168 * them set a directive to several values and get the same result.
169 * @param $namespace Directive's namespace
170 * @param $name Name of Directive
171 * @param $alias Name of aliased value
172 * @param $real Value aliased value will be converted into
174 function defineValueAliases($namespace, $name, $aliases) {
175 $def =& HTMLPurifier_ConfigDef
::instance();
176 if (!isset($def->info
[$namespace][$name])) {
177 trigger_error('Cannot set value alias for non-existant directive',
181 foreach ($aliases as $alias => $real) {
182 if (!$def->info
[$namespace][$name] !== true &&
183 !isset($def->info
[$namespace][$name]->allowed
[$real])
185 trigger_error('Cannot define alias to value that is not allowed',
189 if (isset($def->info
[$namespace][$name]->allowed
[$alias])) {
190 trigger_error('Cannot define alias over allowed value',
194 $def->info
[$namespace][$name]->aliases
[$alias] = $real;
199 * Defines a set of allowed values for a directive.
200 * @param $namespace Namespace of directive
201 * @param $name Name of directive
202 * @param $allowed_values Arraylist of allowed values
204 function defineAllowedValues($namespace, $name, $allowed_values) {
205 $def =& HTMLPurifier_ConfigDef
::instance();
206 if (!isset($def->info
[$namespace][$name])) {
207 trigger_error('Cannot define allowed values for undefined directive',
211 if ($def->info
[$namespace][$name]->allowed
=== true) {
212 $def->info
[$namespace][$name]->allowed
= array();
214 foreach ($allowed_values as $value) {
215 $def->info
[$namespace][$name]->allowed
[$value] = true;
220 * Validate a variable according to type. Return null if invalid.
222 function validate($var, $type) {
223 if (!isset($this->types
[$type])) {
224 trigger_error('Invalid type', E_USER_ERROR
);
232 if (!is_string($var)) return;
233 if ($type === 'istring') $var = strtolower($var);
236 if (is_string($var) && ctype_digit($var)) $var = (int) $var;
237 elseif (!is_int($var)) return;
240 if (is_string($var) && is_numeric($var)) $var = (float) $var;
241 elseif (!is_float($var)) return;
244 if (is_int($var) && ($var === 0 ||
$var === 1)) {
246 } elseif (!is_bool($var)) return;
251 if (!is_array($var)) return;
252 $keys = array_keys($var);
253 if ($keys === array_keys($keys)) {
254 if ($type == 'list') return $var;
255 elseif ($type == 'lookup') {
257 foreach ($var as $key) {
263 if ($type === 'lookup') {
264 foreach ($var as $key => $value) {
272 function mungeFilename($filename) {
273 $offset = strrpos($filename, 'HTMLPurifier');
274 $filename = substr($filename, $offset);
275 $filename = str_replace('\\', '/', $filename);
282 * Base class for configuration entity
284 class HTMLPurifier_ConfigEntity
287 * Plaintext descriptions of the configuration entity is. Organized by
288 * file and line number, so multiple descriptions are allowed.
290 var $descriptions = array();
293 * Adds a description to the array
295 function addDescription($file, $line, $description) {
296 if (!isset($this->descriptions
[$file])) $this->descriptions
[$file] = array();
297 $this->descriptions
[$file][$line] = $description;
302 * Structure object describing of a namespace
304 class HTMLPurifier_ConfigEntity_Namespace
extends HTMLPurifier_ConfigEntity
{}
307 * Structure object containing definition of a directive.
308 * @note This structure does not contain default values
310 class HTMLPurifier_ConfigEntity_Directive
extends HTMLPurifier_ConfigEntity
314 * Hash of value aliases, i.e. values that are equivalent.
316 var $aliases = array();
319 * Lookup table of allowed values of the element, bool true if all allowed.
324 * Allowed type of the directive. Values are:
326 * - istring (case insensitive string)
330 * - lookup (array of value => true)
331 * - list (regular numbered index array)
332 * - hash (array of key => value)
333 * - mixed (anything goes)