Main Page | Class List | Directories | File List | Class Members | File Members | Related Pages

Templification.class.php

Go to the documentation of this file.
00001 <?php
00002 
00062 class Templification
00063 {
00071     var $_input;
00072 
00082     var $_position;
00083 
00092     var $_output;
00093 
00102     var $_errors;
00103 
00111     var $_CallConvertPhpTags;
00112 
00120     var $_CallRemoveNewlines;
00121 
00129     var $_CallRemoveTabs;
00130 
00138     var $_CallCompressWhite;
00139 
00148     var $_CallOutputExpr;
00149 
00158     var $_CallIncludeExpr;
00159 
00168     var $_CallForeachExpr;
00169 
00178     var $_CallIfBlockExpr;
00179 
00189     function & Templification()
00190     {
00191         // Initialize required variables
00192         $this->_input    = ""      ;
00193         $this->_output   = ""      ;
00194         $this->_position = 0       ;
00195         $this->_errors   = array() ;
00196 
00197         // Initialize engine choice
00198         $this->_CallConvertPhpTags = TRUE  ;
00199         $this->_CallRemoveNewlines = FALSE ;
00200         $this->_CallRemoveTabs     = TRUE  ;
00201         $this->_CallCompressWhite  = TRUE  ;
00202         $this->_CallOutputExpr     = TRUE  ;
00203         $this->_CallIncludeExpr    = TRUE  ;
00204         $this->_CallForeachExpr    = TRUE  ;
00205         $this->_CallIfBlockExpr    = TRUE  ;
00206 
00207         // Return self
00208         return($this);
00209     }
00210 
00224     function ChangeMode($NewModes=array())
00225     {
00226         $name_conversion = array(
00227                 "php"  => "_CallConvertPhpTags" ,
00228                 "rmnl" => "_CallRemoveNewlines" ,
00229                 "rmtb" => "_CallRemoveTabs"     ,
00230                 "rmws" => "_CallCompressWhite"  ,
00231                 "out"  => "_CallOutputExpr"     ,
00232                 "inc"  => "_CallIncludeExpr"    ,
00233                 "for"  => "_CallForeachExpr"    ,
00234                 "if"   => "_CallIfBlockExpr"    ,
00235                 );
00236 
00237         if(!is_array($NewModes))
00238         {
00239             return(FALSE);
00240         }
00241 
00242         foreach($NewModes as $Name => $Value)
00243         {
00244             if(!isset($name_conversion[$Name]))
00245             {
00246                 return(FALSE);
00247             }
00248 
00249             if(!is_bool($Value))
00250             {
00251                 return(FALSE);
00252             }
00253 
00254             $Name = $name_conversion[$Name];
00255             $this->$Name = $Value;
00256         }
00257 
00258         return(TRUE);
00259     }
00260 
00271     function GetModes()
00272     {
00273         return(array(
00274             array(
00275                 "Name"        => "php",
00276                 "Default"     => TRUE,
00277                 "Description" =>
00278                     "Allow raw PHP in the templates"
00279                 ),
00280             array(
00281                 "Name"        => "rmnl",
00282                 "Default"     => FALSE,
00283                 "Description" =>
00284                     "Remove all newlines"
00285                 ),
00286             array(
00287                 "Name"        => "rmtb",
00288                 "Default"     => TRUE,
00289                 "Description" =>
00290                     "Remove all tabs"
00291                 ),
00292             array(
00293                 "Name"        => "rmws",
00294                 "Default"     => TRUE,
00295                 "Description" =>
00296                     "Remove extra whitespace"
00297                 ),
00298             array(
00299                 "Name"        => "out",
00300                 "Default"     => TRUE,
00301                 "Description" =>
00302                     "Process {||} tags"
00303                 ),
00304             array(
00305                 "Name"        => "inc",
00306                 "Default"     => TRUE,
00307                 "Description" =>
00308                     "Process {++} tags"
00309                 ),
00310             array(
00311                 "Name"        => "for",
00312                 "Default"     => TRUE,
00313                 "Description" =>
00314                     "Process {@@} tags"
00315                 ),
00316             array(
00317                 "Name"        => "if",
00318                 "Default"     => TRUE,
00319                 "Description" =>
00320                     "Process {??} tags"
00321                 ),
00322             ));
00323     }
00324 
00334     function GetErrors()
00335     {
00336         // Sanity checks
00337         if(!is_array($this->_errors))
00338         {
00339             return(array("Error getting errors... WTF"));
00340         }
00341         foreach($this->_errors as $err)
00342         {
00343             if(!is_string($err))
00344             {
00345                 return(array("Error getting errors... WTF"));
00346             }
00347         }
00348 
00349         // Simple accessor return
00350         return($this->_errors);
00351     }
00352 
00367     function Compile($Input)
00368     {
00369         // Locally reference variables
00370         $I =& $this->_input;
00371         $P =& $this->_position;
00372         $O =& $this->_output;
00373         $E =& $this->_errors;
00374 
00375         // Re-initialize with input
00376         $I = $Input;
00377         $P = 0;
00378         $O = "";
00379         $E = array();
00380 
00381         // Check sanity
00382         if(!is_string($Input))
00383         {
00384             $E[0] = "Template not a string";
00385             $I = "";
00386             $O = "";
00387             $P = 0;
00388         }
00389 
00390         // Run each engine
00391         while(  $this-> _ConvertPhpTags ()
00392              || $this-> _RemoveNewlines ()
00393              || $this-> _RemoveTabs     ()
00394              || $this-> _CompressWhite  ()
00395              || $this-> _OutputExpr     ()
00396              || $this-> _IncludeExpr    ()
00397              || $this-> _ForeachExpr    ()
00398              || $this-> _IfBlockExpr    ()
00399              || $this-> _SimpleCopyChar ()
00400              );
00401 
00402         // Sanity check
00403         if(strlen($I) > $P)
00404         {
00405             $E[0] = "Template not completely parsed!";
00406             $O = "";
00407         }
00408 
00409         // Return output
00410         return($O);
00411     }
00412 
00425     function _CheckPosition($SkipPositionCheck = FALSE)
00426     {
00427         // Locally reference variables
00428         $I =& $this->_input;
00429         $P =& $this->_position;
00430         $O =& $this->_output;
00431         $E =& $this->_errors;
00432 
00433         // Check variables
00434         if(   !is_string($I)
00435           ||  !is_string($O)
00436           ||  !is_array($E)
00437           ||  !is_int($P)
00438           ||  !is_bool($this->_CallConvertPhpTags)
00439           ||  !is_bool($this->_CallRemoveNewlines)
00440           ||  !is_bool($this->_CallRemoveTabs    )
00441           ||  !is_bool($this->_CallCompressWhite )
00442           ||  !is_bool($this->_CallOutputExpr    )
00443           ||  !is_bool($this->_CallIncludeExpr   )
00444           ||  !is_bool($this->_CallForeachExpr   )
00445           ||  !is_bool($this->_CallIfBlockExpr   )
00446           ||  strlen($I) == 0
00447           )
00448         {
00449             return(FALSE);
00450         }
00451         if(FALSE === $SkipPositionCheck && strlen($I) <= $P)
00452         {
00453             return(FALSE);
00454         }
00455 
00456         // No problems found so far
00457         return(TRUE);
00458     }
00459 
00474     function _MatchStart($Needle)
00475     {
00476         // Check sanity
00477         if(!$this->_CheckPosition(TRUE))
00478         {
00479             return(FALSE);
00480         }
00481 
00482         // Locally reference variables
00483         $I =& $this->_input;
00484         $P =& $this->_position;
00485         $O =& $this->_output;
00486         $E =& $this->_errors;
00487 
00488         // First check raw size problems
00489         if((strlen($I) - $P) < strlen($Needle))
00490         {
00491             return(FALSE);
00492         }
00493 
00494         // Step across needle
00495         for($x = 0; $x < strlen($Needle); $x++)
00496         {
00497             if($I[$P+$x] != $Needle[$x])
00498             {
00499                 return(FALSE);
00500             }
00501         }
00502 
00503         // Didn't fail, so beginning matches
00504         return(TRUE);
00505     }
00506 
00525     function _ConvertPhpTags()
00526     {
00527         // Check sanity
00528         if(!$this->_CheckPosition())
00529         {
00530             return(FALSE);
00531         }
00532 
00533         // Check if we should be running
00534         if(!$this->_CallConvertPhpTags)
00535         {
00536             return(FALSE);
00537         }
00538 
00539         // Locally reference variables
00540         $I =& $this->_input;
00541         $P =& $this->_position;
00542         $O =& $this->_output;
00543         $E =& $this->_errors;
00544 
00545         // Check php tags
00546         if($this->_MatchStart("<"."?"))
00547         {
00548             $E[$P] = "Found opening PHP tag";
00549             $O .= "&lt;?";
00550             $P += 2;
00551             return(TRUE);
00552         }
00553         if($this->_MatchStart("?".">"))
00554         {
00555             $E[$P] = "Found closing PHP tag";
00556             $O .= "?&gt;";
00557             $P += 2;
00558             return(TRUE);
00559         }
00560 
00561         // Check asp tags
00562         if($this->_MatchStart("<"."%"))
00563         {
00564             $E[$P] = "Found opening ASP tag";
00565             $O .= "&lt;%";
00566             $P += 2;
00567             return(TRUE);
00568         }
00569         if($this->_MatchStart("%".">"))
00570         {
00571             $E[$P] = "Found closing ASP tag";
00572             $O .= "%&gt;";
00573             $P += 2;
00574             return(TRUE);
00575         }
00576 
00577         // Not handled
00578         return(FALSE);
00579     }
00580 
00592     function _SimpleCopyChar()
00593     {
00594         // Check sanity
00595         if(!$this->_CheckPosition())
00596         {
00597             return(FALSE);
00598         }
00599 
00600         // Locally reference variables
00601         $I =& $this->_input;
00602         $P =& $this->_position;
00603         $O =& $this->_output;
00604         $E =& $this->_errors;
00605 
00606         // Copy character and increment position
00607         $O .= $I[$P];
00608         $P++;
00609         return(TRUE);
00610     }
00611 
00625     function _RemoveNewlines()
00626     {
00627         // Check sanity
00628         if(!$this->_CheckPosition())
00629         {
00630             return(FALSE);
00631         }
00632 
00633         // Check if we should be running
00634         if(!$this->_CallRemoveNewlines)
00635         {
00636             return(FALSE);
00637         }
00638 
00639         // Locally reference variables
00640         $I =& $this->_input;
00641         $P =& $this->_position;
00642         $O =& $this->_output;
00643         $E =& $this->_errors;
00644 
00645         // Check if current character is newline
00646         if("\n" == $I[$P])
00647         {
00648             $P++;
00649             return(TRUE);
00650         }
00651 
00652         // Not handled
00653         return(FALSE);
00654     }
00655 
00669     function _RemoveTabs()
00670     {
00671         // Check sanity
00672         if(!$this->_CheckPosition())
00673         {
00674             return(FALSE);
00675         }
00676 
00677         // Check if we should be running
00678         if(!$this->_CallRemoveTabs)
00679         {
00680             return(FALSE);
00681         }
00682 
00683         // Locally reference variables
00684         $I =& $this->_input;
00685         $P =& $this->_position;
00686         $O =& $this->_output;
00687         $E =& $this->_errors;
00688 
00689         // Check if current character is newline
00690         if("\t" == $I[$P])
00691         {
00692             $P++;
00693             return(TRUE);
00694         }
00695 
00696         // Not handled
00697         return(FALSE);
00698     }
00699 
00713     function _CompressWhite()
00714     {
00715         // Check sanity
00716         if(!$this->_CheckPosition())
00717         {
00718             return(FALSE);
00719         }
00720 
00721         // Check if we should be running
00722         if(!$this->_CallCompressWhite)
00723         {
00724             return(FALSE);
00725         }
00726 
00727         // Locally reference variables
00728         $I =& $this->_input;
00729         $P =& $this->_position;
00730         $O =& $this->_output;
00731         $E =& $this->_errors;
00732 
00733         // Check for whitespace
00734         if($I[$P] == " " || $I[$P] == "\t")
00735         {
00736             while($P < strlen($I) && ($I[$P] == " " || $I[$P] == "\t"))
00737             {
00738                 $P++;
00739             }
00740             $O .= " ";
00741             return(TRUE);
00742         }
00743 
00744         // Not handled
00745         return(FALSE);
00746     }
00747 
00757     function _OutputExpr()
00758     {
00759         // Check sanity
00760         if(!$this->_CheckPosition())
00761         {
00762             return(FALSE);
00763         }
00764 
00765         // Check if we should be running
00766         if(!$this->_CallOutputExpr)
00767         {
00768             return(FALSE);
00769         }
00770 
00771         // Locally reference variables
00772         $I =& $this->_input;
00773         $P =& $this->_position;
00774         $O =& $this->_output;
00775         $E =& $this->_errors;
00776 
00777         // Record the start position for if this is a tag
00778         $start_pos = $P;
00779 
00780         // Reference since we can't just smack these in place
00781         $o = '<'.'?php';
00782         $c = '?'.'>';
00783 
00784         // Check for orphans first
00785         if($this->_MatchStart("|}"))
00786         {
00787             $E[$P] = "Orphan |}";
00788             $O = "";
00789             $P = strlen($I);
00790             return(TRUE);
00791         }
00792 
00793         // Check for opening tag
00794         if($this->_MatchStart("{|"))
00795         {
00796             // Store output for success case only
00797             $temp_output = "";
00798 
00799             // We'll always skip the open tag itself
00800             $start = $P;
00801             $P += 2;
00802 
00803             // Scan to end tag or EOF using temporary position
00804             while($P < strlen($I) && !$this->_MatchStart("|}"))
00805             {
00806                 $temp_output .= $I[$P++];
00807             }
00808 
00809             // If end tag found then close, else record error
00810             if($this->_MatchStart("|}"))
00811             {
00812                 $O .= "$o print ( ";
00813                 $O .= $temp_output;
00814                 $O .= " ); $c";
00815                 $P += 2;
00816             }
00817             else
00818             {
00819                 $E[$start] = "{| Missing closing tag";
00820                 $O = "";
00821                 $P = strlen($I);
00822                 return(TRUE);
00823             }
00824 
00825             // Either way we DID handle this
00826             return(TRUE);
00827         }
00828 
00829         // Didn't find a tag
00830         return(FALSE);
00831     }
00832 
00842     function _IncludeExpr()
00843     {
00844         // Check sanity
00845         if(!$this->_CheckPosition())
00846         {
00847             return(FALSE);
00848         }
00849 
00850         // Check if we should be running
00851         if(!$this->_CallIncludeExpr)
00852         {
00853             return(FALSE);
00854         }
00855 
00856         // Locally reference variables
00857         $I =& $this->_input;
00858         $P =& $this->_position;
00859         $O =& $this->_output;
00860         $E =& $this->_errors;
00861 
00862         // Record the start position for if this is a tag
00863         $start_pos = $P;
00864 
00865         // Reference since we can't just smack these in place
00866         $o = '<'.'?php';
00867         $c = '?'.'>';
00868 
00869         // Check for orphans first
00870         if($this->_MatchStart("+}"))
00871         {
00872             $E[$P] = "Orphan +}";
00873             $O = "";
00874             $P = strlen($I);
00875             return(TRUE);
00876         }
00877 
00878         // Check for opening tag
00879         if($this->_MatchStart("{+"))
00880         {
00881             // Store output for success case only
00882             $temp_output = "";
00883 
00884             // We'll always skip the open tag itself
00885             $start = $P;
00886             $P += 2;
00887 
00888             // Scan to end tag or EOF using temporary position
00889             while($P < strlen($I) && !$this->_MatchStart("+}"))
00890             {
00891                 $temp_output .= $I[$P++];
00892             }
00893 
00894             // If end tag found then close, else record error
00895             if($this->_MatchStart("+}"))
00896             {
00897                 $O .= "$o include ( ";
00898                 $O .= $temp_output;
00899                 $O .= " ); $c";
00900                 $P += 2;
00901             }
00902             else
00903             {
00904                 $E[$start] = "{+ Missing its closing tag";
00905                 $O = "";
00906                 $P = strlen($I);
00907                 return(TRUE);
00908             }
00909 
00910             // Either way we DID handle this
00911             return(TRUE);
00912         }
00913 
00914         // Didn't find a tag
00915         return(FALSE);
00916     }
00917 
00943     function _ForeachExpr()
00944     {
00945         // Statically store start locations
00946         static $start_pos = array();
00947 
00948         // Check sanity
00949         if(!$this->_CheckPosition(TRUE))
00950         {
00951             return(FALSE);
00952         }
00953 
00954         // Check if we should be running
00955         if(!$this->_CallForeachExpr)
00956         {
00957             return(FALSE);
00958         }
00959 
00960         // Locally reference variables
00961         $I =& $this->_input;
00962         $P =& $this->_position;
00963         $O =& $this->_output;
00964         $E =& $this->_errors;
00965 
00966         // Reference since we can't just smack these in place
00967         $o = '<'.'?php';
00968         $c = '?'.'>';
00969 
00970         // If EOF with nest level > 0: Report open-ended loops
00971         if(strlen($I) <= $P && 0 < count($start_pos))
00972         {
00973             foreach($start_pos as $p)
00974             {
00975                 $E[$p] = "Foreach expression missing close";
00976             }
00977             $O = "";
00978             $start_pos = array();
00979             return(FALSE);
00980         }
00981 
00982         // We skipped this in the sanity check so we could report
00983         // open-ended loops, so now we need to perform the check.
00984         if(!$this->_CheckPosition())
00985         {
00986             return(FALSE);
00987         }
00988 
00989         // If we find a closing tag and we're in a nested level, close
00990         // that level, otherwise we found an orphan
00991         if($this->_MatchStart("{@}"))
00992         {
00993             if(0 <  count($start_pos))
00994             {
00995                 array_pop($start_pos);
00996                 $O .= "$o } $c";
00997                 $P += 3;
00998                 return(TRUE);
00999             }
01000             else
01001             {
01002                 $E[$P] = "Found orphan loop-close";
01003                 $O = "";
01004                 $P = strlen($I);
01005                 return(TRUE);
01006             }
01007         }
01008 
01009         // If we found an orphan expression-close
01010         if($this->_MatchStart("@}"))
01011         {
01012             $E[$P] = "Found orphan loop-expression-close";
01013             $O = "";
01014             $P = strlen($I);
01015             return(TRUE);
01016         }
01017 
01018         // If we found a loop-expression-start
01019         if($this->_MatchStart("{@"))
01020         {
01021             // Always skip the tag, even if we end up reporting it
01022             // is an orphan
01023             $start = $P;
01024             $P += 2;
01025             $temp_output = "";
01026 
01027             // Scan to end tag or EOF using temporary position
01028             while($P < strlen($I) && !$this->_MatchStart("@}"))
01029             {
01030                 $temp_output .= $I[$P++];
01031             }
01032 
01033             // If we hit a close-tag instead of close-expression, skip
01034             // the ENTIRE thing
01035             $P--;
01036             if($this->_MatchStart("{@}"))
01037             {
01038                 $E[$start] = "Open-ended loop-expression (close)";
01039                 $O = "";
01040                 $P = strlen($I);
01041                 return(TRUE);
01042             }
01043 
01044             // If we hit EOF, it's an orphan open tag
01045             $P++;
01046             if(!$this->_MatchStart("@}"))
01047             {
01048                 $E[$start] = "Open-ended loop-expression (EOF)";
01049                 $O = "";
01050                 $P = strlen($I);
01051                 return(TRUE);
01052             }
01053 
01054             // We did find the close tag successfully
01055             $O .= "$o foreach ( ";
01056             $O .= $temp_output;
01057             $O .= " ) { $c";
01058             $P += 2;
01059             array_push($start_pos, $start);
01060             return(TRUE);
01061         }
01062     }
01063 
01112     function _IfBlockExpr()
01113     {
01114         // Reference
01115         $o = '<'.'?php';
01116         $c = '?'.'>';
01117         $tagorder = array("{?}" , "{?" , "?}" , "{!}" , "{!" , "!}");
01118         $nestinc = array(
01119             "{?}" => -1,
01120             "{?"  =>  1,
01121             "?}"  =>  0,
01122             "{!}" =>  0,
01123             "{!"  =>  0,
01124             "!}"  =>  0,
01125             );
01126         $allow = array(
01127             "{?}" => array( 2 , 4 , 5 ,   ),
01128             "{?"  => array( 0 , 2 , 4 , 5 ),
01129             "?}"  => array( 1             ),
01130             "{!}" => array( 2 , 4         ),
01131             "{!"  => array( 2 , 4         ),
01132             "!}"  => array( 3             ),
01133             );
01134         $newstate = array(
01135             "{?}" => 0,
01136             "{?"  => 1,
01137             "?}"  => 2,
01138             "{!}" => 5,
01139             "{!"  => 3,
01140             "!}"  => 4,
01141             );
01142         $replace = array(
01143             "{?}" => "$o } $c"        , # Close if block
01144             "{?"  => "$o if( "        , # Open if expression
01145             "?}"  => " ) { $c"        , # Enter if block
01146             "{!}" => "$o } else { $c" , # Enter else block
01147             "{!"  => "$o } elseif( "  , # Open elseif expression
01148             "!}"  => " ) { $c"        , # Enter elseif block
01149             );
01150 
01151         // Statically store nesting info
01152         static $nest = array();
01153 
01154         // Check sanity
01155         if(!$this->_CheckPosition(TRUE))
01156         {
01157             return(FALSE);
01158         }
01159 
01160         // Check if we should be running
01161         if(!$this->_CallIfBlockExpr)
01162         {
01163             return(FALSE);
01164         }
01165 
01166         // Locally reference variables
01167         $I =& $this->_input;
01168         $P =& $this->_position;
01169         $O =& $this->_output;
01170         $E =& $this->_errors;
01171 
01172         // Find most recent addition to nest
01173         if($level =  count($nest))
01174         {
01175             $start =& $nest[$level]["start"];
01176             $state =& $nest[$level]["state"];
01177         }
01178         else
01179         {
01180             $start = 0;
01181             $state = 0;
01182         }
01183 
01184         // Special case: EOF
01185         if(strlen($I) <= $P && $level)
01186         {
01187             foreach($nest as $level)
01188             {
01189                 $name = "";
01190                 switch($level["state"])
01191                 {
01192                     case 1: $name = "If expression"     ; break;
01193                     case 2: $name = "If block"          ; break;
01194                     case 3: $name = "ElseIf expression" ; break;
01195                     case 4: $name = "ElseIf block"      ; break;
01196                     case 5: $name = "Else block"        ; break;
01197                 }
01198 
01199                 $E[$level["start"]] = "$name missing close";
01200             }
01201             $O = "";
01202             $P = strlen($I);
01203             $nest = array();
01204             return(TRUE);
01205         }
01206 
01207         // We skipped this in the sanity check so we could report
01208         // open-ended loops, so now we need to perform the check.
01209         if(!$this->_CheckPosition())
01210         {
01211             return(FALSE);
01212         }
01213 
01214         // For each tag, check current character
01215         foreach($tagorder as $tag)
01216         {
01217             // If we have a match
01218             if($this->_MatchStart($tag))
01219             {
01220                 // If we're allowed at this point to use this tag
01221                 if(in_array($state, $allow[$tag]))
01222                 {
01223                     // Bump nesting level up
01224                     if(1 == $nestinc[$tag])
01225                     {
01226                         $level =  count($nest)+1;
01227                         $start =& $nest[$level]["start"];
01228                         $state =& $nest[$level]["state"];
01229                     }
01230 
01231                     // Record tag location
01232                     $start = $P;
01233                     $state = $newstate[$tag];
01234 
01235                     // Record in output
01236                     $O .= $replace[$tag];
01237                     $P += strlen($tag);
01238 
01239                     // Drop nesting level down
01240                     if(-1 == $nestinc[$tag])
01241                     {
01242                         array_pop($nest);
01243                     }
01244 
01245                     return(TRUE);
01246                 }
01247                 else
01248                 {
01249                     $E[$P] = "Invalid use of $tag";
01250                     $O = "";
01251                     $P = strlen($I);
01252                     return(TRUE);
01253                 }
01254             }
01255         }
01256 
01257         return(FALSE);
01258     }
01259 }
01260 
01261 return("Templification");
01262 
01263 ?>



Generously hosted by   SourceForge.net Logo
Generated on Sat May 14 05:49:25 2005 for Templification by   Doxygen 1.4.1