phpDocumentor lodel
[ class tree: lodel ] [ index: lodel ] [ all elements ]

Source for file parser.php

Documentation is available at parser.php

  1. <?php
  2. /**
  3.  * Fichier Parser
  4.  * PHP versions 4 et 5
  5.  *
  6.  * LODEL - Logiciel d'Edition ELectronique.
  7.  *
  8.  * Copyright (c) 2001-2002, Ghislain Picard, Marin Dacos
  9.  * Copyright (c) 2003, Ghislain Picard, Marin Dacos, Luc Santeramo, Nicolas Nutten, Anne Gentil-Beccot
  10.  * Copyright (c) 2004, Ghislain Picard, Marin Dacos, Luc Santeramo, Anne Gentil-Beccot, Bruno Cénou
  11.  * Copyright (c) 2005, Ghislain Picard, Marin Dacos, Luc Santeramo, Gautier Poupeau, Jean Lamy, Bruno Cénou
  12.  * Copyright (c) 2006, Marin Dacos, Luc Santeramo, Bruno Cénou, Jean Lamy, Mikaël Cixous, Sophie Malafosse
  13.  *
  14.  * Home page: http://www.lodel.org
  15.  *
  16.  * E-Mail: lodel@lodel.org
  17.  *
  18.  * All Rights Reserved
  19.  *
  20.  * This program is free software; you can redistribute it and/or modify
  21.  * it under the terms of the GNU General Public License as published by
  22.  * the Free Software Foundation; either version 2 of the License, or
  23.  * (at your option) any later version.
  24.  *
  25.  * This program is distributed in the hope that it will be useful,
  26.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  27.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  28.  * GNU General Public License for more details.
  29.  *
  30.  * You should have received a copy of the GNU General Public License
  31.  * along with this program; if not, write to the Free Software
  32.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  33.  *
  34.  * @author Ghislain Picard
  35.  * @author Jean Lamy
  36.  * @copyright 2005, Ghislain Picard, Marin Dacos, Luc Santeramo, Gautier Poupeau, Jean Lamy, Bruno Cénou
  37.  * @copyright 2006, Marin Dacos, Luc Santeramo, Bruno Cénou, Jean Lamy, Mikaël Cixous, Sophie Malafosse
  38.  * @licence http://www.gnu.org/copyleft/gpl.html
  39.  * @version CVS:$Id:
  40.  * @package lodel
  41.  */
  42.  
  43.  
  44. function parse($in$out)
  45. {
  46.     $parser new LodelParser;
  47.     $parser->parse($in$out);
  48. }
  49.  
  50. class Parser
  51. {
  52.     
  53.     var $infilename// nom du template
  54.     var $signature;
  55.     var $variable_regexp = "[A-Z][A-Z_0-9]*(?:\.[A-Z][A-Z_0-9]*)*";
  56.     var $variablechar// list of prefix for the variables
  57.  
  58.     var $loops = array ();
  59.     var $funcs = array ();
  60.     var $macrocode = array ();
  61.  
  62.     var $charset;
  63.  
  64.     var $commands = array ();
  65.     var $codepieces = array ()// code piece definition
  66.     var $macros_txt;
  67.     var $fct_txt;
  68.  
  69.     #  var $wantedvars;
  70.     var $looplevel = 0;
  71.  
  72.     var $arr;
  73.     var $countarr;
  74.     var $linearr;
  75.     var $currentline;
  76.     var $ind;
  77.     var $refresh = "";
  78.  
  79.     var $isphp = false// the parser produce a code which produce either html, either php. In the latter, a sequence must be written at the beginning to inform the cache system.
  80.  
  81.     var $id = "";
  82.  
  83.     function errmsg($msg$ind 0)
  84.     {
  85.         // Ŕ REVOIR : déterminer le numéro de ligne dans le template
  86.         //if ($ind)
  87.         //$line = "line ".$this->linearr[$ind];
  88.         //die("LODELSCRIPT ERROR line $line (".$this->infilename."): $msg");
  89.         die("LODELSCRIPT ERROR in file ".$this->infilename." : $msg");
  90.     }
  91.  
  92.     function parse_loop_extra($tables$tablesinselect$extrainselect$selectparts)
  93.     {
  94.     }
  95.  
  96.     function parse_variable_extra($prefix$name)
  97.     {
  98.         return FALSE;
  99.     }
  100.     
  101.     function parse_before($contents)
  102.     {
  103.     }
  104.     
  105.     function parse_after($contents)
  106.     {
  107.     }
  108.     
  109.     function decode_loop_content_extra($balise$content$options$tables)
  110.     {
  111.     }
  112.  
  113.     function Parser()
  114.     // constructor
  115.         $this->commands = array ("USE""MACRO""FUNC""LOOP""IF""LET""ELSE""DO""DOFIRST""DOLAST""BEFORE""AFTER""ALTERNATIVE""ESCAPE""CONTENT""SWITCH""CASE");
  116.  
  117.         $this->codepieces = array ('sqlfetchassoc' => "mysql_fetch_assoc(%s)"'sqlquery' => "mysql_query(%s)"'sqlerror' => "or mymysql_error(%s,%s)"'sqlfree' => "mysql_free_result(%s)"'sqlnumrows' => "mysql_num_rows(%s)");
  118.     }
  119.  
  120.     function parse($in$out)
  121.     {
  122.         global $sharedir;
  123.  
  124.         $this->infilename = $in;
  125.         if (!file_exists($in))
  126.             $this->errmsg("Unable to read file $in");
  127.         $this->signature = preg_replace("/\W+/""_"$out);
  128.         $this->fct_txt = "";
  129.  
  130.         // read the file
  131.         if (!function_exists("file_get_contents")) {
  132.             $fp fopen($in"r");
  133.             while (!feof($fp))
  134.                 $file .= fread($fp1024);
  135.             fclose($fp);
  136.         }    else    {
  137.             $file file_get_contents($in);
  138.         }
  139.         $contents stripcommentandcr($file);
  140.  
  141.         $this->_split_file($contents)// split the contents into commands
  142.         $this->parse_main()// parse the commands
  143.  
  144.         if ($this->ind != $this->countarr)
  145.             $this->errmsg("this file contains more closing tags than opening tags");
  146.  
  147.         $contents join(""$this->arr)// recompose the file
  148.         unset ($this->arr)// save memory now.
  149.         $this->parse_after($contents)// user defined parse function
  150.  
  151.         // remove  <DEFMACRO>.*?</DEFMACRO>
  152.         $contents preg_replace("/<DEF(MACRO|FUNC)\b[^>]*>.*?<\/DEF(MACRO|FUNC)>\s*\n?/s"""$contents);
  153.  
  154.         if ($this->fct_txt)    {
  155.             $contents '<?php 
  156.             '.$this->fct_txt.'?>'.$contents;
  157.         }
  158.         //
  159.         // refresh manager
  160.         //
  161.         #die("::".$this->refresh);
  162.  
  163.         if ($this->refresh)    {
  164.             $code '<'.'?php if ($GLOBALS[cachedfile] && !$dontcheckrefresh) { $cachetime=filemtime($GLOBALS[cachedfile].".php"); ';
  165.  
  166.             // refresh period in second
  167.             if (preg_match("/^\d+$/"$this->refresh)) {
  168.                 $code .= ' if($cachetime+'.$this->refresh.'<time()) return "refresh"; ';
  169.                 // refresh time
  170.             }    else {
  171.                 $code .= '$now = time(); $date = getdate($now);';
  172.  
  173.                 $refreshtimes preg_split("/,/"$this->refresh);
  174.                 foreach ($refreshtimes as $refreshtime{
  175.                     $refreshtime preg_split("/:/"$refreshtime);
  176.                     $code .= '$refreshtime=mktime('.intval($refreshtime[0]).','.intval($refreshtime[1]).','.intval($refreshtime[2]).',$date[mon],$date[mday],$date[year]);';
  177.                     $code .= 'if ($cachetime<$refreshtime && $now>$refreshtime) return "refresh"; ';
  178.                 }
  179.             }
  180.             $code .= '} ?'.'>';
  181.             $contents '<'.'?php echo \''.quote_code($code).'\'; ?>
  182.             '.$contents;
  183.         }    elseif ($this->isphp)    {
  184.             $contents '<?php if ($GLOBALS[cachedfile]) echo \'<?php #--# ?>\'; ?>'.$contents// this is use to check if the output is a must be evaluated as a php or a raw file.
  185.         }
  186.  
  187.         // clean the open/close php tags
  188.         $contents preg_replace(array ('/\?><\?(php\b)?/''/<\?php[\s\n]*\?>/')array ("""")$contents);
  189.  
  190.         if (!$this->charset)
  191.             $this->charset = 'iso-8859-1';
  192.         if ($this->charset != 'utf-8')    {
  193.             #$t=microtime();
  194.             require_once TOINCLUDE'utf8.php'// conversion des caracteres
  195.             $contents utf8_encode($contents);
  196.             convertHTMLtoUTF8($contents);
  197.         }
  198.         @unlink($out)// detruit avant d'ecrire.
  199.         $fp fopen($out"w"or $this->errmsg("cannot write file $out");
  200.         fputs($fp$contents);
  201.         fclose($fp);
  202.         if ($GLOBALS['filemask'])
  203.             chmod($out0666 octdec($GLOBALS['filemask']));
  204.  
  205.         return $ret;
  206.     }
  207.  
  208.     function parse_variable($text$escape "php")
  209.     {
  210.         $i strpos($text"[");
  211.         while ($i !== false{
  212.             $startvar $i;
  213.             $i ++;
  214.             // parenthesis syntaxe [(
  215.             if ($text {$i}    == "("{
  216.                 $para true;
  217.                 $i ++;
  218.             }    else {
  219.                 $para false;
  220.             }
  221.  
  222.             if ($text {$i== "#" || strpos($text {$i }$this->variablechar!== false// 
  223.                 $varchar $text {$i};
  224.                 $i ++;
  225.                 // look for the name of the variable now
  226.                 if ($text {$i}    'A' || $text {$i}    'Z')
  227.                     continue// not a variable
  228.                 $varname $text {$i};
  229.                 $i ++;
  230.                 while (($text {$i}    >= 'A' && $text {$i}    <= 'Z'|| ($text {$i}    >= '0' && 
  231.                                 $text {$i}    <= '9'|| $text {$i}    == "_" || $text {$i}    == ".")    {
  232.                     $varname .= $text {$i};
  233.                     $i ++;
  234.                 }
  235.                 $pipefunction "";
  236.  
  237.                 if ($text {$i}    == ":")    // a lang
  238.                     $lang "";
  239.                     $i ++;
  240.                     while ($text {$i}    >= 'A' && $text {$i}    'Z')    {
  241.                         $lang .= $text {$i};
  242.                         $i ++;
  243.                     }
  244.                     $pipefunction '|multilingue("'.$lang.'")';
  245.                 }
  246.  
  247.                 if ($text {$i}    == "|")    // have a pipe function
  248.                     // look for the end of the variable
  249.                     $bracket 1;
  250.                     $mustnewparse false;
  251.                     while ($bracket{
  252.                         switch ($text {$i})    {
  253.                         case "[" :
  254.                             $bracket ++;
  255.                             $mustparse true// potentially a new variable
  256.                             break;
  257.                         case "]" :
  258.                             $bracket --;
  259.                             break;
  260.                         }
  261.                         if ($bracket 0)
  262.                             $pipefunction .= $text {$i};
  263.                         $i ++;
  264.                     }
  265.                     $i --// comes back to the bracket.
  266.                     if ($para && $pipefunction {strlen($pipefunction}    == ")")    {
  267.                         $pipefunction substr($pipefunction0-1);
  268.                         $i --;
  269.                     }
  270.                     if ($mustparse)    {
  271.                         $this->parse_variable($pipefunctionfalse);
  272.                     }
  273.                 }
  274.                 // look for a proper end of the variable
  275.                 if ($para && $text {$i}    == ")" && $text {$i +1}    == "]")    {
  276.                     $i += 2;
  277.                 }    elseif (!$para && $text {$i"]")    {
  278.                     $i ++;
  279.                 }
  280.                 else
  281.                     continue// not a variable
  282.  
  283.                 // build the variable code
  284.                 $varcode $this->_make_variable_code($varchar$varname$pipefunction$escape);
  285.                 $text substr_replace($text$varcode$startvar$i $startvar);
  286.                 $i $startvar +strlen($varcode)// move the counter
  287.             // we found a variable
  288.             $i strpos($text"["$i);
  289.         // while there are some variable
  290.     }
  291.  
  292.     function _make_variable_code($prefix$name$pipefunction$escape)
  293.     {
  294.         $variable $this->parse_variable_extra($prefix$name);
  295.         if ($variable === false// has the variable being processed ?     
  296.             $variable "\$context['".str_replace(".""']['"strtolower($name))."']";
  297.         }
  298.  
  299.         # parse the filter
  300.         if ($pipefunction// traitement particulier ?
  301.             //echo $pipefunction."<br />";
  302.             $array preg_split('/(?=\|[a-z]*[^\'|^\"]+)/',$pipefunction);
  303.  
  304.             foreach($array as $fct{
  305.             //foreach (explode("|", $pipefunction) as $fct)    {
  306.                 if($fct[0== '|'{
  307.                     $fct substr($fct1);
  308.                 }
  309.                 #foreach (preg_split ('/(?<!")(\|)(?!")/', $pipefunction) as $fct) {
  310.                 // note that explode is a little bit radical. It should be more advanced parser
  311.                 if ($fct == "false" || $fct == "true" || $fct == "else"{
  312.                     $fct .= "function";
  313.                 }
  314.                 if ($fct == "elsefunction"{
  315.                     $fct "falsefunction";
  316.                 }
  317.                 if ($fct)    {
  318.                     // get the args if any 
  319.                     if (preg_match("/^([A-Za-z][A-Za-z_0-9]*)\((.*?)\)$/"$fct$result))    {
  320.                         $args ','$result[2];
  321.                         $fct $result[1];
  322.                     }    elseif (preg_match("/^([A-Za-z][A-Za-z_0-9]*)$/"$fct)) {
  323.                         $args '';
  324.                     else {
  325.                         // error
  326.                         $this->errmsg("The name of the pipe function \"$fct\" is invalid");
  327.                     }
  328.                 }    else
  329.                     continue;
  330.                 //$variable = $fct. '('. $variable.$args. ')';
  331.                 //si la variable est contenue dans les arguments :
  332.                 $variable "$fct($variable$args)";
  333.             }
  334.         }
  335.  
  336.         switch ($escape{
  337.         case 'php' :
  338.             // traitement normal, php espace
  339.             $testcode ' echo '.$variable.';';
  340.             $code '<'.'?php '.$testcode.' ?'.'>';
  341.             break;
  342.         case 'quote' :
  343.             $code '".'.$variable.'."';
  344.             $testcode ' echo "'.$code.'";';
  345.             break;
  346.         default :
  347.             $code $variable;
  348.         }
  349.  
  350.         // unable to test the code.... 
  351.         // must use the PEAR::PHP_Parser
  352.  
  353.         return $code;
  354.     }
  355.  
  356.     function countlines($ind)
  357.     {
  358.         if ($ind == 0{
  359.             $this->currentline += substr_count($this->arr[$ind]"\n");
  360.         }    else {
  361.             $this->linearr[$ind$this->currentline;
  362.             $this->currentline += substr_count($this->arr[$ind +1]"\n"substr_count($this->arr[$ind +2]"\n");
  363.         }
  364.     }
  365.  
  366.     function parse_main()
  367.     {    
  368.         while ($this->ind < $this->countarr{
  369.             switch ($this->arr[$this->ind])    {
  370.             case 'CONTENT' :
  371.                 $attrs $this->_decode_attributs($arr[$this->ind + 1]);
  372.                 $this->charset = $attrs['CHARSET'$attrs['CHARSET'"iso-8859-1";
  373.                 // attribut refresh
  374.                 $this->_checkforrefreshattribut($attrs);
  375.                 $this->_clearposition();
  376.                 break;
  377.             case 'USE' :
  378.                 $siteroot defined('SITEROOT'SITEROOT '';
  379.                 $attrs $this->_decode_attributs($this->arr[$this->ind + 1]);
  380.                 if ($attrs['MACROFILE']{
  381.                     $macrofilename $attrs['MACROFILE'];
  382.                     if (file_exists("tpl/".$macrofilename))    {
  383.                         $contents file_get_contents("tpl/".$macrofilename);
  384.                     }    elseif ($GLOBALS['sharedir'&&     file_exists($siteroot.$GLOBALS['sharedir']."/macros/".$macrofilename)) {
  385.                         $contents file_get_contents($siteroot.$GLOBALS['sharedir']."/macros/".$macrofilename);
  386.                     }    elseif (file_exists($GLOBALS['home']."../tpl/".$macrofilename))    {
  387.                         $contents file_get_contents($GLOBALS['home']."../tpl/".$macrofilename);
  388.                     }    else {
  389.                         $this->errmsg("the macro file \"$macrofilename\" doesn't exist");
  390.                     }
  391.                     $this->macros_txt .= stripcommentandcr($contents);
  392.                     $this->_clearposition();
  393.                 elseif ($attrs['TEMPLATEFILE'])    {
  394.                     $this->_clearposition();
  395.                     $this->arr[$this->ind'<?php insert_template($context,"'.basename($attrs['TEMPLATEFILE']).'"); ?>';
  396.                 }
  397.                 break;
  398.                     // returns
  399.             case 'ELSE' :
  400.             case 'DO' :
  401.             case 'DOFIRST' :
  402.             case 'DOLAST' :
  403.             case 'AFTER' :
  404.             case 'BEFORE' :
  405.             case 'ALTERNATIVE' :
  406.             case 'CASE' :
  407.                 return;
  408.             case '/MACRO' :
  409.             case '/FUNC' :
  410.                 $this->_clearposition();
  411.                 break;
  412.             default :
  413.                 if ($this->arr[$this->ind]{0}    == '/')    {
  414.                     // closing tag ?
  415.                     if ($this->arr[$this->ind + 1])
  416.                         $this->errmsg("The closing tag ".$this->arr[$this->ind]." is malformed");
  417.                     return;
  418.                 }    else {
  419.                     $methodname 'parse_'$this->arr[$this->ind];
  420.                     if (method_exists($this$methodname)) {
  421.                         $this->$methodname ();
  422.                         #call_user_func(array(&$this,$methodname));
  423.