You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
8.7KB

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