3 require_once 'HTMLPurifier/Error.php';
4 require_once 'HTMLPurifier/ConfigDef.php';
5 require_once 'HTMLPurifier/ConfigDef/Namespace.php';
6 require_once 'HTMLPurifier/ConfigDef/Directive.php';
7 require_once 'HTMLPurifier/ConfigDef/DirectiveAlias.php';
10 * Configuration definition, defines directives and their defaults.
11 * @todo The ability to define things multiple times is confusing and should
12 * be factored out to its own function named registerDependency() or
13 * addNote(), where only the namespace.name and an extra descriptions
14 * documenting the nature of the dependency are needed. Since it's
15 * possible that the dependency is registered before the configuration
16 * is defined, deferring it to some sort of cache until it actually
17 * gets defined would be wise, keeping it opaque until it does get
18 * defined. We could add a finalize() method which would cause it to
19 * error out if we get a dangling dependency. It's difficult, however,
20 * to know whether or not it's a dependency, or a codependency, that is
21 * neither of them fully depends on it. Where does the configuration go
22 * then? This could be partially resolved by allowing blanket definitions
23 * and then splitting them up into finer-grained versions, however, there
24 * might be implementation difficulties in ini files regarding order of
27 class HTMLPurifier_ConfigSchema
{
30 * Defaults of the directives and namespaces.
31 * @note This shares the exact same structure as HTMLPurifier_Config::$conf
33 var $defaults = array();
36 * Definition of the directives.
41 * Definition of namespaces.
43 var $info_namespace = array();
46 * Lookup table of allowed types.
50 'istring' => 'Case-insensitive string',
54 'lookup' => 'Lookup array',
55 'list' => 'Array list',
56 'hash' => 'Associative array',
61 * Initializes the default namespaces.
63 function initialize() {
64 $this->defineNamespace('Core', 'Core features that are always available.');
65 $this->defineNamespace('Attr', 'Features regarding attribute validation.');
66 $this->defineNamespace('URI', 'Features regarding Uniform Resource Identifiers.');
67 $this->defineNamespace('HTML', 'Configuration regarding allowed HTML.');
68 $this->defineNamespace('CSS', 'Configuration regarding allowed CSS.');
69 $this->defineNamespace('Test', 'Developer testing configuration for our unit tests.');
73 * Retrieves an instance of the application-wide configuration definition.
76 function &instance($prototype = null) {
78 if ($prototype !== null) {
79 $instance = $prototype;
80 } elseif ($instance === null ||
$prototype === true) {
81 $instance = new HTMLPurifier_ConfigSchema();
82 $instance->initialize();
88 * Defines a directive for configuration
90 * @warning Will fail of directive's namespace is defined
91 * @param $namespace Namespace the directive is in
92 * @param $name Key of directive
93 * @param $default Default value of directive
94 * @param $type Allowed type of the directive. See
95 * HTMLPurifier_DirectiveDef::$type for allowed values
96 * @param $description Description of directive for documentation
99 $namespace, $name, $default, $type,
102 $def =& HTMLPurifier_ConfigSchema
::instance();
103 if (!isset($def->info
[$namespace])) {
104 trigger_error('Cannot define directive for undefined namespace',
108 if (!ctype_alnum($name)) {
109 trigger_error('Directive name must be alphanumeric',
113 if (empty($description)) {
114 trigger_error('Description must be non-empty',
118 if (isset($def->info
[$namespace][$name])) {
120 $def->info
[$namespace][$name]->type
!== $type ||
121 $def->defaults
[$namespace][$name] !== $default
123 trigger_error('Inconsistent default or type, cannot redefine');
128 $type_values = explode('/', $type, 2);
129 $type = $type_values[0];
130 $modifier = isset($type_values[1]) ?
$type_values[1] : false;
131 $allow_null = ($modifier === 'null');
133 if (!isset($def->types
[$type])) {
134 trigger_error('Invalid type for configuration directive',
138 $default = $def->validate($default, $type, $allow_null);
139 if ($def->isError($default)) {
140 trigger_error('Default value does not match directive type',
144 $def->info
[$namespace][$name] =
145 new HTMLPurifier_ConfigDef_Directive();
146 $def->info
[$namespace][$name]->type
= $type;
147 $def->info
[$namespace][$name]->allow_null
= $allow_null;
148 $def->defaults
[$namespace][$name] = $default;
150 $backtrace = debug_backtrace();
151 $file = $def->mungeFilename($backtrace[0]['file']);
152 $line = $backtrace[0]['line'];
153 $def->info
[$namespace][$name]->addDescription($file,$line,$description);
157 * Defines a namespace for directives to be put into.
159 * @param $namespace Namespace's name
160 * @param $description Description of the namespace
162 function defineNamespace($namespace, $description) {
163 $def =& HTMLPurifier_ConfigSchema
::instance();
164 if (isset($def->info
[$namespace])) {
165 trigger_error('Cannot redefine namespace', E_USER_ERROR
);
168 if (!ctype_alnum($namespace)) {
169 trigger_error('Namespace name must be alphanumeric',
173 if (empty($description)) {
174 trigger_error('Description must be non-empty',
178 $def->info
[$namespace] = array();
179 $def->info_namespace
[$namespace] = new HTMLPurifier_ConfigDef_Namespace();
180 $def->info_namespace
[$namespace]->description
= $description;
181 $def->defaults
[$namespace] = array();
185 * Defines a directive value alias.
187 * Directive value aliases are convenient for developers because it lets
188 * them set a directive to several values and get the same result.
190 * @param $namespace Directive's namespace
191 * @param $name Name of Directive
192 * @param $alias Name of aliased value
193 * @param $real Value aliased value will be converted into
195 function defineValueAliases($namespace, $name, $aliases) {
196 $def =& HTMLPurifier_ConfigSchema
::instance();
197 if (!isset($def->info
[$namespace][$name])) {
198 trigger_error('Cannot set value alias for non-existant directive',
202 foreach ($aliases as $alias => $real) {
203 if (!$def->info
[$namespace][$name] !== true &&
204 !isset($def->info
[$namespace][$name]->allowed
[$real])
206 trigger_error('Cannot define alias to value that is not allowed',
210 if (isset($def->info
[$namespace][$name]->allowed
[$alias])) {
211 trigger_error('Cannot define alias over allowed value',
215 $def->info
[$namespace][$name]->aliases
[$alias] = $real;
220 * Defines a set of allowed values for a directive.
222 * @param $namespace Namespace of directive
223 * @param $name Name of directive
224 * @param $allowed_values Arraylist of allowed values
226 function defineAllowedValues($namespace, $name, $allowed_values) {
227 $def =& HTMLPurifier_ConfigSchema
::instance();
228 if (!isset($def->info
[$namespace][$name])) {
229 trigger_error('Cannot define allowed values for undefined directive',
233 $directive =& $def->info
[$namespace][$name];
234 $type = $directive->type
;
235 if ($type != 'string' && $type != 'istring') {
236 trigger_error('Cannot define allowed values for directive whose type is not string',
240 if ($directive->allowed
=== true) {
241 $directive->allowed
= array();
243 foreach ($allowed_values as $value) {
244 $directive->allowed
[$value] = true;
246 if ($def->defaults
[$namespace][$name] !== null &&
247 !isset($directive->allowed
[$def->defaults
[$namespace][$name]])) {
248 trigger_error('Default value must be in allowed range of variables',
250 $directive->allowed
= true; // undo undo!
256 * Defines a directive alias for backwards compatibility
259 * @param $name Directive that will be aliased
260 * @param $new_namespace
261 * @param $new_name Directive that the alias will be to
263 function defineAlias($namespace, $name, $new_namespace, $new_name) {
264 $def =& HTMLPurifier_ConfigSchema
::instance();
265 if (!isset($def->info
[$namespace])) {
266 trigger_error('Cannot define directive alias in undefined namespace',
270 if (!ctype_alnum($name)) {
271 trigger_error('Directive name must be alphanumeric',
275 if (isset($def->info
[$namespace][$name])) {
276 trigger_error('Cannot define alias over directive',
280 if (!isset($def->info
[$new_namespace][$new_name])) {
281 trigger_error('Cannot define alias to undefined directive',
285 if ($def->info
[$new_namespace][$new_name]->class == 'alias') {
286 trigger_error('Cannot define alias to alias',
290 $def->info
[$namespace][$name] =
291 new HTMLPurifier_ConfigDef_DirectiveAlias(
292 $new_namespace, $new_name);
296 * Validate a variable according to type. Return null if invalid.
298 function validate($var, $type, $allow_null = false) {
299 if (!isset($this->types
[$type])) {
300 trigger_error('Invalid type', E_USER_ERROR
);
303 if ($allow_null && $var === null) return null;
309 if (!is_string($var)) break;
310 if ($type === 'istring') $var = strtolower($var);
313 if (is_string($var) && ctype_digit($var)) $var = (int) $var;
314 elseif (!is_int($var)) break;
317 if (is_string($var) && is_numeric($var)) $var = (float) $var;
318 elseif (!is_float($var)) break;
321 if (is_int($var) && ($var === 0 ||
$var === 1)) {
323 } elseif (is_string($var)) {
324 if ($var == 'on' ||
$var == 'true' ||
$var == '1') {
326 } elseif ($var == 'off' ||
$var == 'false' ||
$var == '0') {
331 } elseif (!is_bool($var)) break;
336 if (is_string($var)) {
337 // special case: technically, this is an array with
338 // a single empty string item, but having an empty
339 // array is more intuitive
340 if ($var == '') return array();
341 // simplistic string to array method that only works
342 // for simple lists of tag names or alphanumeric characters
343 $var = explode(',',$var);
345 foreach ($var as $i => $j) $var[$i] = trim($j);
347 if (!is_array($var)) break;
348 $keys = array_keys($var);
349 if ($keys === array_keys($keys)) {
350 if ($type == 'list') return $var;
351 elseif ($type == 'lookup') {
353 foreach ($var as $key) {
359 if ($type === 'lookup') {
360 foreach ($var as $key => $value) {
366 $error = new HTMLPurifier_Error();
371 * Takes an absolute path and munges it into a more manageable relative path
373 function mungeFilename($filename) {
374 $offset = strrpos($filename, 'HTMLPurifier');
375 $filename = substr($filename, $offset);
376 $filename = str_replace('\\', '/', $filename);
381 * Checks if var is an HTMLPurifier_Error object
383 function isError($var) {
384 if (!is_object($var)) return false;
385 if (!is_a($var, 'HTMLPurifier_Error')) return false;