0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2024-12-22 08:21:52 +00:00

Revamp Configuration classes, breaking backwards configuration compatibility (not that there was much to broken to begin with). Fix bug involving PHP 4 object typecasting.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@203 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang 2006-08-11 20:23:41 +00:00
parent 30a8266fc1
commit 0db1cbb7ac
9 changed files with 222 additions and 83 deletions

7
docs/config.txt Normal file
View File

@ -0,0 +1,7 @@
Configuration
Configuration is documented on a per-use case: if a class uses a certain
value from the configuration object, it has to define its name and what the
value is used for. This means decentralized configuration declaration that
is nevertheless error checking.

View File

@ -4,7 +4,7 @@ require_once 'HTMLPurifier/AttrDef.php';
require_once 'HTMLPurifier/IDAccumulator.php';
// NOTE QUIRKY BEHAVIOR: even though this is the id processor, it
// will ignore HTMLPurifier_Config::$attr_id_blacklist: it will only
// will ignore directive Attr:IDBlacklist, since it will only
// go according to the ID accumulator. Since the accumulator is
// automatically generated, it will have already absorbed the
// blacklist. If you're hacking around, make sure you use load()!

View File

@ -4,86 +4,36 @@
class HTMLPurifier_Config
{
// which ids do we not allow?
var $attr_id_blacklist = array();
var $conf;
//////////////////////////////////////////////////////////////////////////
// all below properties have not been implemented yet
// prefix all ids with this
var $attr_id_prefix = '';
// if there's a prefix, we may want to transparently rewrite the
// URLs we parse too. However, we can only do it when it's a pure
// anchor link, so it's not foolproof
var $attr_id_rewrite_urls = false;
// determines how the classes array should be construed:
// blacklist - allow allow except those in $classes_blacklist
// whitelist - only allow those in $classes_whitelist
// when one is chosen, the other has no effect
var $attr_class_mode = 'blacklist';
var $attr_class_blacklist = array();
var $attr_class_whitelist = array();
// designate whether or not to allow numerals in language code subtags
// RFC 1766, the current standard referenced by XML, does not permit
// numbers, but,
// RFC 3066, the superseding best practice standard since January 2001,
// permits them.
// we allow numbers by default, although you generally never see them
// at all.
var $attr_lang_alpha = false;
// max amount of pixels allowed to be specified
var $attr_pixels_hmax = 600; // horizontal context
var $attr_pixels_vmax = 1200; // vertical context
// allowed URI schemes
var $uri_schemes = array(
// based off of MediaWiki's default settings
// the ones that definitely must be implemented (they're the same though)
'http' => true, // "Hypertext Transfer Protocol", nuf' said
'https' => true, // HTTP over SSL (Secure Socket Layer)
// quite useful, but not necessary
'mailto' => true,// Email
'ftp' => true, // "File Transfer Protocol"
'irc' => true, // "Internet Relay Chat", usually needs another app
// obscure
'telnet' => true,// network protocol for non-secure remote terminal sessions
// for Usenet, these two are similar, but distinct
'nntp' => true, // individual Netnews articles
'news' => true // newsgroup or individual Netnews articles
// gopher and worldwind excluded
);
// will munge all URIs to a different URI, which should redirect
// the user to the applicable page. A urlencoded version of the URI
// will replace any instances of %s in the string. One possible
// string is 'http://www.google.com/url?q=%s'. Useful for preventing
// pagerank from being sent to other sites
var $uri_munge = false;
// will add rel="nofollow" to all links, also helps prevent pagerank
// from going around
var $uri_add_relnofollow = false;
// web root of the website, we'll try to auto-detect it. Something
// like 'www.example.com/'???
var $uri_webroot = null;
// transform all relative URIs into their absolute forms, requires
// $uri_webroot
var $uri_make_absolute = false;
// disables external links, requires $uri_webroot
var $uri_disable_external = false;
function HTMLPurifier_Config(&$definition) {
$this->conf = $definition->info; // set up the defaults
}
function createDefault() {
$config = new HTMLPurifier_Config();
$definition =& HTMLPurifier_ConfigDef::instance();
$config = new HTMLPurifier_Config($definition);
return $config;
}
function get($namespace, $key) {
if (!isset($this->conf[$namespace][$key])) {
trigger_error('Cannot retrieve value of undefined directive',
E_USER_ERROR);
return;
}
return $this->conf[$namespace][$key];
}
function set($namespace, $key, $value) {
if (!isset($this->conf[$namespace][$key])) {
trigger_error('Cannot set undefined directive to value',
E_USER_ERROR);
return;
}
$this->conf[$namespace][$key] = $value;
}
}
?>

View File

@ -0,0 +1,48 @@
<?php
class HTMLPurifier_ConfigDef {
var $info = array();
function initialize() {
$this->defineNamespace('Core', 'Core features that are always available.');
$this->defineNamespace('Attr', 'Features regarding attribute validation.');
}
function &instance($prototype = null) {
static $instance;
if ($prototype !== null) {
$instance = $prototype;
} elseif ($instance === null || $prototype === true) {
$instance = new HTMLPurifier_ConfigDef();
$instance->initialize();
}
return $instance;
}
function define($namespace, $name, $default, $description) {
$def =& HTMLPurifier_ConfigDef::instance();
if (!isset($def->info[$namespace])) {
trigger_error('Cannot define directive for undefined namespace',
E_USER_ERROR);
return;
}
if (isset($def->info[$namespace][$name])) {
trigger_error('Cannot redefine directive', E_USER_ERROR);
return;
}
$def->info[$namespace][$name] = $default;
}
function defineNamespace($namespace, $description) {
$def =& HTMLPurifier_ConfigDef::instance();
if (isset($def->info[$namespace])) {
trigger_error('Cannot redefine namespace', E_USER_ERROR);
return;
}
$def->info[$namespace] = array();
}
}
?>

View File

@ -3,6 +3,11 @@
require_once 'HTMLPurifier/Strategy.php';
require_once 'HTMLPurifier/Definition.php';
require_once 'HTMLPurifier/IDAccumulator.php';
require_once 'HTMLPurifier/ConfigDef.php';
HTMLPurifier_ConfigDef::define(
'Attr', 'IDBlacklist', array(),
'Array of IDs not allowed in the document.');
/**
* Validate all attributes in the tokens.
@ -26,7 +31,7 @@ class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
// eventually, we'll have a dedicated context object to hold
// all these accumulators and caches. For now, just an IDAccumulator
$accumulator = new HTMLPurifier_IDAccumulator();
$accumulator->load($config->attr_id_blacklist);
$accumulator->load($config->get('Attr', 'IDBlacklist'));
// create alias to global definition array, see also $defs
// DEFINITION CALL
@ -69,7 +74,7 @@ class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy
// call the definition
if ( isset($defs[$attr_key]) ) {
// there is a local definition defined
if (!$defs[$attr_key]) {
if ($defs[$attr_key] === false) {
// We've explicitly been told not to allow this element.
// This is usually when there's a global definition
// that must be overridden.

View File

@ -0,0 +1,63 @@
<?php
require_once 'HTMLPurifier/ConfigDef.php';
class HTMLPurifier_ConfigDefTest extends UnitTestCase
{
var $old_copy;
var $our_copy;
function setUp() {
// yes, I know this is slightly convoluted, but that's the price
// you pay for using Singletons. Good thing we can overload it.
// first, let's get a clean copy to do tests
$our_copy = new HTMLPurifier_ConfigDef();
// get the old copy
$this->old_copy = HTMLPurifier_ConfigDef::instance();
// put in our copy, and reassign to the REAL reference
$this->our_copy =& HTMLPurifier_ConfigDef::instance($our_copy);
}
function tearDown() {
// testing is done, restore the old copy
HTMLPurifier_ConfigDef::instance($this->old_copy);
}
function testNormal() {
HTMLPurifier_ConfigDef::defineNamespace('Core', 'Configuration that '.
'is always available.');
$this->assertIdentical( array(
'Core' => array()
), $this->our_copy->info);
// note that the description is silently dropped
HTMLPurifier_ConfigDef::define('Core', 'Name', 'default value',
'This is a description of the directive.');
$this->assertIdentical( array(
'Core' => array(
'Name' => 'default value'
)
), $this->our_copy->info);
// test an invalid namespace
HTMLPurifier_ConfigDef::define('Extension', 'Name', false, 'This is '.
'for an extension, but we have not defined its namespace!');
$this->assertError('Cannot define directive for undefined namespace');
$this->assertNoErrors();
$this->swallowErrors();
// test overloading already defined value
HTMLPurifier_ConfigDef::define('Core', 'Name', 89,
'What, you\'re not allowed to overload directives? Bummer!');
$this->assertError('Cannot redefine directive');
$this->assertNoErrors();
$this->swallowErrors();
}
}
?>

View File

@ -0,0 +1,44 @@
<?php
require_once 'HTMLPurifier/Config.php';
class HTMLPurifier_ConfigTest extends UnitTestCase
{
function test() {
$def = new HTMLPurifier_ConfigDef();
$def->info = array(
'Core' => array('Key' => false),
'Attr' => array('Key' => 42),
'Extension' => array('Pert' => 'moo')
);
$config = new HTMLPurifier_Config($def);
// test default value retrieval
$this->assertIdentical($config->get('Core', 'Key'), false);
$this->assertIdentical($config->get('Attr', 'Key'), 42);
$this->assertIdentical($config->get('Extension', 'Pert'), 'moo');
// set some values
$config->set('Core', 'Key', 'foobar');
$this->assertIdentical($config->get('Core', 'Key'), 'foobar');
// try to retrieve undefined value
$config->get('Core', 'NotDefined');
$this->assertError('Cannot retrieve value of undefined directive');
$this->assertNoErrors();
$this->swallowErrors();
// try to set undefined value
$config->set('Foobar', 'Key', 'foobar');
$this->assertError('Cannot set undefined directive to value');
$this->assertNoErrors();
$this->swallowErrors();
}
}
?>

View File

@ -46,7 +46,7 @@ class HTMLPurifier_Strategy_ValidateAttributesTest extends
$inputs[7] = '<div id="invalid">Invalid</div>';
$expect[7] = '<div>Invalid</div>';
$config[7] = HTMLPurifier_Config::createDefault();
$config[7]->attr_id_blacklist = array('invalid');
$config[7]->set('Attr', 'IDBlacklist', array('invalid'));
// test classes
$inputs[8] = '<div class="valid">Valid</div>';

View File

@ -34,11 +34,16 @@ function generate_mock_once($name) {
Mock::generate($name, $mock_name);
}
// this has to be defined before we do any includes of library files
require_once 'HTMLPurifier/ConfigDef.php';
// define callable test files
$test_files = array();
$test_files[] = 'ConfigTest.php';
$test_files[] = 'ConfigDefTest.php';
$test_files[] = 'LexerTest.php';
$test_files[] = 'Lexer/DirectLexTest.php';
//$test_files[] = 'TokenTest.php';
$test_files[] = 'TokenTest.php';
$test_files[] = 'ChildDefTest.php';
$test_files[] = 'GeneratorTest.php';
$test_files[] = 'EntityLookupTest.php';
@ -57,28 +62,45 @@ $test_files[] = 'AttrDef/LangTest.php';
$test_files[] = 'AttrDef/PixelsTest.php';
$test_files[] = 'AttrDef/LengthTest.php';
$test_files[] = 'AttrDef/NumberSpanTest.php';
//$test_files[] = 'AttrDef/URITest.php';
$test_files[] = 'IDAccumulatorTest.php';
$test_files[] = 'TagTransformTest.php';
$test_files[] = 'AttrTransform/LangTest.php';
$test_files[] = 'AttrTransform/TextAlignTest.php';
$test_file_lookup = array_flip($test_files);
function htmlpurifier_path2class($path) {
$temp = $path;
$temp = str_replace('./', '', $temp); // remove leading './'
$temp = str_replace('.\\', '', $temp); // remove leading '.\'
$temp = str_replace('\\', '_', $temp); // normalize \ to _
$temp = str_replace('/', '_', $temp); // normalize / to _
while(strpos($temp, '__') !== false) $temp = str_replace('__', '_', $temp);
$temp = str_replace('.php', '', $temp);
return $temp;
}
// we can't use addTestFile because SimpleTest chokes on E_STRICT warnings
if (isset($_GET['file']) && isset($test_file_lookup[$_GET['file']])) {
// execute only one test
$test_file = $_GET['file'];
$test = new GroupTest('HTMLPurifier - ' . $test_file);
$test->addTestFile('HTMLPurifier/' . $test_file);
$path = 'HTMLPurifier/' . $test_file;
require_once $path;
$test->addTestClass(htmlpurifier_path2class($path));
} else {
$test = new GroupTest('HTMLPurifier');
foreach ($test_files as $test_file) {
$test->addTestFile('HTMLPurifier/' . $test_file);
$path = 'HTMLPurifier/' . $test_file;
require_once $path;
$test->addTestClass(htmlpurifier_path2class($path));
}
}
@ -88,4 +110,4 @@ else $reporter = new HTMLReporter();
$test->run($reporter);
?>
?>