Release 2.0.0.
[htmlpurifier.git] / library / HTMLPurifier / Config.php
blob15d470442d3bb0c3f933d6384be18c1e1bb273e0
1 <?php
3 require_once 'HTMLPurifier/ConfigSchema.php';
5 // member variables
6 require_once 'HTMLPurifier/HTMLDefinition.php';
7 require_once 'HTMLPurifier/CSSDefinition.php';
8 require_once 'HTMLPurifier/Doctype.php';
9 require_once 'HTMLPurifier/DefinitionCacheFactory.php';
11 // accomodations for versions earlier than 4.3.10 and 5.0.2
12 // borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
13 if (!defined('PHP_EOL')) {
14 switch (strtoupper(substr(PHP_OS, 0, 3))) {
15 case 'WIN':
16 define('PHP_EOL', "\r\n");
17 break;
18 case 'DAR':
19 define('PHP_EOL', "\r");
20 break;
21 default:
22 define('PHP_EOL', "\n");
26 /**
27 * Configuration object that triggers customizable behavior.
29 * @warning This class is strongly defined: that means that the class
30 * will fail if an undefined directive is retrieved or set.
32 * @note Many classes that could (although many times don't) use the
33 * configuration object make it a mandatory parameter. This is
34 * because a configuration object should always be forwarded,
35 * otherwise, you run the risk of missing a parameter and then
36 * being stumped when a configuration directive doesn't work.
38 class HTMLPurifier_Config
41 /**
42 * HTML Purifier's version
44 var $version = '2.0.0';
46 /**
47 * Two-level associative array of configuration directives
49 var $conf;
51 /**
52 * Reference HTMLPurifier_ConfigSchema for value checking
54 var $def;
56 /**
57 * Indexed array of definitions
59 var $definitions;
61 /**
62 * Bool indicator whether or not config is finalized
64 var $finalized = false;
66 /**
67 * Bool indicator whether or not to automatically finalize
68 * the object if a read operation is done
70 var $autoFinalize = true;
72 /**
73 * Namespace indexed array of serials for specific namespaces (see
74 * getSerial for more info).
76 var $serials = array();
78 /**
79 * @param $definition HTMLPurifier_ConfigSchema that defines what directives
80 * are allowed.
82 function HTMLPurifier_Config(&$definition) {
83 $this->conf = $definition->defaults; // set up, copy in defaults
84 $this->def = $definition; // keep a copy around for checking
87 /**
88 * Convenience constructor that creates a config object based on a mixed var
89 * @static
90 * @param mixed $config Variable that defines the state of the config
91 * object. Can be: a HTMLPurifier_Config() object,
92 * an array of directives based on loadArray(),
93 * or a string filename of an ini file.
94 * @return Configured HTMLPurifier_Config object
96 function create($config) {
97 if (is_a($config, 'HTMLPurifier_Config')) {
98 // pass-through
99 return $config;
101 $ret = HTMLPurifier_Config::createDefault();
102 if (is_string($config)) $ret->loadIni($config);
103 elseif (is_array($config)) $ret->loadArray($config);
104 if (isset($revision)) $ret->revision = $revision;
105 return $ret;
109 * Convenience constructor that creates a default configuration object.
110 * @static
111 * @return Default HTMLPurifier_Config object.
113 function createDefault() {
114 $definition =& HTMLPurifier_ConfigSchema::instance();
115 $config = new HTMLPurifier_Config($definition);
116 return $config;
120 * Retreives a value from the configuration.
121 * @param $namespace String namespace
122 * @param $key String key
124 function get($namespace, $key, $from_alias = false) {
125 if (!$this->finalized && $this->autoFinalize) $this->finalize();
126 if (!isset($this->def->info[$namespace][$key])) {
127 // can't add % due to SimpleTest bug
128 trigger_error('Cannot retrieve value of undefined directive ' . htmlspecialchars("$namespace.$key"),
129 E_USER_WARNING);
130 return;
132 if ($this->def->info[$namespace][$key]->class == 'alias') {
133 $d = $this->def->info[$namespace][$key];
134 trigger_error('Cannot get value from aliased directive, use real name ' . $d->namespace . '.' . $d->name,
135 E_USER_ERROR);
136 return;
138 return $this->conf[$namespace][$key];
142 * Retreives an array of directives to values from a given namespace
143 * @param $namespace String namespace
145 function getBatch($namespace) {
146 if (!$this->finalized && $this->autoFinalize) $this->finalize();
147 if (!isset($this->def->info[$namespace])) {
148 trigger_error('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace),
149 E_USER_WARNING);
150 return;
152 return $this->conf[$namespace];
156 * Returns a md5 signature of a segment of the configuration object
157 * that uniquely identifies that particular configuration
158 * @param $namespace Namespace to get serial for
160 function getBatchSerial($namespace) {
161 if (empty($this->serials[$namespace])) {
162 $this->serials[$namespace] = md5(serialize($this->getBatch($namespace)));
164 return $this->serials[$namespace];
168 * Retrieves all directives, organized by namespace
170 function getAll() {
171 if (!$this->finalized && $this->autoFinalize) $this->finalize();
172 return $this->conf;
176 * Sets a value to configuration.
177 * @param $namespace String namespace
178 * @param $key String key
179 * @param $value Mixed value
181 function set($namespace, $key, $value, $from_alias = false) {
182 if ($this->isFinalized('Cannot set directive after finalization')) return;
183 if (!isset($this->def->info[$namespace][$key])) {
184 trigger_error('Cannot set undefined directive ' . htmlspecialchars("$namespace.$key") . ' to value',
185 E_USER_WARNING);
186 return;
188 if ($this->def->info[$namespace][$key]->class == 'alias') {
189 if ($from_alias) {
190 trigger_error('Double-aliases not allowed, please fix '.
191 'ConfigSchema bug with' . "$namespace.$key");
193 $this->set($this->def->info[$namespace][$key]->namespace,
194 $this->def->info[$namespace][$key]->name,
195 $value, true);
196 return;
198 $value = $this->def->validate(
199 $value,
200 $type = $this->def->info[$namespace][$key]->type,
201 $this->def->info[$namespace][$key]->allow_null
203 if (is_string($value)) {
204 // resolve value alias if defined
205 if (isset($this->def->info[$namespace][$key]->aliases[$value])) {
206 $value = $this->def->info[$namespace][$key]->aliases[$value];
208 if ($this->def->info[$namespace][$key]->allowed !== true) {
209 // check to see if the value is allowed
210 if (!isset($this->def->info[$namespace][$key]->allowed[$value])) {
211 trigger_error('Value not supported, valid values are: ' .
212 $this->_listify($this->def->info[$namespace][$key]->allowed), E_USER_WARNING);
213 return;
217 if ($this->def->isError($value)) {
218 trigger_error('Value for ' . "$namespace.$key" . ' is of invalid type, should be ' . $type, E_USER_WARNING);
219 return;
221 $this->conf[$namespace][$key] = $value;
223 // reset definitions if the directives they depend on changed
224 // this is a very costly process, so it's discouraged
225 // with finalization
226 if ($namespace == 'HTML' || $namespace == 'CSS') {
227 $this->definitions[$namespace] = null;
230 $this->serials[$namespace] = false;
234 * Convenience function for error reporting
235 * @private
237 function _listify($lookup) {
238 $list = array();
239 foreach ($lookup as $name => $b) $list[] = $name;
240 return implode(', ', $list);
244 * Retrieves reference to the HTML definition.
245 * @param $raw Return a copy that has not been setup yet. Must be
246 * called before it's been setup, otherwise won't work.
248 function &getHTMLDefinition($raw = false) {
249 return $this->getDefinition('HTML', $raw);
253 * Retrieves reference to the CSS definition
255 function &getCSSDefinition($raw = false) {
256 return $this->getDefinition('CSS', $raw);
260 * Retrieves a definition
261 * @param $type Type of definition: HTML, CSS, etc
262 * @param $raw Whether or not definition should be returned raw
264 function &getDefinition($type, $raw = false) {
265 if (!$this->finalized && $this->autoFinalize) $this->finalize();
266 $factory = HTMLPurifier_DefinitionCacheFactory::instance();
267 $cache = $factory->create($type, $this);
268 if (!$raw) {
269 // see if we can quickly supply a definition
270 if (!empty($this->definitions[$type])) {
271 if (!$this->definitions[$type]->setup) {
272 $this->definitions[$type]->setup($this);
274 return $this->definitions[$type];
276 // memory check missed, try cache
277 $this->definitions[$type] = $cache->get($this);
278 if ($this->definitions[$type]) {
279 // definition in cache, return it
280 return $this->definitions[$type];
282 } elseif (
283 !empty($this->definitions[$type]) &&
284 !$this->definitions[$type]->setup
286 // raw requested, raw in memory, quick return
287 return $this->definitions[$type];
289 // quick checks failed, let's create the object
290 if ($type == 'HTML') {
291 $this->definitions[$type] = new HTMLPurifier_HTMLDefinition();
292 } elseif ($type == 'CSS') {
293 $this->definitions[$type] = new HTMLPurifier_CSSDefinition();
294 } else {
295 trigger_error("Definition of $type type not supported");
296 $false = false;
297 return $false;
299 // quick abort if raw
300 if ($raw) {
301 if (is_null($this->get($type, 'DefinitionID'))) {
302 // fatally error out if definition ID not set
303 trigger_error("Cannot retrieve raw version without specifying %$type.DefinitionID", E_USER_ERROR);
304 $false = false;
305 return $false;
307 return $this->definitions[$type];
309 // set it up
310 $this->definitions[$type]->setup($this);
311 // save in cache
312 $cache->set($this->definitions[$type], $this);
313 return $this->definitions[$type];
317 * Loads configuration values from an array with the following structure:
318 * Namespace.Directive => Value
319 * @param $config_array Configuration associative array
321 function loadArray($config_array) {
322 if ($this->isFinalized('Cannot load directives after finalization')) return;
323 foreach ($config_array as $key => $value) {
324 $key = str_replace('_', '.', $key);
325 if (strpos($key, '.') !== false) {
326 // condensed form
327 list($namespace, $directive) = explode('.', $key);
328 $this->set($namespace, $directive, $value);
329 } else {
330 $namespace = $key;
331 $namespace_values = $value;
332 foreach ($namespace_values as $directive => $value) {
333 $this->set($namespace, $directive, $value);
340 * Loads configuration values from $_GET/$_POST that were posted
341 * via ConfigForm
342 * @param $array $_GET or $_POST array to import
343 * @param $index Index/name that the config variables are in
344 * @param $mq_fix Boolean whether or not to enable magic quotes fix
345 * @static
347 function loadArrayFromForm($array, $index, $mq_fix = true) {
348 $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
349 $mq = get_magic_quotes_gpc() && $mq_fix;
350 foreach ($array as $key => $value) {
351 if (!strncmp($key, 'Null_', 5) && !empty($value)) {
352 unset($array[substr($key, 5)]);
353 unset($array[$key]);
355 if ($mq) $array[$key] = stripslashes($value);
357 return @HTMLPurifier_Config::create($array);
361 * Loads configuration values from an ini file
362 * @param $filename Name of ini file
364 function loadIni($filename) {
365 if ($this->isFinalized('Cannot load directives after finalization')) return;
366 $array = parse_ini_file($filename, true);
367 $this->loadArray($array);
371 * Checks whether or not the configuration object is finalized.
372 * @param $error String error message, or false for no error
374 function isFinalized($error = false) {
375 if ($this->finalized && $error) {
376 trigger_error($error, E_USER_ERROR);
378 return $this->finalized;
382 * Finalizes configuration only if auto finalize is on and not
383 * already finalized
385 function autoFinalize() {
386 if (!$this->finalized && $this->autoFinalize) $this->finalize();
390 * Finalizes a configuration object, prohibiting further change
392 function finalize() {
393 $this->finalized = true;