| 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: |