HTMLPurifier/Config.php Quellcode

Config.php
gehe zur Dokumentation dieser Datei
1 <?php
2 
18 {
19 
24  public $version = '4.6.0';
25 
31  public $autoFinalize = true;
32 
33  // protected member variables
34 
40  protected $serials = array();
41 
46  protected $serial;
47 
52  protected $parser = null;
53 
60  public $def;
61 
66  protected $definitions;
67 
72  protected $finalized = false;
73 
78  protected $plist;
79 
84  private $aliasMode;
85 
92  public $chatty = true;
93 
98  private $lock;
99 
106  public function __construct($definition, $parent = null)
107  {
108  $parent = $parent ? $parent : $definition->defaultPlist;
109  $this->plist = new HTMLPurifier_PropertyList($parent);
110  $this->def = $definition; // keep a copy around for checking
111  $this->parser = new HTMLPurifier_VarParser_Flexible();
112  }
113 
123  public static function create($config, $schema = null)
124  {
125  if ($config instanceof HTMLPurifier_Config) {
126  // pass-through
127  return $config;
128  }
129  if (!$schema) {
131  } else {
132  $ret = new HTMLPurifier_Config($schema);
133  }
134  if (is_string($config)) {
135  $ret->loadIni($config);
136  } elseif (is_array($config)) $ret->loadArray($config);
137  return $ret;
138  }
139 
145  public static function inherit(HTMLPurifier_Config $config)
146  {
147  return new HTMLPurifier_Config($config->def, $config->plist);
148  }
149 
154  public static function createDefault()
155  {
156  $definition = HTMLPurifier_ConfigSchema::instance();
157  $config = new HTMLPurifier_Config($definition);
158  return $config;
159  }
160 
169  public function get($key, $a = null)
170  {
171  if ($a !== null) {
172  $this->triggerError(
173  "Using deprecated API: use \$config->get('$key.$a') instead",
174  E_USER_WARNING
175  );
176  $key = "$key.$a";
177  }
178  if (!$this->finalized) {
179  $this->autoFinalize();
180  }
181  if (!isset($this->def->info[$key])) {
182  // can't add % due to SimpleTest bug
183  $this->triggerError(
184  'Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
185  E_USER_WARNING
186  );
187  return;
188  }
189  if (isset($this->def->info[$key]->isAlias)) {
190  $d = $this->def->info[$key];
191  $this->triggerError(
192  'Cannot get value from aliased directive, use real name ' . $d->key,
193  E_USER_ERROR
194  );
195  return;
196  }
197  if ($this->lock) {
198  list($ns) = explode('.', $key);
199  if ($ns !== $this->lock) {
200  $this->triggerError(
201  'Cannot get value of namespace ' . $ns . ' when lock for ' .
202  $this->lock .
203  ' is active, this probably indicates a Definition setup method ' .
204  'is accessing directives that are not within its namespace',
205  E_USER_ERROR
206  );
207  return;
208  }
209  }
210  return $this->plist->get($key);
211  }
212 
220  public function getBatch($namespace)
221  {
222  if (!$this->finalized) {
223  $this->autoFinalize();
224  }
225  $full = $this->getAll();
226  if (!isset($full[$namespace])) {
227  $this->triggerError(
228  'Cannot retrieve undefined namespace ' .
229  htmlspecialchars($namespace),
230  E_USER_WARNING
231  );
232  return;
233  }
234  return $full[$namespace];
235  }
236 
247  public function getBatchSerial($namespace)
248  {
249  if (empty($this->serials[$namespace])) {
250  $batch = $this->getBatch($namespace);
251  unset($batch['DefinitionRev']);
252  $this->serials[$namespace] = sha1(serialize($batch));
253  }
254  return $this->serials[$namespace];
255  }
256 
263  public function getSerial()
264  {
265  if (empty($this->serial)) {
266  $this->serial = sha1(serialize($this->getAll()));
267  }
268  return $this->serial;
269  }
270 
276  public function getAll()
277  {
278  if (!$this->finalized) {
279  $this->autoFinalize();
280  }
281  $ret = array();
282  foreach ($this->plist->squash() as $name => $value) {
283  list($ns, $key) = explode('.', $name, 2);
284  $ret[$ns][$key] = $value;
285  }
286  return $ret;
287  }
288 
296  public function set($key, $value, $a = null)
297  {
298  if (strpos($key, '.') === false) {
299  $namespace = $key;
300  $directive = $value;
301  $value = $a;
302  $key = "$key.$directive";
303  $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);
304  } else {
305  list($namespace) = explode('.', $key);
306  }
307  if ($this->isFinalized('Cannot set directive after finalization')) {
308  return;
309  }
310  if (!isset($this->def->info[$key])) {
311  $this->triggerError(
312  'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
313  E_USER_WARNING
314  );
315  return;
316  }
317  $def = $this->def->info[$key];
318 
319  if (isset($def->isAlias)) {
320  if ($this->aliasMode) {
321  $this->triggerError(
322  'Double-aliases not allowed, please fix '.
323  'ConfigSchema bug with' . $key,
324  E_USER_ERROR
325  );
326  return;
327  }
328  $this->aliasMode = true;
329  $this->set($def->key, $value);
330  $this->aliasMode = false;
331  $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
332  return;
333  }
334 
335  // Raw type might be negative when using the fully optimized form
336  // of stdclass, which indicates allow_null == true
337  $rtype = is_int($def) ? $def : $def->type;
338  if ($rtype < 0) {
339  $type = -$rtype;
340  $allow_null = true;
341  } else {
342  $type = $rtype;
343  $allow_null = isset($def->allow_null);
344  }
345 
346  try {
347  $value = $this->parser->parse($value, $type, $allow_null);
348  } catch (HTMLPurifier_VarParserException $e) {
349  $this->triggerError(
350  'Value for ' . $key . ' is of invalid type, should be ' .
352  E_USER_WARNING
353  );
354  return;
355  }
356  if (is_string($value) && is_object($def)) {
357  // resolve value alias if defined
358  if (isset($def->aliases[$value])) {
359  $value = $def->aliases[$value];
360  }
361  // check to see if the value is allowed
362  if (isset($def->allowed) && !isset($def->allowed[$value])) {
363  $this->triggerError(
364  'Value not supported, valid values are: ' .
365  $this->_listify($def->allowed),
366  E_USER_WARNING
367  );
368  return;
369  }
370  }
371  $this->plist->set($key, $value);
372 
373  // reset definitions if the directives they depend on changed
374  // this is a very costly process, so it's discouraged
375  // with finalization
376  if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {
377  $this->definitions[$namespace] = null;
378  }
379 
380  $this->serials[$namespace] = false;
381  }
382 
390  private function _listify($lookup)
391  {
392  $list = array();
393  foreach ($lookup as $name => $b) {
394  $list[] = $name;
395  }
396  return implode(', ', $list);
397  }
398 
413  public function getHTMLDefinition($raw = false, $optimized = false)
414  {
415  return $this->getDefinition('HTML', $raw, $optimized);
416  }
417 
432  public function getCSSDefinition($raw = false, $optimized = false)
433  {
434  return $this->getDefinition('CSS', $raw, $optimized);
435  }
436 
451  public function getURIDefinition($raw = false, $optimized = false)
452  {
453  return $this->getDefinition('URI', $raw, $optimized);
454  }
455 
473  public function getDefinition($type, $raw = false, $optimized = false)
474  {
475  if ($optimized && !$raw) {
476  throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false");
477  }
478  if (!$this->finalized) {
479  $this->autoFinalize();
480  }
481  // temporarily suspend locks, so we can handle recursive definition calls
482  $lock = $this->lock;
483  $this->lock = null;
485  $cache = $factory->create($type, $this);
486  $this->lock = $lock;
487  if (!$raw) {
488  // full definition
489  // ---------------
490  // check if definition is in memory
491  if (!empty($this->definitions[$type])) {
492  $def = $this->definitions[$type];
493  // check if the definition is setup
494  if ($def->setup) {
495  return $def;
496  } else {
497  $def->setup($this);
498  if ($def->optimized) {
499  $cache->add($def, $this);
500  }
501  return $def;
502  }
503  }
504  // check if definition is in cache
505  $def = $cache->get($this);
506  if ($def) {
507  // definition in cache, save to memory and return it
508  $this->definitions[$type] = $def;
509  return $def;
510  }
511  // initialize it
512  $def = $this->initDefinition($type);
513  // set it up
514  $this->lock = $type;
515  $def->setup($this);
516  $this->lock = null;
517  // save in cache
518  $cache->add($def, $this);
519  // return it
520  return $def;
521  } else {
522  // raw definition
523  // --------------
524  // check preconditions
525  $def = null;
526  if ($optimized) {
527  if (is_null($this->get($type . '.DefinitionID'))) {
528  // fatally error out if definition ID not set
529  throw new HTMLPurifier_Exception(
530  "Cannot retrieve raw version without specifying %$type.DefinitionID"
531  );
532  }
533  }
534  if (!empty($this->definitions[$type])) {
535  $def = $this->definitions[$type];
536  if ($def->setup && !$optimized) {
537  $extra = $this->chatty ?
538  " (try moving this code block earlier in your initialization)" :
539  "";
540  throw new HTMLPurifier_Exception(
541  "Cannot retrieve raw definition after it has already been setup" .
542  $extra
543  );
544  }
545  if ($def->optimized === null) {
546  $extra = $this->chatty ? " (try flushing your cache)" : "";
547  throw new HTMLPurifier_Exception(
548  "Optimization status of definition is unknown" . $extra
549  );
550  }
551  if ($def->optimized !== $optimized) {
552  $msg = $optimized ? "optimized" : "unoptimized";
553  $extra = $this->chatty ?
554  " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)"
555  : "";
556  throw new HTMLPurifier_Exception(
557  "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra
558  );
559  }
560  }
561  // check if definition was in memory
562  if ($def) {
563  if ($def->setup) {
564  // invariant: $optimized === true (checked above)
565  return null;
566  } else {
567  return $def;
568  }
569  }
570  // if optimized, check if definition was in cache
571  // (because we do the memory check first, this formulation
572  // is prone to cache slamming, but I think
573  // guaranteeing that either /all/ of the raw
574  // setup code or /none/ of it is run is more important.)
575  if ($optimized) {
576  // This code path only gets run once; once we put
577  // something in $definitions (which is guaranteed by the
578  // trailing code), we always short-circuit above.
579  $def = $cache->get($this);
580  if ($def) {
581  // save the full definition for later, but don't
582  // return it yet
583  $this->definitions[$type] = $def;
584  return null;
585  }
586  }
587  // check invariants for creation
588  if (!$optimized) {
589  if (!is_null($this->get($type . '.DefinitionID'))) {
590  if ($this->chatty) {
591  $this->triggerError(
592  'Due to a documentation error in previous version of HTML Purifier, your ' .
593  'definitions are not being cached. If this is OK, you can remove the ' .
594  '%$type.DefinitionRev and %$type.DefinitionID declaration. Otherwise, ' .
595  'modify your code to use maybeGetRawDefinition, and test if the returned ' .
596  'value is null before making any edits (if it is null, that means that a ' .
597  'cached version is available, and no raw operations are necessary). See ' .
598  '<a href="http://htmlpurifier.org/docs/enduser-customize.html#optimized">' .
599  'Customize</a> for more details',
600  E_USER_WARNING
601  );
602  } else {
603  $this->triggerError(
604  "Useless DefinitionID declaration",
605  E_USER_WARNING
606  );
607  }
608  }
609  }
610  // initialize it
611  $def = $this->initDefinition($type);
612  $def->optimized = $optimized;
613  return $def;
614  }
615  throw new HTMLPurifier_Exception("The impossible happened!");
616  }
617 
626  private function initDefinition($type)
627  {
628  // quick checks failed, let's create the object
629  if ($type == 'HTML') {
631  } elseif ($type == 'CSS') {
633  } elseif ($type == 'URI') {
635  } else {
636  throw new HTMLPurifier_Exception(
637  "Definition of $type type not supported"
638  );
639  }
640  $this->definitions[$type] = $def;
641  return $def;
642  }
643 
644  public function maybeGetRawDefinition($name)
645  {
646  return $this->getDefinition($name, true, true);
647  }
648 
649  public function maybeGetRawHTMLDefinition()
650  {
651  return $this->getDefinition('HTML', true, true);
652  }
653 
654  public function maybeGetRawCSSDefinition()
655  {
656  return $this->getDefinition('CSS', true, true);
657  }
658 
659  public function maybeGetRawURIDefinition()
660  {
661  return $this->getDefinition('URI', true, true);
662  }
663 
670  public function loadArray($config_array)
671  {
672  if ($this->isFinalized('Cannot load directives after finalization')) {
673  return;
674  }
675  foreach ($config_array as $key => $value) {
676  $key = str_replace('_', '.', $key);
677  if (strpos($key, '.') !== false) {
678  $this->set($key, $value);
679  } else {
680  $namespace = $key;
681  $namespace_values = $value;
682  foreach ($namespace_values as $directive => $value2) {
683  $this->set($namespace .'.'. $directive, $value2);
684  }
685  }
686  }
687  }
688 
699  public static function getAllowedDirectivesForForm($allowed, $schema = null)
700  {
701  if (!$schema) {
703  }
704  if ($allowed !== true) {
705  if (is_string($allowed)) {
706  $allowed = array($allowed);
707  }
708  $allowed_ns = array();
709  $allowed_directives = array();
710  $blacklisted_directives = array();
711  foreach ($allowed as $ns_or_directive) {
712  if (strpos($ns_or_directive, '.') !== false) {
713  // directive
714  if ($ns_or_directive[0] == '-') {
715  $blacklisted_directives[substr($ns_or_directive, 1)] = true;
716  } else {
717  $allowed_directives[$ns_or_directive] = true;
718  }
719  } else {
720  // namespace
721  $allowed_ns[$ns_or_directive] = true;
722  }
723  }
724  }
725  $ret = array();
726  foreach ($schema->info as $key => $def) {
727  list($ns, $directive) = explode('.', $key, 2);
728  if ($allowed !== true) {
729  if (isset($blacklisted_directives["$ns.$directive"])) {
730  continue;
731  }
732  if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) {
733  continue;
734  }
735  }
736  if (isset($def->isAlias)) {
737  continue;
738  }
739  if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') {
740  continue;
741  }
742  $ret[] = array($ns, $directive);
743  }
744  return $ret;
745  }
746 
759  public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
760  {
761  $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
762  $config = HTMLPurifier_Config::create($ret, $schema);
763  return $config;
764  }
765 
774  public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true)
775  {
776  $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
777  $this->loadArray($ret);
778  }
779 
792  public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
793  {
794  if ($index !== false) {
795  $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
796  }
797  $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
798 
799  $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);
800  $ret = array();
801  foreach ($allowed as $key) {
802  list($ns, $directive) = $key;
803  $skey = "$ns.$directive";
804  if (!empty($array["Null_$skey"])) {
805  $ret[$ns][$directive] = null;
806  continue;
807  }
808  if (!isset($array[$skey])) {
809  continue;
810  }
811  $value = $mq ? stripslashes($array[$skey]) : $array[$skey];
812  $ret[$ns][$directive] = $value;
813  }
814  return $ret;
815  }
816 
822  public function loadIni($filename)
823  {
824  if ($this->isFinalized('Cannot load directives after finalization')) {
825  return;
826  }
827  $array = parse_ini_file($filename, true);
828  $this->loadArray($array);
829  }
830 
838  public function isFinalized($error = false)
839  {
840  if ($this->finalized && $error) {
841  $this->triggerError($error, E_USER_ERROR);
842  }
843  return $this->finalized;
844  }
845 
850  public function autoFinalize()
851  {
852  if ($this->autoFinalize) {
853  $this->finalize();
854  } else {
855  $this->plist->squash(true);
856  }
857  }
858 
862  public function finalize()
863  {
864  $this->finalized = true;
865  $this->parser = null;
866  }
867 
875  protected function triggerError($msg, $no)
876  {
877  // determine previous stack frame
878  $extra = '';
879  if ($this->chatty) {
880  $trace = debug_backtrace();
881  // zip(tail(trace), trace) -- but PHP is not Haskell har har
882  for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {
883  // XXX this is not correct on some versions of HTML Purifier
884  if ($trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
885  continue;
886  }
887  $frame = $trace[$i];
888  $extra = " invoked on line {$frame['line']} in file {$frame['file']}";
889  break;
890  }
891  }
892  trigger_error($msg . $extra, $no);
893  }
894 
901  public function serialize()
902  {
903  $this->getDefinition('HTML');
904  $this->getDefinition('CSS');
905  $this->getDefinition('URI');
906  return serialize($this);
907  }
908 
909 }
910 
911 // vim: et sw=4 sts=4




Korrekturen, Hinweise und Ergänzungen

Bitte scheuen Sie sich nicht und melden Sie, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Support Forum wählen. Es ist eine kostenlose Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden kommentarlos entfernt.