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