diff --git a/library/HTMLPurifier/AttrDef/Composite.php b/library/HTMLPurifier/AttrDef/Composite.php
new file mode 100644
index 00000000..bf18b31c
--- /dev/null
+++ b/library/HTMLPurifier/AttrDef/Composite.php
@@ -0,0 +1,22 @@
+defs =& $defs;
+ }
+
+ function validate($string, $config, &$context) {
+ foreach ($this->defs as $i => $def) {
+ $result = $this->defs[$i]->validate($string, $config, $context);
+ if ($result !== false) return $result;
+ }
+ return false;
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/tests/HTMLPurifier/AttrDef/CompositeTest.php b/tests/HTMLPurifier/AttrDef/CompositeTest.php
new file mode 100644
index 00000000..bd81db60
--- /dev/null
+++ b/tests/HTMLPurifier/AttrDef/CompositeTest.php
@@ -0,0 +1,81 @@
+expectOnce('validate', $def1_params);
+ $def1->setReturnValue('validate', $output, $def1_params);
+ $def2->expectNever('validate');
+
+ $this->assertIdentical($output,
+ $def->validate($input, $config, $context));
+
+ $def1->tally();
+ $def2->tally();
+
+ // second test, first def fails, second def works
+
+ $def1 =& new HTMLPurifier_AttrDefMock($this);
+ $def2 =& new HTMLPurifier_AttrDefMock($this);
+ $defs = array(&$def1, &$def2);
+ $def =& new HTMLPurifier_AttrDef_Composite($defs);
+ $input = 'BOOMA';
+ $output = 'booma';
+ $def_params = array($input, $config, $context);
+ $def1->expectOnce('validate', $def_params);
+ $def1->setReturnValue('validate', false, $def_params);
+ $def2->expectOnce('validate', $def_params);
+ $def2->setReturnValue('validate', $output, $def_params);
+
+ $this->assertIdentical($output,
+ $def->validate($input, $config, $context));
+
+ $def1->tally();
+ $def2->tally();
+
+ // third test, all fail, so composite faiils
+
+ $def1 =& new HTMLPurifier_AttrDefMock($this);
+ $def2 =& new HTMLPurifier_AttrDefMock($this);
+ $defs = array(&$def1, &$def2);
+ $def =& new HTMLPurifier_AttrDef_Composite($defs);
+ $input = 'BOOMA';
+ $output = false;
+ $def_params = array($input, $config, $context);
+ $def1->expectOnce('validate', $def_params);
+ $def1->setReturnValue('validate', false, $def_params);
+ $def2->expectOnce('validate', $def_params);
+ $def2->setReturnValue('validate', false, $def_params);
+
+ $this->assertIdentical($output,
+ $def->validate($input, $config, $context));
+
+ $def1->tally();
+ $def2->tally();
+
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/tests/index.php b/tests/index.php
index dfc1a0f7..cd9d7c5d 100644
--- a/tests/index.php
+++ b/tests/index.php
@@ -64,6 +64,7 @@ $test_files[] = 'AttrDef/LengthTest.php';
$test_files[] = 'AttrDef/NumberSpanTest.php';
$test_files[] = 'AttrDef/URITest.php';
$test_files[] = 'AttrDef/CSSTest.php';
+$test_files[] = 'AttrDef/CompositeTest.php';
$test_files[] = 'IDAccumulatorTest.php';
$test_files[] = 'TagTransformTest.php';
$test_files[] = 'AttrTransform/LangTest.php';