mirror of
https://github.com/ezyang/htmlpurifier.git
synced 2024-12-22 08:21:52 +00:00
Fix two bugs with caching of customized raw definitions.
The first bug is that we will repeatedly write out the result of a customized raw definition to the filesystem, even when a cache entry already exists. The second bug is that caching these definitions doesn't actually work (the cache entry is written but never used.) A new API for retrieving raw definitions permits the user to take advantage of caching. Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
This commit is contained in:
parent
6dcc37cb55
commit
f3d050c517
8
NEWS
8
NEWS
@ -9,7 +9,11 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
|
|||||||
. Internal change
|
. Internal change
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
4.2.1, unknown release date
|
4.3.0, unknown release date
|
||||||
|
# Fixed broken caching of customized raw definitions, but requires an
|
||||||
|
API change. The old API still works but will emit a warning,
|
||||||
|
see http://htmlpurifier.org/docs/enduser-customize.html#optimized
|
||||||
|
for how to upgrade your code.
|
||||||
! Added %HTML.Nofollow to add rel="nofollow" to external links.
|
! Added %HTML.Nofollow to add rel="nofollow" to external links.
|
||||||
! More types of SPL autoloaders allowed on later versions of PHP.
|
! More types of SPL autoloaders allowed on later versions of PHP.
|
||||||
! Implementations for position, top, left, right, bottom, z-index
|
! Implementations for position, top, left, right, bottom, z-index
|
||||||
@ -24,6 +28,8 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
|
|||||||
This safety check is only done for HTMLPurifier.auto.php; if you
|
This safety check is only done for HTMLPurifier.auto.php; if you
|
||||||
are using standalone or the specialized includes files, you're
|
are using standalone or the specialized includes files, you're
|
||||||
expected to know what you're doing.
|
expected to know what you're doing.
|
||||||
|
- Stop repeatedly writing the cache file after I'm done customizing a
|
||||||
|
raw definition.
|
||||||
|
|
||||||
4.2.0, released 2010-09-15
|
4.2.0, released 2010-09-15
|
||||||
! Added %Core.RemoveProcessingInstructions, which lets you remove
|
! Added %Core.RemoveProcessingInstructions, which lets you remove
|
||||||
|
@ -146,7 +146,9 @@
|
|||||||
<pre>$config = HTMLPurifier_Config::createDefault();
|
<pre>$config = HTMLPurifier_Config::createDefault();
|
||||||
$config->set('HTML.DefinitionID', 'enduser-customize.html tutorial');
|
$config->set('HTML.DefinitionID', 'enduser-customize.html tutorial');
|
||||||
$config->set('HTML.DefinitionRev', 1);
|
$config->set('HTML.DefinitionRev', 1);
|
||||||
$def = $config->getHTMLDefinition(true);</pre>
|
if ($def = $config->maybeGetRawHTMLDefinition()) {
|
||||||
|
// our code will go here
|
||||||
|
}</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Assuming that HTML Purifier has already been properly loaded (hint:
|
Assuming that HTML Purifier has already been properly loaded (hint:
|
||||||
@ -174,23 +176,15 @@ $def = $config->getHTMLDefinition(true);</pre>
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
The fourth line retrieves a raw <code>HTMLPurifier_HTMLDefinition</code>
|
The fourth line retrieves a raw <code>HTMLPurifier_HTMLDefinition</code>
|
||||||
object that we will be tweaking. If the parameter was removed, we
|
object that we will be tweaking. Interestingly enough, we have
|
||||||
would be retrieving a fully formed definition object, which is somewhat
|
placed it in an if block: this is because
|
||||||
useless for customization purposes.
|
<code>maybeGetRawHTMLDefinition</code>, as its name suggests, may
|
||||||
|
return a NULL, in which case we should skip doing any
|
||||||
|
initialization. This, in fact, will correspond to when our fully
|
||||||
|
customized object is already in the cache.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>Broken backwards-compatibility</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Those of you who have already been twiddling around with the raw
|
|
||||||
HTML definition object, you'll be noticing that you're getting an error
|
|
||||||
when you attempt to retrieve the raw definition object without specifying
|
|
||||||
a DefinitionID. It is vital to caching (see below) that you make a unique
|
|
||||||
name for your customized definition, so make up something right now and
|
|
||||||
things will operate again.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Turn off caching</h2>
|
<h2>Turn off caching</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@ -781,6 +775,75 @@ $form->excludes = array('form' => true);</strong></pre>
|
|||||||
<li><a href="http://repo.or.cz/w/htmlpurifier.git?a=blob;hb=HEAD;f=library/HTMLPurifier/ElementDef.php"><code>library/HTMLPurifier/ElementDef.php</code></a></li>
|
<li><a href="http://repo.or.cz/w/htmlpurifier.git?a=blob;hb=HEAD;f=library/HTMLPurifier/ElementDef.php"><code>library/HTMLPurifier/ElementDef.php</code></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<h2 id="optimized">Notes for HTML Purifier 4.2.0 and earlier</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Previously, this tutorial gave some incorrect template code for
|
||||||
|
editing raw definitions, and that template code will now produce the
|
||||||
|
error <q>Due to a documentation error in previous version of HTML
|
||||||
|
Purifier...</q> Here is how to mechanically transform old-style
|
||||||
|
code into new-style code.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
First, identify all code that edits the raw definition object, and
|
||||||
|
put it together. Ensure none of this code must be run on every
|
||||||
|
request; if some sub-part needs to always be run, move it outside
|
||||||
|
this block. Here is an example below, with the raw definition
|
||||||
|
object code bolded.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>$config = HTMLPurifier_Config::createDefault();
|
||||||
|
$config->set('HTML.DefinitionID', 'enduser-customize.html tutorial');
|
||||||
|
$config->set('HTML.DefinitionRev', 1);
|
||||||
|
$def = $config->getHTMLDefinition(true);
|
||||||
|
<strong>$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');</strong>
|
||||||
|
$purifier = new HTMLPurifier($config);</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Next, replace the raw definition retrieval with a
|
||||||
|
maybeGetRawHTMLDefinition method call inside an if conditional, and
|
||||||
|
place the editing code inside that if block.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>$config = HTMLPurifier_Config::createDefault();
|
||||||
|
$config->set('HTML.DefinitionID', 'enduser-customize.html tutorial');
|
||||||
|
$config->set('HTML.DefinitionRev', 1);
|
||||||
|
<strong>if ($def = $config->maybeGetRawHTMLDefinition()) {
|
||||||
|
$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');
|
||||||
|
}</strong>
|
||||||
|
$purifier = new HTMLPurifier($config);</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
And you're done! Alternatively, if you're OK with not ever caching
|
||||||
|
your code, the following will still work and not emit warnings.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>$config = HTMLPurifier_Config::createDefault();
|
||||||
|
$def = $config->getHTMLDefinition(true);
|
||||||
|
$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');
|
||||||
|
$purifier = new HTMLPurifier($config);</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
A slightly less efficient version of this was what was going on with
|
||||||
|
old versions of HTML Purifier.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<em>Technical notes:</em> ajh pointed out on <a
|
||||||
|
href="http://htmlpurifier.org/phorum/read.php?5,5164,5169#msg-5169">in a forum topic</a> that
|
||||||
|
HTML Purifier appeared to be repeatedly writing to the cache even
|
||||||
|
when a cache entry already existed. Investigation lead to the
|
||||||
|
discovery of the following infelicity: caching of customized
|
||||||
|
definitions didn't actually work! The problem was that even though
|
||||||
|
a cache file would be written out at the end of the process, there
|
||||||
|
was no way for HTML Purifier to say, <q>Actually, I've already got a
|
||||||
|
copy of your work, no need to reconfigure your
|
||||||
|
customizations</q>. This required the API to change: placing
|
||||||
|
all of the customizations to the raw definition object in a
|
||||||
|
conditional which could be skipped.
|
||||||
|
</p>
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
||||||
|
|
||||||
<!-- vim: et sw=4 sts=4
|
<!-- vim: et sw=4 sts=4
|
||||||
|
@ -76,7 +76,8 @@ class HTMLPurifier_Config
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to false if you do not want line and file numbers in errors
|
* Set to false if you do not want line and file numbers in errors
|
||||||
* (useful when unit testing)
|
* (useful when unit testing). This will also compress some errors
|
||||||
|
* and exceptions.
|
||||||
*/
|
*/
|
||||||
public $chatty = true;
|
public $chatty = true;
|
||||||
|
|
||||||
@ -318,26 +319,64 @@ class HTMLPurifier_Config
|
|||||||
* Retrieves object reference to the HTML definition.
|
* Retrieves object reference to the HTML definition.
|
||||||
* @param $raw Return a copy that has not been setup yet. Must be
|
* @param $raw Return a copy that has not been setup yet. Must be
|
||||||
* called before it's been setup, otherwise won't work.
|
* called before it's been setup, otherwise won't work.
|
||||||
|
* @param $optimized If true, this method may return null, to
|
||||||
|
* indicate that a cached version of the modified
|
||||||
|
* definition object is available and no further edits
|
||||||
|
* are necessary. Consider using
|
||||||
|
* maybeGetRawHTMLDefinition, which is more explicitly
|
||||||
|
* named, instead.
|
||||||
*/
|
*/
|
||||||
public function getHTMLDefinition($raw = false) {
|
public function getHTMLDefinition($raw = false, $optimized = false) {
|
||||||
return $this->getDefinition('HTML', $raw);
|
return $this->getDefinition('HTML', $raw, $optimized);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves object reference to the CSS definition
|
* Retrieves object reference to the CSS definition
|
||||||
* @param $raw Return a copy that has not been setup yet. Must be
|
* @param $raw Return a copy that has not been setup yet. Must be
|
||||||
* called before it's been setup, otherwise won't work.
|
* called before it's been setup, otherwise won't work.
|
||||||
|
* @param $optimized If true, this method may return null, to
|
||||||
|
* indicate that a cached version of the modified
|
||||||
|
* definition object is available and no further edits
|
||||||
|
* are necessary. Consider using
|
||||||
|
* maybeGetRawCSSDefinition, which is more explicitly
|
||||||
|
* named, instead.
|
||||||
*/
|
*/
|
||||||
public function getCSSDefinition($raw = false) {
|
public function getCSSDefinition($raw = false, $optimized = false) {
|
||||||
return $this->getDefinition('CSS', $raw);
|
return $this->getDefinition('CSS', $raw, $optimized);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves object reference to the URI definition
|
||||||
|
* @param $raw Return a copy that has not been setup yet. Must be
|
||||||
|
* called before it's been setup, otherwise won't work.
|
||||||
|
* @param $optimized If true, this method may return null, to
|
||||||
|
* indicate that a cached version of the modified
|
||||||
|
* definition object is available and no further edits
|
||||||
|
* are necessary. Consider using
|
||||||
|
* maybeGetRawURIDefinition, which is more explicitly
|
||||||
|
* named, instead.
|
||||||
|
*/
|
||||||
|
public function getURIDefinition($raw = false, $optimized = false) {
|
||||||
|
return $this->getDefinition('URI', $raw, $optimized);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a definition
|
* Retrieves a definition
|
||||||
* @param $type Type of definition: HTML, CSS, etc
|
* @param $type Type of definition: HTML, CSS, etc
|
||||||
* @param $raw Whether or not definition should be returned raw
|
* @param $raw Whether or not definition should be returned raw
|
||||||
|
* @param $optimized Only has an effect when $raw is true. Whether
|
||||||
|
* or not to return null if the result is already present in
|
||||||
|
* the cache. This is off by default for backwards
|
||||||
|
* compatibility reasons, but you need to do things this
|
||||||
|
* way in order to ensure that caching is done properly.
|
||||||
|
* Check out enduser-customize.html for more details.
|
||||||
|
* We probably won't ever change this default, as much as the
|
||||||
|
* maybe semantics is the "right thing to do."
|
||||||
*/
|
*/
|
||||||
public function getDefinition($type, $raw = false) {
|
public function getDefinition($type, $raw = false, $optimized = false) {
|
||||||
|
if ($optimized && !$raw) {
|
||||||
|
throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false");
|
||||||
|
}
|
||||||
if (!$this->finalized) $this->autoFinalize();
|
if (!$this->finalized) $this->autoFinalize();
|
||||||
// temporarily suspend locks, so we can handle recursive definition calls
|
// temporarily suspend locks, so we can handle recursive definition calls
|
||||||
$lock = $this->lock;
|
$lock = $this->lock;
|
||||||
@ -346,52 +385,137 @@ class HTMLPurifier_Config
|
|||||||
$cache = $factory->create($type, $this);
|
$cache = $factory->create($type, $this);
|
||||||
$this->lock = $lock;
|
$this->lock = $lock;
|
||||||
if (!$raw) {
|
if (!$raw) {
|
||||||
// see if we can quickly supply a definition
|
// full definition
|
||||||
|
// ---------------
|
||||||
|
// check if definition is in memory
|
||||||
if (!empty($this->definitions[$type])) {
|
if (!empty($this->definitions[$type])) {
|
||||||
if (!$this->definitions[$type]->setup) {
|
$def = $this->definitions[$type];
|
||||||
$this->definitions[$type]->setup($this);
|
// check if the definition is setup
|
||||||
$cache->set($this->definitions[$type], $this);
|
if ($def->setup) {
|
||||||
}
|
return $def;
|
||||||
return $this->definitions[$type];
|
|
||||||
}
|
|
||||||
// memory check missed, try cache
|
|
||||||
$this->definitions[$type] = $cache->get($this);
|
|
||||||
if ($this->definitions[$type]) {
|
|
||||||
// definition in cache, return it
|
|
||||||
return $this->definitions[$type];
|
|
||||||
}
|
|
||||||
} elseif (
|
|
||||||
!empty($this->definitions[$type]) &&
|
|
||||||
!$this->definitions[$type]->setup
|
|
||||||
) {
|
|
||||||
// raw requested, raw in memory, quick return
|
|
||||||
return $this->definitions[$type];
|
|
||||||
}
|
|
||||||
// quick checks failed, let's create the object
|
|
||||||
if ($type == 'HTML') {
|
|
||||||
$this->definitions[$type] = new HTMLPurifier_HTMLDefinition();
|
|
||||||
} elseif ($type == 'CSS') {
|
|
||||||
$this->definitions[$type] = new HTMLPurifier_CSSDefinition();
|
|
||||||
} elseif ($type == 'URI') {
|
|
||||||
$this->definitions[$type] = new HTMLPurifier_URIDefinition();
|
|
||||||
} else {
|
} else {
|
||||||
throw new HTMLPurifier_Exception("Definition of $type type not supported");
|
$def->setup($this);
|
||||||
|
if ($def->optimized) $cache->add($def, $this);
|
||||||
|
return $def;
|
||||||
}
|
}
|
||||||
// quick abort if raw
|
}
|
||||||
if ($raw) {
|
// check if definition is in cache
|
||||||
|
$def = $cache->get($this);
|
||||||
|
if ($def) {
|
||||||
|
// definition in cache, save to memory and return it
|
||||||
|
$this->definitions[$type] = $def;
|
||||||
|
return $def;
|
||||||
|
}
|
||||||
|
// initialize it
|
||||||
|
$def = $this->initDefinition($type);
|
||||||
|
// set it up
|
||||||
|
$this->lock = $type;
|
||||||
|
$def->setup($this);
|
||||||
|
$this->lock = null;
|
||||||
|
// save in cache
|
||||||
|
$cache->add($def, $this);
|
||||||
|
// return it
|
||||||
|
return $def;
|
||||||
|
} else {
|
||||||
|
// raw definition
|
||||||
|
// --------------
|
||||||
|
// check preconditions
|
||||||
|
$def = null;
|
||||||
|
if ($optimized) {
|
||||||
if (is_null($this->get($type . '.DefinitionID'))) {
|
if (is_null($this->get($type . '.DefinitionID'))) {
|
||||||
// fatally error out if definition ID not set
|
// fatally error out if definition ID not set
|
||||||
throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID");
|
throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID");
|
||||||
}
|
}
|
||||||
return $this->definitions[$type];
|
|
||||||
}
|
}
|
||||||
// set it up
|
if (!empty($this->definitions[$type])) {
|
||||||
$this->lock = $type;
|
$def = $this->definitions[$type];
|
||||||
$this->definitions[$type]->setup($this);
|
if ($def->setup && !$optimized) {
|
||||||
$this->lock = null;
|
$extra = $this->chatty ? " (try moving this code block earlier in your initialization)" : "";
|
||||||
// save in cache
|
throw new HTMLPurifier_Exception("Cannot retrieve raw definition after it has already been setup" . $extra);
|
||||||
$cache->set($this->definitions[$type], $this);
|
}
|
||||||
return $this->definitions[$type];
|
if ($def->optimized === null) {
|
||||||
|
$extra = $this->chatty ? " (try flushing your cache)" : "";
|
||||||
|
throw new HTMLPurifier_Exception("Optimization status of definition is unknown" . $extra);
|
||||||
|
}
|
||||||
|
if ($def->optimized !== $optimized) {
|
||||||
|
$msg = $optimized ? "optimized" : "unoptimized";
|
||||||
|
$extra = $this->chatty ? " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)" : "";
|
||||||
|
throw new HTMLPurifier_Exception("Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check if definition was in memory
|
||||||
|
if ($def) {
|
||||||
|
if ($def->setup) {
|
||||||
|
// invariant: $optimized === true (checked above)
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return $def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if optimized, check if definition was in cache
|
||||||
|
// (because we do the memory check first, this formulation
|
||||||
|
// is prone to cache slamming, but I think
|
||||||
|
// guaranteeing that either /all/ of the raw
|
||||||
|
// setup code or /none/ of it is run is more important.)
|
||||||
|
if ($optimized) {
|
||||||
|
// This code path only gets run once; once we put
|
||||||
|
// something in $definitions (which is guaranteed by the
|
||||||
|
// trailing code), we always short-circuit above.
|
||||||
|
$def = $cache->get($this);
|
||||||
|
if ($def) {
|
||||||
|
// save the full definition for later, but don't
|
||||||
|
// return it yet
|
||||||
|
$this->definitions[$type] = $def;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check invariants for creation
|
||||||
|
if (!$optimized) {
|
||||||
|
if (!is_null($this->get($type . '.DefinitionID'))) {
|
||||||
|
if ($this->chatty) {
|
||||||
|
$this->triggerError("Due to a documentation error in previous version of HTML Purifier, your definitions are not being cached. If this is OK, you can remove the %$type.DefinitionRev and %$type.DefinitionID declaration. Otherwise, modify your code to use maybeGetRawDefinition, and test if the returned value is null before making any edits (if it is null, that means that a cached version is available, and no raw operations are necessary). See <a href='http://htmlpurifier.org/docs/enduser-customize.html#optimized'>Customize</a> for more details", E_USER_WARNING);
|
||||||
|
} else {
|
||||||
|
$this->triggerError("Useless DefinitionID declaration", E_USER_WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// initialize it
|
||||||
|
$def = $this->initDefinition($type);
|
||||||
|
$def->optimized = $optimized;
|
||||||
|
return $def;
|
||||||
|
}
|
||||||
|
throw new HTMLPurifier_Exception("The impossible happened!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function initDefinition($type) {
|
||||||
|
// quick checks failed, let's create the object
|
||||||
|
if ($type == 'HTML') {
|
||||||
|
$def = new HTMLPurifier_HTMLDefinition();
|
||||||
|
} elseif ($type == 'CSS') {
|
||||||
|
$def = new HTMLPurifier_CSSDefinition();
|
||||||
|
} elseif ($type == 'URI') {
|
||||||
|
$def = new HTMLPurifier_URIDefinition();
|
||||||
|
} else {
|
||||||
|
throw new HTMLPurifier_Exception("Definition of $type type not supported");
|
||||||
|
}
|
||||||
|
$this->definitions[$type] = $def;
|
||||||
|
return $def;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maybeGetRawDefinition($name) {
|
||||||
|
return $this->getDefinition($name, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maybeGetRawHTMLDefinition() {
|
||||||
|
return $this->getDefinition('HTML', true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maybeGetRawCSSDefinition() {
|
||||||
|
return $this->getDefinition('CSS', true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function maybeGetRawURIDefinition() {
|
||||||
|
return $this->getDefinition('URI', true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -549,17 +673,22 @@ class HTMLPurifier_Config
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a nicely formatted error message by supplying the
|
* Produces a nicely formatted error message by supplying the
|
||||||
* stack frame information from two levels up and OUTSIDE of
|
* stack frame information OUTSIDE of HTMLPurifier_Config.
|
||||||
* HTMLPurifier_Config.
|
|
||||||
*/
|
*/
|
||||||
protected function triggerError($msg, $no) {
|
protected function triggerError($msg, $no) {
|
||||||
// determine previous stack frame
|
// determine previous stack frame
|
||||||
$backtrace = debug_backtrace();
|
|
||||||
if ($this->chatty && isset($backtrace[1])) {
|
|
||||||
$frame = $backtrace[1];
|
|
||||||
$extra = " on line {$frame['line']} in file {$frame['file']}";
|
|
||||||
} else {
|
|
||||||
$extra = '';
|
$extra = '';
|
||||||
|
if ($this->chatty) {
|
||||||
|
$trace = debug_backtrace();
|
||||||
|
// zip(tail(trace), trace) -- but PHP is not Haskell har har
|
||||||
|
for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {
|
||||||
|
if ($trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$frame = $trace[$i];
|
||||||
|
$extra = " invoked on line {$frame['line']} in file {$frame['file']}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
trigger_error($msg . $extra, $no);
|
trigger_error($msg . $extra, $no);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
HTML.Nofollow
|
HTML.Nofollow
|
||||||
TYPE: bool
|
TYPE: bool
|
||||||
VERSION: 4.2.1
|
VERSION: 4.3.0
|
||||||
DEFAULT: FALSE
|
DEFAULT: FALSE
|
||||||
--DESCRIPTION--
|
--DESCRIPTION--
|
||||||
If enabled, nofollow rel attributes are added to all outgoing links.
|
If enabled, nofollow rel attributes are added to all outgoing links.
|
||||||
|
@ -12,6 +12,17 @@ abstract class HTMLPurifier_Definition
|
|||||||
*/
|
*/
|
||||||
public $setup = false;
|
public $setup = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, write out the final definition object to the cache after
|
||||||
|
* setup. This will be true only if all invocations to get a raw
|
||||||
|
* definition object are also optimized. This does not cause file
|
||||||
|
* system thrashing because on subsequent calls the cached object
|
||||||
|
* is used and any writes to the raw definition object are short
|
||||||
|
* circuited. See enduser-customize.html for the high-level
|
||||||
|
* picture.
|
||||||
|
*/
|
||||||
|
public $optimized = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What type of definition is it?
|
* What type of definition is it?
|
||||||
*/
|
*/
|
||||||
|
@ -18,8 +18,6 @@ class HTMLPurifier_AttrValidator_ErrorsTest extends HTMLPurifier_ErrorsHarness
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testAttributesTransformedGlobalPre() {
|
function testAttributesTransformedGlobalPre() {
|
||||||
$this->config->set('HTML.DefinitionID',
|
|
||||||
'HTMLPurifier_AttrValidator_ErrorsTest::testAttributesTransformedGlobalPre');
|
|
||||||
$def = $this->config->getHTMLDefinition(true);
|
$def = $this->config->getHTMLDefinition(true);
|
||||||
generate_mock_once('HTMLPurifier_AttrTransform');
|
generate_mock_once('HTMLPurifier_AttrTransform');
|
||||||
$transform = new HTMLPurifier_AttrTransformMock();
|
$transform = new HTMLPurifier_AttrTransformMock();
|
||||||
|
@ -4,6 +4,7 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
|
|||||||
{
|
{
|
||||||
|
|
||||||
protected $schema;
|
protected $schema;
|
||||||
|
protected $oldFactory;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
// set up a dummy schema object for testing
|
// set up a dummy schema object for testing
|
||||||
@ -230,23 +231,58 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
|
|||||||
$this->assertNotEqual($def, $old_def);
|
$this->assertNotEqual($def, $old_def);
|
||||||
$this->assertTrue($def->setup);
|
$this->assertTrue($def->setup);
|
||||||
|
|
||||||
// test retrieval of raw definition
|
}
|
||||||
|
|
||||||
|
function test_getHTMLDefinition_deprecatedRawError() {
|
||||||
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
|
$config->chatty = false;
|
||||||
|
// test deprecated retrieval of raw definition
|
||||||
$config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->test_getHTMLDefinition()');
|
$config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->test_getHTMLDefinition()');
|
||||||
$config->set('HTML.DefinitionRev', 3);
|
$config->set('HTML.DefinitionRev', 3);
|
||||||
|
$this->expectError("Useless DefinitionID declaration");
|
||||||
$def = $config->getHTMLDefinition(true);
|
$def = $config->getHTMLDefinition(true);
|
||||||
$this->assertNotEqual($def, $old_def);
|
|
||||||
$this->assertEqual(false, $def->setup);
|
$this->assertEqual(false, $def->setup);
|
||||||
|
|
||||||
// auto initialization
|
// auto initialization
|
||||||
$config->getHTMLDefinition();
|
$config->getHTMLDefinition();
|
||||||
$this->assertTrue($def->setup);
|
$this->assertTrue($def->setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_getHTMLDefinition_optimizedRawError() {
|
||||||
|
$this->expectException(new HTMLPurifier_Exception("Cannot set optimized = true when raw = false"));
|
||||||
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
|
$config->getHTMLDefinition(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_getHTMLDefinition_rawAfterSetupError() {
|
||||||
|
$this->expectException(new HTMLPurifier_Exception("Cannot retrieve raw definition after it has already been setup"));
|
||||||
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
|
$config->chatty = false;
|
||||||
|
$config->getHTMLDefinition();
|
||||||
|
$config->getHTMLDefinition(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_getHTMLDefinition_inconsistentOptimizedError() {
|
||||||
|
$this->expectError("Useless DefinitionID declaration");
|
||||||
|
$this->expectException(new HTMLPurifier_Exception("Inconsistent use of optimized and unoptimized raw definition retrievals"));
|
||||||
|
$config = HTMLPurifier_Config::create(array('HTML.DefinitionID' => 'HTMLPurifier_ConfigTest->test_getHTMLDefinition_inconsistentOptimizedError'));
|
||||||
|
$config->chatty = false;
|
||||||
|
$config->getHTMLDefinition(true, false);
|
||||||
|
$config->getHTMLDefinition(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_getHTMLDefinition_inconsistentOptimizedError2() {
|
||||||
|
$this->expectException(new HTMLPurifier_Exception("Inconsistent use of optimized and unoptimized raw definition retrievals"));
|
||||||
|
$config = HTMLPurifier_Config::create(array('HTML.DefinitionID' => 'HTMLPurifier_ConfigTest->test_getHTMLDefinition_inconsistentOptimizedError2'));
|
||||||
|
$config->chatty = false;
|
||||||
|
$config->getHTMLDefinition(true, true);
|
||||||
|
$config->getHTMLDefinition(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_getHTMLDefinition_rawError() {
|
function test_getHTMLDefinition_rawError() {
|
||||||
$config = HTMLPurifier_Config::createDefault();
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
$this->expectException(new HTMLPurifier_Exception('Cannot retrieve raw version without specifying %HTML.DefinitionID'));
|
$this->expectException(new HTMLPurifier_Exception('Cannot retrieve raw version without specifying %HTML.DefinitionID'));
|
||||||
$def = $config->getHTMLDefinition(true);
|
$def = $config->getHTMLDefinition(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_getCSSDefinition() {
|
function test_getCSSDefinition() {
|
||||||
@ -458,6 +494,64 @@ class HTMLPurifier_ConfigTest extends HTMLPurifier_Harness
|
|||||||
$this->assertIdentical($config, $config2);
|
$this->assertIdentical($config, $config2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testDefinitionCachingNothing() {
|
||||||
|
list($mock, $config) = $this->setupCacheMock('HTML');
|
||||||
|
// should not touch the cache
|
||||||
|
$mock->expectNever('get');
|
||||||
|
$mock->expectNever('add');
|
||||||
|
$mock->expectNever('set');
|
||||||
|
$config->getDefinition('HTML', true);
|
||||||
|
$config->getDefinition('HTML', true);
|
||||||
|
$config->getDefinition('HTML');
|
||||||
|
$this->teardownCacheMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDefinitionCachingOptimized() {
|
||||||
|
list($mock, $config) = $this->setupCacheMock('HTML');
|
||||||
|
$mock->expectNever('set');
|
||||||
|
$config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->testDefinitionCachingOptimized');
|
||||||
|
$mock->expectOnce('get');
|
||||||
|
$mock->setReturnValue('get', null);
|
||||||
|
$this->assertTrue($config->maybeGetRawHTMLDefinition());
|
||||||
|
$this->assertTrue($config->maybeGetRawHTMLDefinition());
|
||||||
|
$mock->expectOnce('add');
|
||||||
|
$config->getDefinition('HTML');
|
||||||
|
$this->teardownCacheMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDefinitionCachingOptimizedHit() {
|
||||||
|
$fake_config = HTMLPurifier_Config::createDefault();
|
||||||
|
$fake_def = $fake_config->getHTMLDefinition();
|
||||||
|
list($mock, $config) = $this->setupCacheMock('HTML');
|
||||||
|
// should never frob cache
|
||||||
|
$mock->expectNever('add');
|
||||||
|
$mock->expectNever('set');
|
||||||
|
$config->set('HTML.DefinitionID', 'HTMLPurifier_ConfigTest->testDefinitionCachingOptimizedHit');
|
||||||
|
$mock->expectOnce('get');
|
||||||
|
$mock->setReturnValue('get', $fake_def);
|
||||||
|
$this->assertNull($config->maybeGetRawHTMLDefinition());
|
||||||
|
$config->getDefinition('HTML');
|
||||||
|
$config->getDefinition('HTML');
|
||||||
|
$this->teardownCacheMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setupCacheMock($type) {
|
||||||
|
// inject our definition cache mock globally (borrowed from
|
||||||
|
// DefinitionFactoryTest)
|
||||||
|
generate_mock_once("HTMLPurifier_DefinitionCacheFactory");
|
||||||
|
$factory = new HTMLPurifier_DefinitionCacheFactoryMock();
|
||||||
|
$this->oldFactory = HTMLPurifier_DefinitionCacheFactory::instance();
|
||||||
|
HTMLPurifier_DefinitionCacheFactory::instance($factory);
|
||||||
|
generate_mock_once("HTMLPurifier_DefinitionCache");
|
||||||
|
$mock = new HTMLPurifier_DefinitionCacheMock();
|
||||||
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
|
$factory->setReturnValue('create', $mock, array($type, $config));
|
||||||
|
return array($mock, $config);
|
||||||
|
}
|
||||||
|
protected function teardownCacheMock() {
|
||||||
|
HTMLPurifier_DefinitionCacheFactory::instance($this->oldFactory);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: et sw=4 sts=4
|
// vim: et sw=4 sts=4
|
||||||
|
@ -172,6 +172,7 @@ class HTMLPurifier_DefinitionCache_SerializerTest extends HTMLPurifier_Definitio
|
|||||||
* Asserts that a file does not exist, ignoring the stat cache
|
* Asserts that a file does not exist, ignoring the stat cache
|
||||||
*/
|
*/
|
||||||
function assertFileNotExist($file) {
|
function assertFileNotExist($file) {
|
||||||
|
clearstatcache();
|
||||||
$this->assertFalse(file_exists($file), 'Expected ' . $file . ' does not exist');
|
$this->assertFalse(file_exists($file), 'Expected ' . $file . ' does not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,9 +254,7 @@ a[href|title]
|
|||||||
|
|
||||||
function test_addAttribute() {
|
function test_addAttribute() {
|
||||||
|
|
||||||
$config = HTMLPurifier_Config::create(array(
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
'HTML.DefinitionID' => 'HTMLPurifier_HTMLDefinitionTest->test_addAttribute'
|
|
||||||
));
|
|
||||||
$def = $config->getHTMLDefinition(true);
|
$def = $config->getHTMLDefinition(true);
|
||||||
$def->addAttribute('span', 'custom', 'Enum#attribute');
|
$def->addAttribute('span', 'custom', 'Enum#attribute');
|
||||||
|
|
||||||
@ -269,9 +267,7 @@ a[href|title]
|
|||||||
|
|
||||||
function test_addAttribute_multiple() {
|
function test_addAttribute_multiple() {
|
||||||
|
|
||||||
$config = HTMLPurifier_Config::create(array(
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
'HTML.DefinitionID' => 'HTMLPurifier_HTMLDefinitionTest->test_addAttribute_multiple'
|
|
||||||
));
|
|
||||||
$def = $config->getHTMLDefinition(true);
|
$def = $config->getHTMLDefinition(true);
|
||||||
$def->addAttribute('span', 'custom', 'Enum#attribute');
|
$def->addAttribute('span', 'custom', 'Enum#attribute');
|
||||||
$def->addAttribute('span', 'foo', 'Text');
|
$def->addAttribute('span', 'foo', 'Text');
|
||||||
@ -285,9 +281,7 @@ a[href|title]
|
|||||||
|
|
||||||
function test_addElement() {
|
function test_addElement() {
|
||||||
|
|
||||||
$config = HTMLPurifier_Config::create(array(
|
$config = HTMLPurifier_Config::createDefault();
|
||||||
'HTML.DefinitionID' => 'HTMLPurifier_HTMLDefinitionTest->test_addElement'
|
|
||||||
));
|
|
||||||
$def = $config->getHTMLDefinition(true);
|
$def = $config->getHTMLDefinition(true);
|
||||||
$def->addElement('marquee', 'Inline', 'Inline', 'Common', array('width' => 'Length'));
|
$def->addElement('marquee', 'Inline', 'Inline', 'Common', array('width' => 'Length'));
|
||||||
|
|
||||||
@ -299,8 +293,6 @@ a[href|title]
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_injector() {
|
function test_injector() {
|
||||||
$this->config->set('HTML.DefinitionID', 'HTMLPurifier_HTMLDefinitionTest->test_injector');
|
|
||||||
|
|
||||||
generate_mock_once('HTMLPurifier_Injector');
|
generate_mock_once('HTMLPurifier_Injector');
|
||||||
$injector = new HTMLPurifier_InjectorMock();
|
$injector = new HTMLPurifier_InjectorMock();
|
||||||
$injector->name = 'MyInjector';
|
$injector->name = 'MyInjector';
|
||||||
@ -317,8 +309,6 @@ a[href|title]
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_injectorMissingNeeded() {
|
function test_injectorMissingNeeded() {
|
||||||
$this->config->set('HTML.DefinitionID', 'HTMLPurifier_HTMLDefinitionTest->test_injectorMissingNeeded');
|
|
||||||
|
|
||||||
generate_mock_once('HTMLPurifier_Injector');
|
generate_mock_once('HTMLPurifier_Injector');
|
||||||
$injector = new HTMLPurifier_InjectorMock();
|
$injector = new HTMLPurifier_InjectorMock();
|
||||||
$injector->name = 'MyInjector';
|
$injector->name = 'MyInjector';
|
||||||
@ -333,8 +323,6 @@ a[href|title]
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_injectorIntegration() {
|
function test_injectorIntegration() {
|
||||||
$this->config->set('HTML.DefinitionID', 'HTMLPurifier_HTMLDefinitionTest->test_injectorIntegration');
|
|
||||||
|
|
||||||
$module = $this->config->getHTMLDefinition(true)->getAnonymousModule();
|
$module = $this->config->getHTMLDefinition(true)->getAnonymousModule();
|
||||||
$module->info_injector[] = 'Linkify';
|
$module->info_injector[] = 'Linkify';
|
||||||
|
|
||||||
@ -345,8 +333,6 @@ a[href|title]
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_injectorIntegrationFail() {
|
function test_injectorIntegrationFail() {
|
||||||
$this->config->set('HTML.DefinitionID', 'HTMLPurifier_HTMLDefinitionTest->test_injectorIntegrationFail');
|
|
||||||
|
|
||||||
$this->config->set('HTML.Allowed', 'p');
|
$this->config->set('HTML.Allowed', 'p');
|
||||||
|
|
||||||
$module = $this->config->getHTMLDefinition(true)->getAnonymousModule();
|
$module = $this->config->getHTMLDefinition(true)->getAnonymousModule();
|
||||||
|
@ -5,7 +5,6 @@ class HTMLPurifier_HTMLModule_SafeEmbedTest extends HTMLPurifier_HTMLModuleHarne
|
|||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
$this->config->set('HTML.DefinitionID', 'HTMLPurifier_HTMLModule_SafeEmbedTest');
|
|
||||||
$def = $this->config->getHTMLDefinition(true);
|
$def = $this->config->getHTMLDefinition(true);
|
||||||
$def->manager->addModule('SafeEmbed');
|
$def->manager->addModule('SafeEmbed');
|
||||||
}
|
}
|
||||||
|
@ -80,9 +80,6 @@ alert(<b>bold</b>);
|
|||||||
}
|
}
|
||||||
|
|
||||||
function testRequiredAttributesTestNotPerformedOnEndTag() {
|
function testRequiredAttributesTestNotPerformedOnEndTag() {
|
||||||
$this->config->set('HTML.DefinitionID',
|
|
||||||
'HTMLPurifier_Strategy_RemoveForeignElementsTest'.
|
|
||||||
'->testRequiredAttributesTestNotPerformedOnEndTag');
|
|
||||||
$def = $this->config->getHTMLDefinition(true);
|
$def = $this->config->getHTMLDefinition(true);
|
||||||
$def->addElement('f', 'Block', 'Optional: #PCDATA', false, array('req*' => 'Text'));
|
$def->addElement('f', 'Block', 'Optional: #PCDATA', false, array('req*' => 'Text'));
|
||||||
$this->assertResult('<f req="text">Foo</f> Bar');
|
$this->assertResult('<f req="text">Foo</f> Bar');
|
||||||
|
Loading…
Reference in New Issue
Block a user