PHPIDS
Current file: /home/mario/workspace/php-ids.org/trunk/lib/IDS/vendors/htmlpurifier/HTMLPurifier/Strategy/MakeWellFormed.php
Legend: executed not executed dead code

  Coverage
  Classes Methods Lines
Total
100.00 %100.00%
100.00% 1 / 1
100.00 %100.00%
100.00% 2 / 2
91.21 %91.21%
91.21% 83 / 91
 
HTMLPurifier_Strategy_MakeWellFormed
100.00 %100.00%
100.00% 1 / 1
100.00 %100.00%
100.00% 2 / 2
91.21 %91.21%
91.21% 83 / 91
 public function execute($tokens, $config, $context)
100.00 %100.00%
100.00% 1 / 1
100.00 %100.00%
100.00% 72 / 72
 public function processToken($token, $config, $context)
100.00 %100.00%
100.00% 1 / 1
55.56 %55.56%
55.56% 10 / 18


       1                 : <?php                                                                                                                    
       2                 :                                                                                                                          
       3                 : /**                                                                                                                      
       4                 :  * Takes tokens makes them well-formed (balance end tags, etc.)                                                          
       5                 :  */                                                                                                                      
       6               1 : class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy                                                 
       7                 : {                                                                                                                        
       8                 :                                                                                                                          
       9                 :     /**                                                                                                                  
      10                 :      * Locally shared variable references                                                                                
      11                 :      */                                                                                                                  
      12                 :     protected $inputTokens, $inputIndex, $outputTokens, $currentNesting,                                                 
      13                 :         $currentInjector, $injectors;                                                                                    
      14                 :                                                                                                                          
      15                 :     public function execute($tokens, $config, $context) {                                                                
      16                 :                                                                                                                          
      17               2 :         $definition = $config->getHTMLDefinition();                                                                      
      18                 :                                                                                                                          
      19                 :         // local variables                                                                                               
      20               2 :         $result = array();                                                                                               
      21               2 :         $generator = new HTMLPurifier_Generator($config, $context);                                                      
      22               2 :         $escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags');                                                
      23               2 :         $e = $context->get('ErrorCollector', true);                                                                      
      24                 :                                                                                                                          
      25                 :         // member variables                                                                                              
      26               2 :         $this->currentNesting = array();                                                                                 
      27               2 :         $this->inputIndex     = false;                                                                                   
      28               2 :         $this->inputTokens    =& $tokens;                                                                                
      29               2 :         $this->outputTokens   =& $result;                                                                                
      30                 :                                                                                                                          
      31                 :         // context variables                                                                                             
      32               2 :         $context->register('CurrentNesting', $this->currentNesting);                                                     
      33               2 :         $context->register('InputIndex',     $this->inputIndex);                                                         
      34               2 :         $context->register('InputTokens',    $tokens);                                                                   
      35                 :                                                                                                                          
      36                 :         // -- begin INJECTOR --                                                                                          
      37                 :                                                                                                                          
      38               2 :         $this->injectors = array();                                                                                      
      39                 :                                                                                                                          
      40               2 :         $injectors = $config->getBatch('AutoFormat');                                                                    
      41               2 :         $def_injectors = $definition->info_injector;                                                                     
      42               2 :         $custom_injectors = $injectors['Custom'];                                                                        
      43               2 :         unset($injectors['Custom']); // special case                                                                     
      44               2 :         foreach ($injectors as $injector => $b) {                                                                        
      45               2 :             $injector = "HTMLPurifier_Injector_$injector";                                                               
      46               2 :             if (!$b) continue;                                                                                           
      47                 :             $this->injectors[] = new $injector;                                                                          
      48                 :         }                                                                                                                
      49               2 :         foreach ($def_injectors as $injector) {                                                                          
      50                 :             // assumed to be objects                                                                                     
      51                 :             $this->injectors[] = $injector;                                                                              
      52                 :         }                                                                                                                
      53               2 :         foreach ($custom_injectors as $injector) {                                                                       
      54                 :             if (is_string($injector)) {                                                                                  
      55                 :                 $injector = "HTMLPurifier_Injector_$injector";                                                           
      56                 :                 $injector = new $injector;                                                                               
      57                 :             }                                                                                                            
      58                 :             $this->injectors[] = $injector;                                                                              
      59                 :         }                                                                                                                
      60                 :                                                                                                                          
      61                 :         // array index of the injector that resulted in an array                                                         
      62                 :         // substitution. This enables processTokens() to know which                                                      
      63                 :         // injectors are affected by the added tokens and which are                                                      
      64                 :         // not (namely, the ones after the current injector are not                                                      
      65                 :         // affected)                                                                                                     
      66               2 :         $this->currentInjector = false;                                                                                  
      67                 :                                                                                                                          
      68                 :         // give the injectors references to the definition and context                                                   
      69                 :         // variables for performance reasons                                                                             
      70               2 :         foreach ($this->injectors as $i => $injector) {                                                                  
      71                 :             $error = $injector->prepare($config, $context);                                                              
      72                 :             if (!$error) continue;                                                                                       
      73                 :             array_splice($this->injectors, $i, 1); // rm the injector                                                    
      74                 :             trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING);     
      75                 :         }                                                                                                                
      76                 :                                                                                                                          
      77                 :         // warning: most foreach loops follow the convention $i => $injector.                                            
      78                 :         // Don't define these as loop-wide variables, please!                                                            
      79                 :                                                                                                                          
      80                 :         // -- end INJECTOR --                                                                                            
      81                 :                                                                                                                          
      82               2 :         $token = false;                                                                                                  
      83               2 :         $context->register('CurrentToken', $token);                                                                      
      84                 :                                                                                                                          
      85                 :         // isset is in loop because $tokens size changes during loop exec                                                
      86               2 :         for ($this->inputIndex = 0; isset($tokens[$this->inputIndex]); $this->inputIndex++) {                            
      87                 :                                                                                                                          
      88                 :             // if all goes well, this token will be passed through unharmed                                              
      89               2 :             $token = $tokens[$this->inputIndex];                                                                         
      90                 :                                                                                                                          
      91                 :             //printTokens($tokens, $this->inputIndex);                                                                   
      92                 :                                                                                                                          
      93               2 :             foreach ($this->injectors as $injector) {                                                                    
      94                 :                 if ($injector->skip > 0) $injector->skip--;                                                              
      95                 :             }                                                                                                            
      96                 :                                                                                                                          
      97                 :             // quick-check: if it's not a tag, no need to process                                                        
      98               2 :             if (empty( $token->is_tag )) {                                                                               
      99               2 :                 if ($token instanceof HTMLPurifier_Token_Text) {                                                         
     100                 :                      // injector handler code; duplicated for performance reasons                                        
     101               2 :                      foreach ($this->injectors as $i => $injector) {                                                     
     102                 :                          if (!$injector->skip) $injector->handleText($token);                                            
     103                 :                          if (is_array($token)) {                                                                         
     104                 :                              $this->currentInjector = $i;                                                                
     105                 :                              break;                                                                                      
     106                 :                          }                                                                                               
     107                 :                      }                                                                                                   
     108               2 :                 }                                                                                                        
     109               2 :                 $this->processToken($token, $config, $context);                                                          
     110               2 :                 continue;                                                                                                
     111                 :             }                                                                                                            
     112                 :                                                                                                                          
     113               2 :             $info = $definition->info[$token->name]->child;                                                              
     114                 :                                                                                                                          
     115                 :             // quick tag checks: anything that's *not* an end tag                                                        
     116               2 :             $ok = false;                                                                                                 
     117               2 :             if ($info->type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {                                 
     118                 :                 // test if it claims to be a start tag but is empty                                                      
     119                 :                 $token = new HTMLPurifier_Token_Empty($token->name, $token->attr);                                       
     120                 :                 $ok = true;                                                                                              
     121               2 :             } elseif ($info->type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {                           
     122                 :                 // claims to be empty but really is a start tag                                                          
     123                 :                 $token = array(                                                                                          
     124               1 :                     new HTMLPurifier_Token_Start($token->name, $token->attr),                                            
     125               1 :                     new HTMLPurifier_Token_End($token->name)                                                             
     126               1 :                 );                                                                                                       
     127               1 :                 $ok = true;                                                                                              
     128               2 :             } elseif ($token instanceof HTMLPurifier_Token_Empty) {                                                      
     129                 :                 // real empty token                                                                                      
     130               2 :                 $ok = true;                                                                                              
     131               2 :             } elseif ($token instanceof HTMLPurifier_Token_Start) {                                                      
     132                 :                 // start tag                                                                                             
     133                 :                                                                                                                          
     134                 :                 // ...unless they also have to close their parent                                                        
     135               2 :                 if (!empty($this->currentNesting)) {                                                                     
     136                 :                                                                                                                          
     137               2 :                     $parent = array_pop($this->currentNesting);                                                          
     138               2 :                     $parent_info = $definition->info[$parent->name];                                                     
     139                 :                                                                                                                          
     140                 :                     // this can be replaced with a more general algorithm:                                               
     141                 :                     // if the token is not allowed by the parent, auto-close                                             
     142                 :                     // the parent                                                                                        
     143               2 :                     if (!isset($parent_info->child->elements[$token->name])) {                                           
     144                 :                         if ($e) $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent);                 
     145                 :                         // close the parent, then re-loop to reprocess token                                             
     146                 :                         $result[] = new HTMLPurifier_Token_End($parent->name);                                           
     147                 :                         $this->inputIndex--;                                                                             
     148                 :                         continue;                                                                                        
     149                 :                     }                                                                                                    
     150                 :                                                                                                                          
     151               2 :                     $this->currentNesting[] = $parent; // undo the pop                                                   
     152               2 :                 }                                                                                                        
     153               2 :                 $ok = true;                                                                                              
     154               2 :             }                                                                                                            
     155                 :                                                                                                                          
     156                 :             // injector handler code; duplicated for performance reasons                                                 
     157               2 :             if ($ok) {                                                                                                   
     158               2 :                 foreach ($this->injectors as $i => $injector) {                                                          
     159                 :                     if (!$injector->skip) $injector->handleElement($token);                                              
     160                 :                     if (is_array($token)) {                                                                              
     161                 :                         $this->currentInjector = $i;                                                                     
     162                 :                         break;                                                                                           
     163                 :                     }                                                                                                    
     164                 :                 }                                                                                                        
     165               2 :                 $this->processToken($token, $config, $context);                                                          
     166               2 :                 continue;                                                                                                
     167                 :             }                                                                                                            
     168                 :                                                                                                                          
     169                 :             // sanity check: we should be dealing with a closing tag                                                     
     170               2 :             if (!$token instanceof HTMLPurifier_Token_End) continue;                                                     
     171                 :                                                                                                                          
     172                 :             // make sure that we have something open                                                                     
     173               2 :             if (empty($this->currentNesting)) {                                                                          
     174                 :                 if ($escape_invalid_tags) {                                                                              
     175                 :                     if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');                 
     176                 :                     $result[] = new HTMLPurifier_Token_Text(                                                             
     177                 :                         $generator->generateFromToken($token)                                                            
     178                 :                     );                                                                                                   
     179                 :                 } elseif ($e) {                                                                                          
     180                 :                     $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');                         
     181                 :                 }                                                                                                        
     182                 :                 continue;                                                                                                
     183                 :             }                                                                                                            
     184                 :                                                                                                                          
     185                 :             // first, check for the simplest case: everything closes neatly                                              
     186               2 :             $current_parent = array_pop($this->currentNesting);                                                          
     187               2 :             if ($current_parent->name == $token->name) {                                                                 
     188               2 :                 $result[] = $token;                                                                                      
     189               2 :                 foreach ($this->injectors as $i => $injector) {                                                          
     190                 :                     $injector->notifyEnd($token);                                                                        
     191                 :                 }                                                                                                        
     192               2 :                 continue;                                                                                                
     193                 :             }                                                                                                            
     194                 :                                                                                                                          
     195                 :             // okay, so we're trying to close the wrong tag                                                              
     196                 :                                                                                                                          
     197                 :             // undo the pop previous pop                                                                                 
     198                 :             $this->currentNesting[] = $current_parent;                                                                   
     199                 :                                                                                                                          
     200                 :             // scroll back the entire nest, trying to find our tag.                                                      
     201                 :             // (feature could be to specify how far you'd like to go)                                                    
     202                 :             $size = count($this->currentNesting);                                                                        
     203                 :             // -2 because -1 is the last element, but we already checked that                                            
     204                 :             $skipped_tags = false;                                                                                       
     205                 :             for ($i = $size - 2; $i >= 0; $i--) {                                                                        
     206                 :                 if ($this->currentNesting[$i]->name == $token->name) {                                                   
     207                 :                     // current nesting is modified                                                                       
     208                 :                     $skipped_tags = array_splice($this->currentNesting, $i);                                             
     209                 :                     break;                                                                                               
     210                 :                 }                                                                                                        
     211                 :             }                                                                                                            
     212                 :                                                                                                                          
     213                 :             // we still didn't find the tag, so remove                                                                   
     214                 :             if ($skipped_tags === false) {                                                                               
     215                 :                 if ($escape_invalid_tags) {                                                                              
     216                 :                     $result[] = new HTMLPurifier_Token_Text(                                                             
     217                 :                         $generator->generateFromToken($token)                                                            
     218                 :                     );                                                                                                   
     219                 :                     if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');                       
     220                 :                 } elseif ($e) {                                                                                          
     221                 :                     $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');                               
     222                 :                 }                                                                                                        
     223                 :                 continue;                                                                                                
     224                 :             }                                                                                                            
     225                 :                                                                                                                          
     226                 :             // okay, we found it, close all the skipped tags                                                             
     227                 :             // note that skipped tags contains the element we need closed                                                
     228                 :             for ($i = count($skipped_tags) - 1; $i >= 0; $i--) {                                                         
     229                 :                 // please don't redefine $i!                                                                             
     230                 :                 if ($i && $e && !isset($skipped_tags[$i]->armor['MakeWellFormed_TagClosedError'])) {                     
     231                 :                     $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$i]);         
     232                 :                 }                                                                                                        
     233                 :                 $result[] = $new_token = new HTMLPurifier_Token_End($skipped_tags[$i]->name);                            
     234                 :                 foreach ($this->injectors as $injector) {                                                                
     235                 :                     $injector->notifyEnd($new_token);                                                                    
     236                 :                 }                                                                                                        
     237                 :             }                                                                                                            
     238                 :                                                                                                                          
     239                 :         }                                                                                                                
     240                 :                                                                                                                          
     241               2 :         $context->destroy('CurrentNesting');                                                                             
     242               2 :         $context->destroy('InputTokens');                                                                                
     243               2 :         $context->destroy('InputIndex');                                                                                 
     244               2 :         $context->destroy('CurrentToken');                                                                               
     245                 :                                                                                                                          
     246                 :         // we're at the end now, fix all still unclosed tags (this is                                                    
     247                 :         // duplicated from the end of the loop with some slight modifications)                                           
     248                 :         // not using $skipped_tags since it would invariably be all of them                                              
     249               2 :         if (!empty($this->currentNesting)) {                                                                             
     250                 :             for ($i = count($this->currentNesting) - 1; $i >= 0; $i--) {                                                 
     251                 :                 // please don't redefine $i!                                                                             
     252                 :                 if ($e && !isset($this->currentNesting[$i]->armor['MakeWellFormed_TagClosedError'])) {                   
     253                 :                     $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $this->currentNesting[$i]);
     254                 :                 }                                                                                                        
     255                 :                 $result[] = $new_token = new HTMLPurifier_Token_End($this->currentNesting[$i]->name);                    
     256                 :                 foreach ($this->injectors as $injector) {                                                                
     257                 :                     $injector->notifyEnd($new_token);                                                                    
     258                 :                 }                                                                                                        
     259                 :             }                                                                                                            
     260                 :         }                                                                                                                
     261                 :                                                                                                                          
     262               2 :         unset($this->outputTokens, $this->injectors, $this->currentInjector,                                             
     263                 :           $this->currentNesting, $this->inputTokens, $this->inputIndex);                                                 
     264                 :                                                                                                                          
     265               2 :         return $result;                                                                                                  
     266                 :     }                                                                                                                    
     267                 :                                                                                                                          
     268                 :     function processToken($token, $config, $context) {                                                                   
     269               2 :         if (is_array($token)) {                                                                                          
     270                 :             // the original token was overloaded by an injector, time                                                    
     271                 :             // to some fancy acrobatics                                                                                  
     272                 :                                                                                                                          
     273                 :             // $this->inputIndex is decremented so that the entire set gets                                              
     274                 :             // re-processed                                                                                              
     275               1 :             array_splice($this->inputTokens, $this->inputIndex--, 1, $token);                                            
     276                 :                                                                                                                          
     277                 :             // adjust the injector skips based on the array substitution                                                 
     278               1 :             if ($this->injectors) {                                                                                      
     279               0 :                 $offset = count($token);                                                                                 
     280               0 :                 for ($i = 0; $i <= $this->currentInjector; $i++) {                                                       
     281                 :                     // because of the skip back, we need to add one more                                                 
     282                 :                     // for uninitialized injectors. I'm not exactly                                                      
     283                 :                     // sure why this is the case, but I think it has to                                                  
     284                 :                     // do with the fact that we're decrementing skips                                                    
     285                 :                     // before re-checking text                                                                           
     286               0 :                     if (!$this->injectors[$i]->skip) $this->injectors[$i]->skip++;                                       
     287               0 :                     $this->injectors[$i]->skip += $offset;                                                               
     288               0 :                 }                                                                                                        
     289               0 :             }                                                                                                            
     290               2 :         } elseif ($token) {                                                                                              
     291                 :             // regular case                                                                                              
     292               2 :             $this->outputTokens[] = $token;                                                                              
     293               2 :             if ($token instanceof HTMLPurifier_Token_Start) {                                                            
     294               2 :                 $this->currentNesting[] = $token;                                                                        
     295               2 :             } elseif ($token instanceof HTMLPurifier_Token_End) {                                                        
     296               0 :                 array_pop($this->currentNesting); // not actually used                                                   
     297               0 :             }                                                                                                            
     298               2 :         }                                                                                                                
     299               2 :     }                                                                                                                    
     300                 :                                                                                                                          
     301                 : }                                                                                                                        
     302                 :                                                                                                                          

Generated by PHPUnit 3.3.1 and Xdebug 2.0.2 at Thu Sep 25 18:42:10 CEST 2008.