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: */
22: public $name;
23:
24: /**
25: * Instance of HTMLPurifier_HTMLDefinition
26: */
27: protected $htmlDefinition;
28:
29: /**
30: * Reference to CurrentNesting variable in Context. This is an array
31: * list of tokens that we are currently "inside"
32: */
33: protected $currentNesting;
34:
35: /**
36: * Reference to InputTokens variable in Context. This is an array
37: * list of the input tokens that are being processed.
38: */
39: protected $inputTokens;
40:
41: /**
42: * Reference to InputIndex variable in Context. This is an integer
43: * array index for $this->inputTokens that indicates what token
44: * is currently being processed.
45: */
46: protected $inputIndex;
47:
48: /**
49: * Array of elements and attributes this injector creates and therefore
50: * need to be allowed by the definition. Takes form of
51: * array('element' => array('attr', 'attr2'), 'element2')
52: */
53: public $needed = array();
54:
55: /**
56: * Index of inputTokens to rewind to.
57: */
58: protected $rewind = false;
59:
60: /**
61: * Rewind to a spot to re-perform processing. This is useful if you
62: * deleted a node, and now need to see if this change affected any
63: * earlier nodes. Rewinding does not affect other injectors, and can
64: * result in infinite loops if not used carefully.
65: * @warning HTML Purifier will prevent you from fast-forwarding with this
66: * function.
67: */
68: public function rewind($index) {
69: $this->rewind = $index;
70: }
71:
72: /**
73: * Retrieves rewind, and then unsets it.
74: */
75: public function getRewind() {
76: $r = $this->rewind;
77: $this->rewind = false;
78: return $r;
79: }
80:
81: /**
82: * Prepares the injector by giving it the config and context objects:
83: * this allows references to important variables to be made within
84: * the injector. This function also checks if the HTML environment
85: * will work with the Injector (see checkNeeded()).
86: * @param $config Instance of HTMLPurifier_Config
87: * @param $context Instance of HTMLPurifier_Context
88: * @return Boolean false if success, string of missing needed element/attribute if failure
89: */
90: public function prepare($config, $context) {
91: $this->htmlDefinition = $config->getHTMLDefinition();
92: // Even though this might fail, some unit tests ignore this and
93: // still test checkNeeded, so be careful. Maybe get rid of that
94: // dependency.
95: $result = $this->checkNeeded($config);
96: if ($result !== false) return $result;
97: $this->currentNesting =& $context->get('CurrentNesting');
98: $this->inputTokens =& $context->get('InputTokens');
99: $this->inputIndex =& $context->get('InputIndex');
100: return false;
101: }
102:
103: /**
104: * This function checks if the HTML environment
105: * will work with the Injector: if p tags are not allowed, the
106: * Auto-Paragraphing injector should not be enabled.
107: * @param $config Instance of HTMLPurifier_Config
108: * @param $context Instance of HTMLPurifier_Context
109: * @return Boolean false if success, string of missing needed element/attribute if failure
110: */
111: public function checkNeeded($config) {
112: $def = $config->getHTMLDefinition();
113: foreach ($this->needed as $element => $attributes) {
114: if (is_int($element)) $element = $attributes;
115: if (!isset($def->info[$element])) return $element;
116: if (!is_array($attributes)) continue;
117: foreach ($attributes as $name) {
118: if (!isset($def->info[$element]->attr[$name])) return "$element.$name";
119: }
120: }
121: return false;
122: }
123:
124: /**
125: * Tests if the context node allows a certain element
126: * @param $name Name of element to test for
127: * @return True if element is allowed, false if it is not
128: */
129: public function allowsElement($name) {
130: if (!empty($this->currentNesting)) {
131: $parent_token = array_pop($this->currentNesting);
132: $this->currentNesting[] = $parent_token;
133: $parent = $this->htmlDefinition->info[$parent_token->name];
134: } else {
135: $parent = $this->htmlDefinition->info_parent_def;
136: }
137: if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) {
138: return false;
139: }
140: // check for exclusion
141: for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) {
142: $node = $this->currentNesting[$i];
143: $def = $this->htmlDefinition->info[$node->name];
144: if (isset($def->excludes[$name])) return false;
145: }
146: return true;
147: }
148:
149: /**
150: * Iterator function, which starts with the next token and continues until
151: * you reach the end of the input tokens.
152: * @warning Please prevent previous references from interfering with this
153: * functions by setting $i = null beforehand!
154: * @param &$i Current integer index variable for inputTokens
155: * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference
156: */
157: protected function forward(&$i, &$current) {
158: if ($i === null) $i = $this->inputIndex + 1;
159: else $i++;
160: if (!isset($this->inputTokens[$i])) return false;
161: $current = $this->inputTokens[$i];
162: return true;
163: }
164:
165: /**
166: * Similar to _forward, but accepts a third parameter $nesting (which
167: * should be initialized at 0) and stops when we hit the end tag
168: * for the node $this->inputIndex starts in.
169: */
170: protected function forwardUntilEndToken(&$i, &$current, &$nesting) {
171: $result = $this->forward($i, $current);
172: if (!$result) return false;
173: if ($nesting === null) $nesting = 0;
174: if ($current instanceof HTMLPurifier_Token_Start) $nesting++;
175: elseif ($current instanceof HTMLPurifier_Token_End) {
176: if ($nesting <= 0) return false;
177: $nesting--;
178: }
179: return true;
180: }
181:
182: /**
183: * Iterator function, starts with the previous token and continues until
184: * you reach the beginning of input tokens.
185: * @warning Please prevent previous references from interfering with this
186: * functions by setting $i = null beforehand!
187: * @param &$i Current integer index variable for inputTokens
188: * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference
189: */
190: protected function backward(&$i, &$current) {
191: if ($i === null) $i = $this->inputIndex - 1;
192: else $i--;
193: if ($i < 0) return false;
194: $current = $this->inputTokens[$i];
195: return true;
196: }
197:
198: /**
199: * Initializes the iterator at the current position. Use in a do {} while;
200: * loop to force the _forward and _backward functions to start at the
201: * current location.
202: * @warning Please prevent previous references from interfering with this
203: * functions by setting $i = null beforehand!
204: * @param &$i Current integer index variable for inputTokens
205: * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference
206: */
207: protected function current(&$i, &$current) {
208: if ($i === null) $i = $this->inputIndex;
209: $current = $this->inputTokens[$i];
210: }
211:
212: /**
213: * Handler that is called when a text token is processed
214: */
215: public function handleText(&$token) {}
216:
217: /**
218: * Handler that is called when a start or empty token is processed
219: */
220: public function handleElement(&$token) {}
221:
222: /**
223: * Handler that is called when an end token is processed
224: */
225: public function handleEnd(&$token) {
226: $this->notifyEnd($token);
227: }
228:
229: /**
230: * Notifier that is called when an end token is processed
231: * @note This differs from handlers in that the token is read-only
232: * @deprecated
233: */
234: public function notifyEnd($token) {}
235:
236:
237: }
238:
239: // vim: et sw=4 sts=4
240: