1: | <?php |
2: | |
3: | /** |
4: | * Injects tokens into the document while parsing for well-formedness. |
5: | * This enables "formatter-like" functionality such as auto-paragraphing, |
6: | * smiley-ification and linkification to take place. |
7: | * |
8: | * A note on how handlers create changes; this is done by assigning a new |
9: | * value to the $token reference. These values can take a variety of forms and |
10: | * are best described HTMLPurifier_Strategy_MakeWellFormed->processToken() |
11: | * documentation. |
12: | * |
13: | * @todo Allow injectors to request a re-run on their output. This |
14: | * would help if an operation is recursive. |
15: | */ |
16: | abstract class HTMLPurifier_Injector |
17: | { |
18: | |
19: | /** |
20: | * Advisory name of injector, this is for friendly error messages. |
21: | * @type string |
22: | */ |
23: | public $name; |
24: | |
25: | /** |
26: | * @type HTMLPurifier_HTMLDefinition |
27: | */ |
28: | protected $htmlDefinition; |
29: | |
30: | /** |
31: | * Reference to CurrentNesting variable in Context. This is an array |
32: | * list of tokens that we are currently "inside" |
33: | * @type array |
34: | */ |
35: | protected $currentNesting; |
36: | |
37: | /** |
38: | * Reference to current token. |
39: | * @type HTMLPurifier_Token |
40: | */ |
41: | protected $currentToken; |
42: | |
43: | /** |
44: | * Reference to InputZipper variable in Context. |
45: | * @type HTMLPurifier_Zipper |
46: | */ |
47: | protected $inputZipper; |
48: | |
49: | /** |
50: | * Array of elements and attributes this injector creates and therefore |
51: | * need to be allowed by the definition. Takes form of |
52: | * array('element' => array('attr', 'attr2'), 'element2') |
53: | * @type array |
54: | */ |
55: | public $needed = array(); |
56: | |
57: | /** |
58: | * Number of elements to rewind backwards (relative). |
59: | * @type bool|int |
60: | */ |
61: | protected $rewindOffset = false; |
62: | |
63: | /** |
64: | * Rewind to a spot to re-perform processing. This is useful if you |
65: | * deleted a node, and now need to see if this change affected any |
66: | * earlier nodes. Rewinding does not affect other injectors, and can |
67: | * result in infinite loops if not used carefully. |
68: | * @param bool|int $offset |
69: | * @warning HTML Purifier will prevent you from fast-forwarding with this |
70: | * function. |
71: | */ |
72: | public function rewindOffset($offset) |
73: | { |
74: | $this->rewindOffset = $offset; |
75: | } |
76: | |
77: | /** |
78: | * Retrieves rewind offset, and then unsets it. |
79: | * @return bool|int |
80: | */ |
81: | public function getRewindOffset() |
82: | { |
83: | $r = $this->rewindOffset; |
84: | $this->rewindOffset = false; |
85: | return $r; |
86: | } |
87: | |
88: | /** |
89: | * Prepares the injector by giving it the config and context objects: |
90: | * this allows references to important variables to be made within |
91: | * the injector. This function also checks if the HTML environment |
92: | * will work with the Injector (see checkNeeded()). |
93: | * @param HTMLPurifier_Config $config |
94: | * @param HTMLPurifier_Context $context |
95: | * @return bool|string Boolean false if success, string of missing needed element/attribute if failure |
96: | */ |
97: | public function prepare($config, $context) |
98: | { |
99: | $this->htmlDefinition = $config->getHTMLDefinition(); |
100: | // Even though this might fail, some unit tests ignore this and |
101: | // still test checkNeeded, so be careful. Maybe get rid of that |
102: | // dependency. |
103: | $result = $this->checkNeeded($config); |
104: | if ($result !== false) { |
105: | return $result; |
106: | } |
107: | $this->currentNesting =& $context->get('CurrentNesting'); |
108: | $this->currentToken =& $context->get('CurrentToken'); |
109: | $this->inputZipper =& $context->get('InputZipper'); |
110: | return false; |
111: | } |
112: | |
113: | /** |
114: | * This function checks if the HTML environment |
115: | * will work with the Injector: if p tags are not allowed, the |
116: | * Auto-Paragraphing injector should not be enabled. |
117: | * @param HTMLPurifier_Config $config |
118: | * @return bool|string Boolean false if success, string of missing needed element/attribute if failure |
119: | */ |
120: | public function checkNeeded($config) |
121: | { |
122: | $def = $config->getHTMLDefinition(); |
123: | foreach ($this->needed as $element => $attributes) { |
124: | if (is_int($element)) { |
125: | $element = $attributes; |
126: | } |
127: | if (!isset($def->info[$element])) { |
128: | return $element; |
129: | } |
130: | if (!is_array($attributes)) { |
131: | continue; |
132: | } |
133: | foreach ($attributes as $name) { |
134: | if (!isset($def->info[$element]->attr[$name])) { |
135: | return "$element.$name"; |
136: | } |
137: | } |
138: | } |
139: | return false; |
140: | } |
141: | |
142: | /** |
143: | * Tests if the context node allows a certain element |
144: | * @param string $name Name of element to test for |
145: | * @return bool True if element is allowed, false if it is not |
146: | */ |
147: | public function allowsElement($name) |
148: | { |
149: | if (!empty($this->currentNesting)) { |
150: | $parent_token = array_pop($this->currentNesting); |
151: | $this->currentNesting[] = $parent_token; |
152: | $parent = $this->htmlDefinition->info[$parent_token->name]; |
153: | } else { |
154: | $parent = $this->htmlDefinition->info_parent_def; |
155: | } |
156: | if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) { |
157: | return false; |
158: | } |
159: | // check for exclusion |
160: | if (!empty($this->currentNesting)) { |
161: | for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) { |
162: | $node = $this->currentNesting[$i]; |
163: | $def = $this->htmlDefinition->info[$node->name]; |
164: | if (isset($def->excludes[$name])) { |
165: | return false; |
166: | } |
167: | } |
168: | } |
169: | return true; |
170: | } |
171: | |
172: | /** |
173: | * Iterator function, which starts with the next token and continues until |
174: | * you reach the end of the input tokens. |
175: | * @warning Please prevent previous references from interfering with this |
176: | * functions by setting $i = null beforehand! |
177: | * @param int $i Current integer index variable for inputTokens |
178: | * @param HTMLPurifier_Token $current Current token variable. |
179: | * Do NOT use $token, as that variable is also a reference |
180: | * @return bool |
181: | */ |
182: | protected function forward(&$i, &$current) |
183: | { |
184: | if ($i === null) { |
185: | $i = count($this->inputZipper->back) - 1; |
186: | } else { |
187: | $i--; |
188: | } |
189: | if ($i < 0) { |
190: | return false; |
191: | } |
192: | $current = $this->inputZipper->back[$i]; |
193: | return true; |
194: | } |
195: | |
196: | /** |
197: | * Similar to _forward, but accepts a third parameter $nesting (which |
198: | * should be initialized at 0) and stops when we hit the end tag |
199: | * for the node $this->inputIndex starts in. |
200: | * @param int $i Current integer index variable for inputTokens |
201: | * @param HTMLPurifier_Token $current Current token variable. |
202: | * Do NOT use $token, as that variable is also a reference |
203: | * @param int $nesting |
204: | * @return bool |
205: | */ |
206: | protected function forwardUntilEndToken(&$i, &$current, &$nesting) |
207: | { |
208: | $result = $this->forward($i, $current); |
209: | if (!$result) { |
210: | return false; |
211: | } |
212: | if ($nesting === null) { |
213: | $nesting = 0; |
214: | } |
215: | if ($current instanceof HTMLPurifier_Token_Start) { |
216: | $nesting++; |
217: | } elseif ($current instanceof HTMLPurifier_Token_End) { |
218: | if ($nesting <= 0) { |
219: | return false; |
220: | } |
221: | $nesting--; |
222: | } |
223: | return true; |
224: | } |
225: | |
226: | /** |
227: | * Iterator function, starts with the previous token and continues until |
228: | * you reach the beginning of input tokens. |
229: | * @warning Please prevent previous references from interfering with this |
230: | * functions by setting $i = null beforehand! |
231: | * @param int $i Current integer index variable for inputTokens |
232: | * @param HTMLPurifier_Token $current Current token variable. |
233: | * Do NOT use $token, as that variable is also a reference |
234: | * @return bool |
235: | */ |
236: | protected function backward(&$i, &$current) |
237: | { |
238: | if ($i === null) { |
239: | $i = count($this->inputZipper->front) - 1; |
240: | } else { |
241: | $i--; |
242: | } |
243: | if ($i < 0) { |
244: | return false; |
245: | } |
246: | $current = $this->inputZipper->front[$i]; |
247: | return true; |
248: | } |
249: | |
250: | /** |
251: | * Handler that is called when a text token is processed |
252: | */ |
253: | public function handleText(&$token) |
254: | { |
255: | } |
256: | |
257: | /** |
258: | * Handler that is called when a start or empty token is processed |
259: | */ |
260: | public function handleElement(&$token) |
261: | { |
262: | } |
263: | |
264: | /** |
265: | * Handler that is called when an end token is processed |
266: | */ |
267: | public function handleEnd(&$token) |
268: | { |
269: | $this->notifyEnd($token); |
270: | } |
271: | |
272: | /** |
273: | * Notifier that is called when an end token is processed |
274: | * @param HTMLPurifier_Token $token Current token variable. |
275: | * @note This differs from handlers in that the token is read-only |
276: | * @deprecated |
277: | */ |
278: | public function notifyEnd($token) |
279: | { |
280: | } |
281: | } |
282: | |
283: | // vim: et sw=4 sts=4 |
284: |