1: <?php
2:
3: /**
4: * Validates the attributes of a token. Doesn't manage required attributes
5: * very well. The only reason we factored this out was because RemoveForeignElements
6: * also needed it besides ValidateAttributes.
7: */
8: class HTMLPurifier_AttrValidator
9: {
10:
11: /**
12: * Validates the attributes of a token, returning a modified token
13: * that has valid tokens
14: * @param $token Reference to token to validate. We require a reference
15: * because the operation this class performs on the token are
16: * not atomic, so the context CurrentToken to be updated
17: * throughout
18: * @param $config Instance of HTMLPurifier_Config
19: * @param $context Instance of HTMLPurifier_Context
20: */
21: public function validateToken(&$token, &$config, $context) {
22:
23: $definition = $config->getHTMLDefinition();
24: $e =& $context->get('ErrorCollector', true);
25:
26: // initialize IDAccumulator if necessary
27: $ok =& $context->get('IDAccumulator', true);
28: if (!$ok) {
29: $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
30: $context->register('IDAccumulator', $id_accumulator);
31: }
32:
33: // initialize CurrentToken if necessary
34: $current_token =& $context->get('CurrentToken', true);
35: if (!$current_token) $context->register('CurrentToken', $token);
36:
37: if (
38: !$token instanceof HTMLPurifier_Token_Start &&
39: !$token instanceof HTMLPurifier_Token_Empty
40: ) return $token;
41:
42: // create alias to global definition array, see also $defs
43: // DEFINITION CALL
44: $d_defs = $definition->info_global_attr;
45:
46: // don't update token until the very end, to ensure an atomic update
47: $attr = $token->attr;
48:
49: // do global transformations (pre)
50: // nothing currently utilizes this
51: foreach ($definition->info_attr_transform_pre as $transform) {
52: $attr = $transform->transform($o = $attr, $config, $context);
53: if ($e) {
54: if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
55: }
56: }
57:
58: // do local transformations only applicable to this element (pre)
59: // ex. <p align="right"> to <p style="text-align:right;">
60: foreach ($definition->info[$token->name]->attr_transform_pre as $transform) {
61: $attr = $transform->transform($o = $attr, $config, $context);
62: if ($e) {
63: if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
64: }
65: }
66:
67: // create alias to this element's attribute definition array, see
68: // also $d_defs (global attribute definition array)
69: // DEFINITION CALL
70: $defs = $definition->info[$token->name]->attr;
71:
72: $attr_key = false;
73: $context->register('CurrentAttr', $attr_key);
74:
75: // iterate through all the attribute keypairs
76: // Watch out for name collisions: $key has previously been used
77: foreach ($attr as $attr_key => $value) {
78:
79: // call the definition
80: if ( isset($defs[$attr_key]) ) {
81: // there is a local definition defined
82: if ($defs[$attr_key] === false) {
83: // We've explicitly been told not to allow this element.
84: // This is usually when there's a global definition
85: // that must be overridden.
86: // Theoretically speaking, we could have a
87: // AttrDef_DenyAll, but this is faster!
88: $result = false;
89: } else {
90: // validate according to the element's definition
91: $result = $defs[$attr_key]->validate(
92: $value, $config, $context
93: );
94: }
95: } elseif ( isset($d_defs[$attr_key]) ) {
96: // there is a global definition defined, validate according
97: // to the global definition
98: $result = $d_defs[$attr_key]->validate(
99: $value, $config, $context
100: );
101: } else {
102: // system never heard of the attribute? DELETE!
103: $result = false;
104: }
105:
106: // put the results into effect
107: if ($result === false || $result === null) {
108: // this is a generic error message that should replaced
109: // with more specific ones when possible
110: if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed');
111:
112: // remove the attribute
113: unset($attr[$attr_key]);
114: } elseif (is_string($result)) {
115: // generally, if a substitution is happening, there
116: // was some sort of implicit correction going on. We'll
117: // delegate it to the attribute classes to say exactly what.
118:
119: // simple substitution
120: $attr[$attr_key] = $result;
121: } else {
122: // nothing happens
123: }
124:
125: // we'd also want slightly more complicated substitution
126: // involving an array as the return value,
127: // although we're not sure how colliding attributes would
128: // resolve (certain ones would be completely overriden,
129: // others would prepend themselves).
130: }
131:
132: $context->destroy('CurrentAttr');
133:
134: // post transforms
135:
136: // global (error reporting untested)
137: foreach ($definition->info_attr_transform_post as $transform) {
138: $attr = $transform->transform($o = $attr, $config, $context);
139: if ($e) {
140: if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
141: }
142: }
143:
144: // local (error reporting untested)
145: foreach ($definition->info[$token->name]->attr_transform_post as $transform) {
146: $attr = $transform->transform($o = $attr, $config, $context);
147: if ($e) {
148: if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
149: }
150: }
151:
152: $token->attr = $attr;
153:
154: // destroy CurrentToken if we made it ourselves
155: if (!$current_token) $context->destroy('CurrentToken');
156:
157: }
158:
159:
160: }
161:
162: // vim: et sw=4 sts=4
163: