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
00192 $this->_input = "" ;
00193 $this->_output = "" ;
00194 $this->_position = 0 ;
00195 $this->_errors = array() ;
00196
00197
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
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
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
00350 return($this->_errors);
00351 }
00352
00367 function Compile($Input)
00368 {
00369
00370 $I =& $this->_input;
00371 $P =& $this->_position;
00372 $O =& $this->_output;
00373 $E =& $this->_errors;
00374
00375
00376 $I = $Input;
00377 $P = 0;
00378 $O = "";
00379 $E = array();
00380
00381
00382 if(!is_string($Input))
00383 {
00384 $E[0] = "Template not a string";
00385 $I = "";
00386 $O = "";
00387 $P = 0;
00388 }
00389
00390
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
00403 if(strlen($I) > $P)
00404 {
00405 $E[0] = "Template not completely parsed!";
00406 $O = "";
00407 }
00408
00409
00410 return($O);
00411 }
00412
00425 function _CheckPosition($SkipPositionCheck = FALSE)
00426 {
00427
00428 $I =& $this->_input;
00429 $P =& $this->_position;
00430 $O =& $this->_output;
00431 $E =& $this->_errors;
00432
00433
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
00457 return(TRUE);
00458 }
00459
00474 function _MatchStart($Needle)
00475 {
00476
00477 if(!$this->_CheckPosition(TRUE))
00478 {
00479 return(FALSE);
00480 }
00481
00482
00483 $I =& $this->_input;
00484 $P =& $this->_position;
00485 $O =& $this->_output;
00486 $E =& $this->_errors;
00487
00488
00489 if((strlen($I) - $P) < strlen($Needle))
00490 {
00491 return(FALSE);
00492 }
00493
00494
00495 for($x = 0; $x < strlen($Needle); $x++)
00496 {
00497 if($I[$P+$x] != $Needle[$x])
00498 {
00499 return(FALSE);
00500 }
00501 }
00502
00503
00504 return(TRUE);
00505 }
00506
00525 function _ConvertPhpTags()
00526 {
00527
00528 if(!$this->_CheckPosition())
00529 {
00530 return(FALSE);
00531 }
00532
00533
00534 if(!$this->_CallConvertPhpTags)
00535 {
00536 return(FALSE);
00537 }
00538
00539
00540 $I =& $this->_input;
00541 $P =& $this->_position;
00542 $O =& $this->_output;
00543 $E =& $this->_errors;
00544
00545
00546 if($this->_MatchStart("<"."?"))
00547 {
00548 $E[$P] = "Found opening PHP tag";
00549 $O .= "<?";
00550 $P += 2;
00551 return(TRUE);
00552 }
00553 if($this->_MatchStart("?".">"))
00554 {
00555 $E[$P] = "Found closing PHP tag";
00556 $O .= "?>";
00557 $P += 2;
00558 return(TRUE);
00559 }
00560
00561
00562 if($this->_MatchStart("<"."%"))
00563 {
00564 $E[$P] = "Found opening ASP tag";
00565 $O .= "<%";
00566 $P += 2;
00567 return(TRUE);
00568 }
00569 if($this->_MatchStart("%".">"))
00570 {
00571 $E[$P] = "Found closing ASP tag";
00572 $O .= "%>";
00573 $P += 2;
00574 return(TRUE);
00575 }
00576
00577
00578 return(FALSE);
00579 }
00580
00592 function _SimpleCopyChar()
00593 {
00594
00595 if(!$this->_CheckPosition())
00596 {
00597 return(FALSE);
00598 }
00599
00600
00601 $I =& $this->_input;
00602 $P =& $this->_position;
00603 $O =& $this->_output;
00604 $E =& $this->_errors;
00605
00606
00607 $O .= $I[$P];
00608 $P++;
00609 return(TRUE);
00610 }
00611
00625 function _RemoveNewlines()
00626 {
00627
00628 if(!$this->_CheckPosition())
00629 {
00630 return(FALSE);
00631 }
00632
00633
00634 if(!$this->_CallRemoveNewlines)
00635 {
00636 return(FALSE);
00637 }
00638
00639
00640 $I =& $this->_input;
00641 $P =& $this->_position;
00642 $O =& $this->_output;
00643 $E =& $this->_errors;
00644
00645
00646 if("\n" == $I[$P])
00647 {
00648 $P++;
00649 return(TRUE);
00650 }
00651
00652
00653 return(FALSE);
00654 }
00655
00669 function _RemoveTabs()
00670 {
00671
00672 if(!$this->_CheckPosition())
00673 {
00674 return(FALSE);
00675 }
00676
00677
00678 if(!$this->_CallRemoveTabs)
00679 {
00680 return(FALSE);
00681 }
00682
00683
00684 $I =& $this->_input;
00685 $P =& $this->_position;
00686 $O =& $this->_output;
00687 $E =& $this->_errors;
00688
00689
00690 if("\t" == $I[$P])
00691 {
00692 $P++;
00693 return(TRUE);
00694 }
00695
00696
00697 return(FALSE);
00698 }
00699
00713 function _CompressWhite()
00714 {
00715
00716 if(!$this->_CheckPosition())
00717 {
00718 return(FALSE);
00719 }
00720
00721
00722 if(!$this->_CallCompressWhite)
00723 {
00724 return(FALSE);
00725 }
00726
00727
00728 $I =& $this->_input;
00729 $P =& $this->_position;
00730 $O =& $this->_output;
00731 $E =& $this->_errors;
00732
00733
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
00745 return(FALSE);
00746 }
00747
00757 function _OutputExpr()
00758 {
00759
00760 if(!$this->_CheckPosition())
00761 {
00762 return(FALSE);
00763 }
00764
00765
00766 if(!$this->_CallOutputExpr)
00767 {
00768 return(FALSE);
00769 }
00770
00771
00772 $I =& $this->_input;
00773 $P =& $this->_position;
00774 $O =& $this->_output;
00775 $E =& $this->_errors;
00776
00777
00778 $start_pos = $P;
00779
00780
00781 $o = '<'.'?php';
00782 $c = '?'.'>';
00783
00784
00785 if($this->_MatchStart("|}"))
00786 {
00787 $E[$P] = "Orphan |}";
00788 $O = "";
00789 $P = strlen($I);
00790 return(TRUE);
00791 }
00792
00793
00794 if($this->_MatchStart("{|"))
00795 {
00796
00797 $temp_output = "";
00798
00799
00800 $start = $P;
00801 $P += 2;
00802
00803
00804 while($P < strlen($I) && !$this->_MatchStart("|}"))
00805 {
00806 $temp_output .= $I[$P++];
00807 }
00808
00809
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
00826 return(TRUE);
00827 }
00828
00829
00830 return(FALSE);
00831 }
00832
00842 function _IncludeExpr()
00843 {
00844
00845 if(!$this->_CheckPosition())
00846 {
00847 return(FALSE);
00848 }
00849
00850
00851 if(!$this->_CallIncludeExpr)
00852 {
00853 return(FALSE);
00854 }
00855
00856
00857 $I =& $this->_input;
00858 $P =& $this->_position;
00859 $O =& $this->_output;
00860 $E =& $this->_errors;
00861
00862
00863 $start_pos = $P;
00864
00865
00866 $o = '<'.'?php';
00867 $c = '?'.'>';
00868
00869
00870 if($this->_MatchStart("+}"))
00871 {
00872 $E[$P] = "Orphan +}";
00873 $O = "";
00874 $P = strlen($I);
00875 return(TRUE);
00876 }
00877
00878
00879 if($this->_MatchStart("{+"))
00880 {
00881
00882 $temp_output = "";
00883
00884
00885 $start = $P;
00886 $P += 2;
00887
00888
00889 while($P < strlen($I) && !$this->_MatchStart("+}"))
00890 {
00891 $temp_output .= $I[$P++];
00892 }
00893
00894
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
00911 return(TRUE);
00912 }
00913
00914
00915 return(FALSE);
00916 }
00917
00943 function _ForeachExpr()
00944 {
00945
00946 static $start_pos = array();
00947
00948
00949 if(!$this->_CheckPosition(TRUE))
00950 {
00951 return(FALSE);
00952 }
00953
00954
00955 if(!$this->_CallForeachExpr)
00956 {
00957 return(FALSE);
00958 }
00959
00960
00961 $I =& $this->_input;
00962 $P =& $this->_position;
00963 $O =& $this->_output;
00964 $E =& $this->_errors;
00965
00966
00967 $o = '<'.'?php';
00968 $c = '?'.'>';
00969
00970
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
00983
00984 if(!$this->_CheckPosition())
00985 {
00986 return(FALSE);
00987 }
00988
00989
00990
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
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
01019 if($this->_MatchStart("{@"))
01020 {
01021
01022
01023 $start = $P;
01024 $P += 2;
01025 $temp_output = "";
01026
01027
01028 while($P < strlen($I) && !$this->_MatchStart("@}"))
01029 {
01030 $temp_output .= $I[$P++];
01031 }
01032
01033
01034
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
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
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
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
01152 static $nest = array();
01153
01154
01155 if(!$this->_CheckPosition(TRUE))
01156 {
01157 return(FALSE);
01158 }
01159
01160
01161 if(!$this->_CallIfBlockExpr)
01162 {
01163 return(FALSE);
01164 }
01165
01166
01167 $I =& $this->_input;
01168 $P =& $this->_position;
01169 $O =& $this->_output;
01170 $E =& $this->_errors;
01171
01172
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
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
01208
01209 if(!$this->_CheckPosition())
01210 {
01211 return(FALSE);
01212 }
01213
01214
01215 foreach($tagorder as $tag)
01216 {
01217
01218 if($this->_MatchStart($tag))
01219 {
01220
01221 if(in_array($state, $allow[$tag]))
01222 {
01223
01224 if(1 == $nestinc[$tag])
01225 {
01226 $level = count($nest)+1;
01227 $start =& $nest[$level]["start"];
01228 $state =& $nest[$level]["state"];
01229 }
01230
01231
01232 $start = $P;
01233 $state = $newstate[$tag];
01234
01235
01236 $O .= $replace[$tag];
01237 $P += strlen($tag);
01238
01239
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 ?>