| Conditions | 87 |
| Paths | > 20000 |
| Total Lines | 449 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
| 1 | <?php |
||
| 66 | public function execute($tokens, $config, $context) |
||
| 67 | { |
||
| 68 | $definition = $config->getHTMLDefinition(); |
||
| 69 | |||
| 70 | // local variables |
||
| 71 | $generator = new HTMLPurifier_Generator($config, $context); |
||
| 72 | $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); |
||
| 73 | // used for autoclose early abortion |
||
| 74 | $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config); |
||
| 75 | $e = $context->get('ErrorCollector', true); |
||
| 76 | $i = false; // injector index |
||
| 77 | list($zipper, $token) = HTMLPurifier_Zipper::fromArray($tokens); |
||
| 78 | if ($token === NULL) { |
||
| 79 | return array(); |
||
| 80 | } |
||
| 81 | $reprocess = false; // whether or not to reprocess the same token |
||
| 82 | $stack = array(); |
||
| 83 | |||
| 84 | // member variables |
||
| 85 | $this->stack =& $stack; |
||
| 86 | $this->tokens =& $tokens; |
||
| 87 | $this->token =& $token; |
||
| 88 | $this->zipper =& $zipper; |
||
| 89 | $this->config = $config; |
||
| 90 | $this->context = $context; |
||
| 91 | |||
| 92 | // context variables |
||
| 93 | $context->register('CurrentNesting', $stack); |
||
| 94 | $context->register('InputZipper', $zipper); |
||
| 95 | $context->register('CurrentToken', $token); |
||
| 96 | |||
| 97 | // -- begin INJECTOR -- |
||
| 98 | |||
| 99 | $this->injectors = array(); |
||
| 100 | |||
| 101 | $injectors = $config->getBatch('AutoFormat'); |
||
| 102 | $def_injectors = $definition->info_injector; |
||
| 103 | $custom_injectors = $injectors['Custom']; |
||
| 104 | unset($injectors['Custom']); // special case |
||
| 105 | foreach ($injectors as $injector => $b) { |
||
| 106 | // XXX: Fix with a legitimate lookup table of enabled filters |
||
| 107 | if (strpos($injector, '.') !== false) { |
||
| 108 | continue; |
||
| 109 | } |
||
| 110 | $injector = "HTMLPurifier_Injector_$injector"; |
||
| 111 | if (!$b) { |
||
| 112 | continue; |
||
| 113 | } |
||
| 114 | $this->injectors[] = new $injector; |
||
| 115 | } |
||
| 116 | foreach ($def_injectors as $injector) { |
||
| 117 | // assumed to be objects |
||
| 118 | $this->injectors[] = $injector; |
||
| 119 | } |
||
| 120 | foreach ($custom_injectors as $injector) { |
||
| 121 | if (!$injector) { |
||
| 122 | continue; |
||
| 123 | } |
||
| 124 | if (is_string($injector)) { |
||
| 125 | $injector = "HTMLPurifier_Injector_$injector"; |
||
| 126 | $injector = new $injector; |
||
| 127 | } |
||
| 128 | $this->injectors[] = $injector; |
||
| 129 | } |
||
| 130 | |||
| 131 | // give the injectors references to the definition and context |
||
| 132 | // variables for performance reasons |
||
| 133 | foreach ($this->injectors as $ix => $injector) { |
||
| 134 | $error = $injector->prepare($config, $context); |
||
| 135 | if (!$error) { |
||
| 136 | continue; |
||
| 137 | } |
||
| 138 | array_splice($this->injectors, $ix, 1); // rm the injector |
||
| 139 | trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); |
||
| 140 | } |
||
| 141 | |||
| 142 | // -- end INJECTOR -- |
||
| 143 | |||
| 144 | // a note on reprocessing: |
||
| 145 | // In order to reduce code duplication, whenever some code needs |
||
| 146 | // to make HTML changes in order to make things "correct", the |
||
| 147 | // new HTML gets sent through the purifier, regardless of its |
||
| 148 | // status. This means that if we add a start token, because it |
||
| 149 | // was totally necessary, we don't have to update nesting; we just |
||
| 150 | // punt ($reprocess = true; continue;) and it does that for us. |
||
| 151 | |||
| 152 | // isset is in loop because $tokens size changes during loop exec |
||
| 153 | for (;; |
||
| 154 | // only increment if we don't need to reprocess |
||
| 155 | $reprocess ? $reprocess = false : $token = $zipper->next($token)) { |
||
| 156 | |||
| 157 | // check for a rewind |
||
| 158 | if (is_int($i)) { |
||
| 159 | // possibility: disable rewinding if the current token has a |
||
| 160 | // rewind set on it already. This would offer protection from |
||
| 161 | // infinite loop, but might hinder some advanced rewinding. |
||
| 162 | $rewind_offset = $this->injectors[$i]->getRewindOffset(); |
||
| 163 | if (is_int($rewind_offset)) { |
||
| 164 | for ($j = 0; $j < $rewind_offset; $j++) { |
||
| 165 | if (empty($zipper->front)) break; |
||
| 166 | $token = $zipper->prev($token); |
||
| 167 | // indicate that other injectors should not process this token, |
||
| 168 | // but we need to reprocess it. See Note [Injector skips] |
||
| 169 | unset($token->skip[$i]); |
||
| 170 | $token->rewind = $i; |
||
| 171 | if ($token instanceof HTMLPurifier_Token_Start) { |
||
| 172 | array_pop($this->stack); |
||
| 173 | } elseif ($token instanceof HTMLPurifier_Token_End) { |
||
| 174 | $this->stack[] = $token->start; |
||
| 175 | } |
||
| 176 | } |
||
| 177 | } |
||
| 178 | $i = false; |
||
| 179 | } |
||
| 180 | |||
| 181 | // handle case of document end |
||
| 182 | if ($token === NULL) { |
||
| 183 | // kill processing if stack is empty |
||
| 184 | if (empty($this->stack)) { |
||
| 185 | break; |
||
| 186 | } |
||
| 187 | |||
| 188 | // peek |
||
| 189 | $top_nesting = array_pop($this->stack); |
||
| 190 | $this->stack[] = $top_nesting; |
||
| 191 | |||
| 192 | // send error [TagClosedSuppress] |
||
| 193 | if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) { |
||
| 194 | $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting); |
||
| 195 | } |
||
| 196 | |||
| 197 | // append, don't splice, since this is the end |
||
| 198 | $token = new HTMLPurifier_Token_End($top_nesting->name); |
||
| 199 | |||
| 200 | // punt! |
||
| 201 | $reprocess = true; |
||
| 202 | continue; |
||
| 203 | } |
||
| 204 | |||
| 205 | //echo '<br>'; printZipper($zipper, $token);//printTokens($this->stack); |
||
| 206 | //flush(); |
||
| 207 | |||
| 208 | // quick-check: if it's not a tag, no need to process |
||
| 209 | if (empty($token->is_tag)) { |
||
| 210 | if ($token instanceof HTMLPurifier_Token_Text) { |
||
| 211 | foreach ($this->injectors as $i => $injector) { |
||
| 212 | if (isset($token->skip[$i])) { |
||
| 213 | // See Note [Injector skips] |
||
| 214 | continue; |
||
| 215 | } |
||
| 216 | if ($token->rewind !== null && $token->rewind !== $i) { |
||
| 217 | continue; |
||
| 218 | } |
||
| 219 | // XXX fuckup |
||
| 220 | $r = $token; |
||
| 221 | $injector->handleText($r); |
||
| 222 | $token = $this->processToken($r, $i); |
||
| 223 | $reprocess = true; |
||
| 224 | break; |
||
| 225 | } |
||
| 226 | } |
||
| 227 | // another possibility is a comment |
||
| 228 | continue; |
||
| 229 | } |
||
| 230 | |||
| 231 | if (isset($definition->info[$token->name])) { |
||
| 232 | $type = $definition->info[$token->name]->child->type; |
||
| 233 | } else { |
||
| 234 | $type = false; // Type is unknown, treat accordingly |
||
| 235 | } |
||
| 236 | |||
| 237 | // quick tag checks: anything that's *not* an end tag |
||
| 238 | $ok = false; |
||
| 239 | if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { |
||
| 240 | // claims to be a start tag but is empty |
||
| 241 | $token = new HTMLPurifier_Token_Empty( |
||
| 242 | $token->name, |
||
| 243 | $token->attr, |
||
| 244 | $token->line, |
||
| 245 | $token->col, |
||
| 246 | $token->armor |
||
| 247 | ); |
||
| 248 | $ok = true; |
||
| 249 | } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { |
||
| 250 | // claims to be empty but really is a start tag |
||
| 251 | // NB: this assignment is required |
||
| 252 | $old_token = $token; |
||
| 253 | $token = new HTMLPurifier_Token_End($token->name); |
||
| 254 | $token = $this->insertBefore( |
||
| 255 | new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor) |
||
| 256 | ); |
||
| 257 | // punt (since we had to modify the input stream in a non-trivial way) |
||
| 258 | $reprocess = true; |
||
| 259 | continue; |
||
| 260 | } elseif ($token instanceof HTMLPurifier_Token_Empty) { |
||
| 261 | // real empty token |
||
| 262 | $ok = true; |
||
| 263 | } elseif ($token instanceof HTMLPurifier_Token_Start) { |
||
| 264 | // start tag |
||
| 265 | |||
| 266 | // ...unless they also have to close their parent |
||
| 267 | if (!empty($this->stack)) { |
||
| 268 | |||
| 269 | // Performance note: you might think that it's rather |
||
| 270 | // inefficient, recalculating the autoclose information |
||
| 271 | // for every tag that a token closes (since when we |
||
| 272 | // do an autoclose, we push a new token into the |
||
| 273 | // stream and then /process/ that, before |
||
| 274 | // re-processing this token.) But this is |
||
| 275 | // necessary, because an injector can make an |
||
| 276 | // arbitrary transformations to the autoclosing |
||
| 277 | // tokens we introduce, so things may have changed |
||
| 278 | // in the meantime. Also, doing the inefficient thing is |
||
| 279 | // "easy" to reason about (for certain perverse definitions |
||
| 280 | // of "easy") |
||
| 281 | |||
| 282 | $parent = array_pop($this->stack); |
||
| 283 | $this->stack[] = $parent; |
||
| 284 | |||
| 285 | $parent_def = null; |
||
| 286 | $parent_elements = null; |
||
| 287 | $autoclose = false; |
||
| 288 | if (isset($definition->info[$parent->name])) { |
||
| 289 | $parent_def = $definition->info[$parent->name]; |
||
| 290 | $parent_elements = $parent_def->child->getAllowedElements($config); |
||
| 291 | $autoclose = !isset($parent_elements[$token->name]); |
||
| 292 | } |
||
| 293 | |||
| 294 | if ($autoclose && $definition->info[$token->name]->wrap) { |
||
| 295 | // Check if an element can be wrapped by another |
||
| 296 | // element to make it valid in a context (for |
||
| 297 | // example, <ul><ul> needs a <li> in between) |
||
| 298 | $wrapname = $definition->info[$token->name]->wrap; |
||
| 299 | $wrapdef = $definition->info[$wrapname]; |
||
| 300 | $elements = $wrapdef->child->getAllowedElements($config); |
||
| 301 | if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) { |
||
| 302 | $newtoken = new HTMLPurifier_Token_Start($wrapname); |
||
| 303 | $token = $this->insertBefore($newtoken); |
||
| 304 | $reprocess = true; |
||
| 305 | continue; |
||
| 306 | } |
||
| 307 | } |
||
| 308 | |||
| 309 | $carryover = false; |
||
| 310 | if ($autoclose && $parent_def->formatting) { |
||
| 311 | $carryover = true; |
||
| 312 | } |
||
| 313 | |||
| 314 | if ($autoclose) { |
||
| 315 | // check if this autoclose is doomed to fail |
||
| 316 | // (this rechecks $parent, which his harmless) |
||
| 317 | $autoclose_ok = isset($global_parent_allowed_elements[$token->name]); |
||
| 318 | if (!$autoclose_ok) { |
||
| 319 | foreach ($this->stack as $ancestor) { |
||
| 320 | $elements = $definition->info[$ancestor->name]->child->getAllowedElements($config); |
||
| 321 | if (isset($elements[$token->name])) { |
||
| 322 | $autoclose_ok = true; |
||
| 323 | break; |
||
| 324 | } |
||
| 325 | if ($definition->info[$token->name]->wrap) { |
||
| 326 | $wrapname = $definition->info[$token->name]->wrap; |
||
| 327 | $wrapdef = $definition->info[$wrapname]; |
||
| 328 | $wrap_elements = $wrapdef->child->getAllowedElements($config); |
||
| 329 | if (isset($wrap_elements[$token->name]) && isset($elements[$wrapname])) { |
||
| 330 | $autoclose_ok = true; |
||
| 331 | break; |
||
| 332 | } |
||
| 333 | } |
||
| 334 | } |
||
| 335 | } |
||
| 336 | if ($autoclose_ok) { |
||
| 337 | // errors need to be updated |
||
| 338 | $new_token = new HTMLPurifier_Token_End($parent->name); |
||
| 339 | $new_token->start = $parent; |
||
| 340 | // [TagClosedSuppress] |
||
| 341 | if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { |
||
| 342 | if (!$carryover) { |
||
| 343 | $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); |
||
| 344 | } else { |
||
| 345 | $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); |
||
| 346 | } |
||
| 347 | } |
||
| 348 | if ($carryover) { |
||
| 349 | $element = clone $parent; |
||
| 350 | // [TagClosedAuto] |
||
| 351 | $element->armor['MakeWellFormed_TagClosedError'] = true; |
||
| 352 | $element->carryover = true; |
||
| 353 | $token = $this->processToken(array($new_token, $token, $element)); |
||
| 354 | } else { |
||
| 355 | $token = $this->insertBefore($new_token); |
||
| 356 | } |
||
| 357 | } else { |
||
| 358 | $token = $this->remove(); |
||
| 359 | } |
||
| 360 | $reprocess = true; |
||
| 361 | continue; |
||
| 362 | } |
||
| 363 | |||
| 364 | } |
||
| 365 | $ok = true; |
||
| 366 | } |
||
| 367 | |||
| 368 | if ($ok) { |
||
| 369 | foreach ($this->injectors as $i => $injector) { |
||
| 370 | if (isset($token->skip[$i])) { |
||
| 371 | // See Note [Injector skips] |
||
| 372 | continue; |
||
| 373 | } |
||
| 374 | if ($token->rewind !== null && $token->rewind !== $i) { |
||
| 375 | continue; |
||
| 376 | } |
||
| 377 | $r = $token; |
||
| 378 | $injector->handleElement($r); |
||
| 379 | $token = $this->processToken($r, $i); |
||
| 380 | $reprocess = true; |
||
| 381 | break; |
||
| 382 | } |
||
| 383 | if (!$reprocess) { |
||
| 384 | // ah, nothing interesting happened; do normal processing |
||
| 385 | if ($token instanceof HTMLPurifier_Token_Start) { |
||
| 386 | $this->stack[] = $token; |
||
| 387 | } elseif ($token instanceof HTMLPurifier_Token_End) { |
||
| 388 | throw new HTMLPurifier_Exception( |
||
| 389 | 'Improper handling of end tag in start code; possible error in MakeWellFormed' |
||
| 390 | ); |
||
| 391 | } |
||
| 392 | } |
||
| 393 | continue; |
||
| 394 | } |
||
| 395 | |||
| 396 | // sanity check: we should be dealing with a closing tag |
||
| 397 | if (!$token instanceof HTMLPurifier_Token_End) { |
||
| 398 | throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier'); |
||
| 399 | } |
||
| 400 | |||
| 401 | // make sure that we have something open |
||
| 402 | if (empty($this->stack)) { |
||
| 403 | if ($escape_invalid_tags) { |
||
| 404 | if ($e) { |
||
| 405 | $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); |
||
| 406 | } |
||
| 407 | $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); |
||
| 408 | } else { |
||
| 409 | if ($e) { |
||
| 410 | $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); |
||
| 411 | } |
||
| 412 | $token = $this->remove(); |
||
| 413 | } |
||
| 414 | $reprocess = true; |
||
| 415 | continue; |
||
| 416 | } |
||
| 417 | |||
| 418 | // first, check for the simplest case: everything closes neatly. |
||
| 419 | // Eventually, everything passes through here; if there are problems |
||
| 420 | // we modify the input stream accordingly and then punt, so that |
||
| 421 | // the tokens get processed again. |
||
| 422 | $current_parent = array_pop($this->stack); |
||
| 423 | if ($current_parent->name == $token->name) { |
||
| 424 | $token->start = $current_parent; |
||
| 425 | foreach ($this->injectors as $i => $injector) { |
||
| 426 | if (isset($token->skip[$i])) { |
||
| 427 | // See Note [Injector skips] |
||
| 428 | continue; |
||
| 429 | } |
||
| 430 | if ($token->rewind !== null && $token->rewind !== $i) { |
||
| 431 | continue; |
||
| 432 | } |
||
| 433 | $r = $token; |
||
| 434 | $injector->handleEnd($r); |
||
| 435 | $token = $this->processToken($r, $i); |
||
| 436 | $this->stack[] = $current_parent; |
||
| 437 | $reprocess = true; |
||
| 438 | break; |
||
| 439 | } |
||
| 440 | continue; |
||
| 441 | } |
||
| 442 | |||
| 443 | // okay, so we're trying to close the wrong tag |
||
| 444 | |||
| 445 | // undo the pop previous pop |
||
| 446 | $this->stack[] = $current_parent; |
||
| 447 | |||
| 448 | // scroll back the entire nest, trying to find our tag. |
||
| 449 | // (feature could be to specify how far you'd like to go) |
||
| 450 | $size = count($this->stack); |
||
| 451 | // -2 because -1 is the last element, but we already checked that |
||
| 452 | $skipped_tags = false; |
||
| 453 | for ($j = $size - 2; $j >= 0; $j--) { |
||
| 454 | if ($this->stack[$j]->name == $token->name) { |
||
| 455 | $skipped_tags = array_slice($this->stack, $j); |
||
| 456 | break; |
||
| 457 | } |
||
| 458 | } |
||
| 459 | |||
| 460 | // we didn't find the tag, so remove |
||
| 461 | if ($skipped_tags === false) { |
||
| 462 | if ($escape_invalid_tags) { |
||
| 463 | if ($e) { |
||
| 464 | $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); |
||
| 465 | } |
||
| 466 | $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); |
||
| 467 | } else { |
||
| 468 | if ($e) { |
||
| 469 | $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); |
||
| 470 | } |
||
| 471 | $token = $this->remove(); |
||
| 472 | } |
||
| 473 | $reprocess = true; |
||
| 474 | continue; |
||
| 475 | } |
||
| 476 | |||
| 477 | // do errors, in REVERSE $j order: a,b,c with </a></b></c> |
||
| 478 | $c = count($skipped_tags); |
||
| 479 | if ($e) { |
||
| 480 | for ($j = $c - 1; $j > 0; $j--) { |
||
| 481 | // notice we exclude $j == 0, i.e. the current ending tag, from |
||
| 482 | // the errors... [TagClosedSuppress] |
||
| 483 | if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) { |
||
| 484 | $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]); |
||
| 485 | } |
||
| 486 | } |
||
| 487 | } |
||
| 488 | |||
| 489 | // insert tags, in FORWARD $j order: c,b,a with </a></b></c> |
||
| 490 | $replace = array($token); |
||
| 491 | for ($j = 1; $j < $c; $j++) { |
||
| 492 | // ...as well as from the insertions |
||
| 493 | $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name); |
||
| 494 | $new_token->start = $skipped_tags[$j]; |
||
| 495 | array_unshift($replace, $new_token); |
||
| 496 | if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) { |
||
| 497 | // [TagClosedAuto] |
||
| 498 | $element = clone $skipped_tags[$j]; |
||
| 499 | $element->carryover = true; |
||
| 500 | $element->armor['MakeWellFormed_TagClosedError'] = true; |
||
| 501 | $replace[] = $element; |
||
| 502 | } |
||
| 503 | } |
||
| 504 | $token = $this->processToken($replace); |
||
| 505 | $reprocess = true; |
||
| 506 | continue; |
||
| 507 | } |
||
| 508 | |||
| 509 | $context->destroy('CurrentToken'); |
||
| 510 | $context->destroy('CurrentNesting'); |
||
| 511 | $context->destroy('InputZipper'); |
||
| 512 | |||
| 513 | unset($this->injectors, $this->stack, $this->tokens); |
||
| 514 | return $zipper->toArray($token); |
||
| 515 | } |
||
| 660 |