<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license,      |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Alan Knowles <alan@akbkhome.com>                            |
// +----------------------------------------------------------------------+
//
// $Id: Standard.php 334846 2014-09-12 04:50:56Z alan_k $
//
//  Base Compiler Class
//  Standard 'Original Flavour' Flexy compiler

/*------------------------------------------------------------------------------------------
                                        NOTICE: 
                             THIS COMPILER IS DEPRECIATED 
                                USE THE FLEXY COMPILER 
     
                      The Flexy Compiler should be Compatible

------------------------------------------------------------------------------------------*/

require_once 'HTML/Template/Flexy/Tokenizer.php';
require_once 'HTML/Template/Flexy/Token.php';

// cache for po files..
$GLOBALS['_html_template_flexy_compiler_standard']['PO'] = array();


class HTML_Template_Flexy_Compiler_Standard extends HTML_Template_Flexy_Compiler 
{
    
    
    var $flexy;
    /**
    * The compile method.
    * 
    * @params   object HTML_Template_Flexy
    * @params   string|false string to compile of false to use a file.
    * @return   string   filename of template
    * @access   public
    */
    function compile($flexy,$string=false) 
    {
        // read the entire file into one variable
        
        // note this should be moved to new HTML_Template_Flexy_Token
        // and that can then manage all the tokens in one place..
        global $_HTML_TEMPLATE_FLEXY_COMPILER;
        
        $this->flexy = $flexy;
        
        $gettextStrings = &$_HTML_TEMPLATE_FLEXY_COMPILER['gettextStrings'];
        $gettextStrings = array(); // reset it.
        
        if (@$this->options['debug']) {
            echo "compiling template $flexy->currentTemplate<BR>";
            
        }
         
        // reset the elements.
        $flexy->_elements = array();
        
        // replace this with a singleton??
        
        $GLOBALS['_HTML_TEMPLATE_FLEXY']['currentOptions'] = $this->options;
        $GLOBALS['_HTML_TEMPLATE_FLEXY']['elements'] = array();
        $GLOBALS['_HTML_TEMPLATE_FLEXY']['filename'] = $flexy->currentTemplate;
        $GLOBALS['_HTML_TEMPLATE_FLEXY']['prefixOutput']  = '';
        $GLOBALS['_HTML_TEMPLATE_FLEXY']['compiledTemplate']  = $flexy->compiledTemplate;
        
        if (is_array($this->options['Translation2'])) {
            require_once 'Translation2.php';
            $this->options['Translation2'] = new Translation2(
                $this->options['Translation2']['driver'],
                @$this->options['Translation2']['options']
            );
        }
        
        
        if ($this->is_a($this->options['Translation2'],'Translation2')) {
            $this->options['Translation2']->setLang($this->options['locale']);
            // fixme - needs to be more specific to which template to use..
            foreach ($this->options['templateDir'] as $tt) {
                $n = basename($flexy->currentTemplate);
                if (substr($flexy->currentTemplate,0,strlen($tt)) == $tt) {
                    $n = substr($flexy->currentTemplate,strlen($tt)+1);
                }
                //echo $n;
            }
            $this->options['Translation2']->setPageID($n);
        } else {
            setlocale(LC_ALL, $this->options['locale']);
        }
        
        
        
        
        
        
        
        $data = $string;
        $res = false;
        if ($string === false) {
            $data = file_get_contents($flexy->currentTemplate);
        }
         
            // PRE PROCESS {_(.....)} translation markers.
        $got_gettext_markup = false;
        
        
        
        if (strpos($data,'{_(') !== false) {
            $matches = array();
            $lmatches = explode ('{_(', $data);
            array_shift($lmatches);
            // shift the first..
            foreach ($lmatches as $k) {
                if (false === strpos($k,')_}')) {
                    continue;
                }
                $x = explode(')_}',$k);
                $matches[] = $x[0];
            }
        
        
           //echo '<PRE>';print_r($matches);
            // we may need to do some house cleaning here...
            $gettextStrings = $matches;
            $got_gettext_markup = true;
            
            
            // replace them now..  
            // ** leaving in the tag (which should be ignored by the parser..
            // we then get rid of the tags during the toString method in this class.
            foreach($matches as $v) {
                $data = str_replace('{_('.$v.')_}', '{_('.$this->flexy->translateString($v).')_}',$data);
            }
            
        }
            
        if (isset($_HTML_TEMPLATE_FLEXY_COMPILER['cache'][md5($data)])) {
            $res = $_HTML_TEMPLATE_FLEXY_COMPILER['cache'][md5($data)];
        } else {
        
             
            $tokenizer = new HTML_Template_Flexy_Tokenizer($data);
            $tokenizer->fileName = $flexy->currentTemplate;
            
            
            //$tokenizer->debug=1;
            $tokenizer->options['ignore_html'] = $this->options['nonHTML'];
            $tokenizer->options['ignore_php']  = !$this->options['allowPHP'];
            
            $res = HTML_Template_Flexy_Token::buildTokens($tokenizer);
         
            $_HTML_TEMPLATE_FLEXY_COMPILER['cache'][md5($data)] = $res;
            
        }
        
        if ($this->is_a($res,'PEAR_Error')) {
            return $res;
        }   
        // turn tokens into Template..
        
        $data = $res->compile($this);
        
        if ($this->is_a($data,'PEAR_Error')) {
            return $data;
        }
        
        $data = $GLOBALS['_HTML_TEMPLATE_FLEXY']['prefixOutput'] . $data;
        
        if (   @$this->options['debug']) {
            echo "<B>Result: </B><PRE>".htmlspecialchars($data)."</PRE><BR>";
            
        }

        if ($this->options['nonHTML']) {
           $data =  str_replace("?>\n","?>\n\n",$data);
        }
        
         
        
        
        // at this point we are into writing stuff...
        if ($this->options['compileToString']) {
            $flexy->elements =  $GLOBALS['_HTML_TEMPLATE_FLEXY']['elements'];
            return $data;
        }
        
        
        
        
        // error checking?
        $file  = $flexy->compiledTemplate;
        if (isset($flexy->options['output.block'])) {
            list($file,$part) = explode('#',$file  );
        }
        
        if( ($cfp = fopen( $file , 'w' )) ) {
            if (@$this->options['debug']) {
                echo "<B>Writing: </B>".htmlspecialchars($data)."<BR>";
                
            }
            fwrite($cfp,$data);
            fclose($cfp);
            
            chmod($file,0775);
            // make the timestamp of the two items match.
            clearstatcache();
            touch($file, filemtime($flexy->currentTemplate));
            if ($file != $flexy->compiledTemplate) {
                chmod($flexy->compiledTemplate,0775);
                // make the timestamp of the two items match.
                clearstatcache();
                touch($flexy->compiledTemplate, filemtime($flexy->currentTemplate));
            }
             
            
        } else {
            return HTML_Template_Flexy::staticRaiseError('HTML_Template_Flexy::failed to write to '.$flexy->compiledTemplate,
                HTML_TEMPLATE_FLEXY_ERROR_FILE ,HTML_TEMPLATE_FLEXY_ERROR_RETURN);
        }
        // gettext strings
        if (file_exists($flexy->getTextStringsFile)) {
            unlink($flexy->getTextStringsFile);
        }
        
        if($gettextStrings && ($cfp = fopen( $flexy->getTextStringsFile, 'w') ) ) {
            
            fwrite($cfp,serialize(array_unique($gettextStrings)));
            fclose($cfp);
        }
        
        // elements
        if (file_exists($flexy->elementsFile)) {
            unlink($flexy->elementsFile);
        }
        
        if( $GLOBALS['_HTML_TEMPLATE_FLEXY']['elements'] &&
            ($cfp = fopen( $flexy->elementsFile, 'w') ) ) {
            fwrite($cfp,serialize( $GLOBALS['_HTML_TEMPLATE_FLEXY']['elements']));
            fclose($cfp);
            // now clear it.
        
        }
        
        return true;
    }
    
    /**
    * Flag indicating compiler is inside {_( .... )_} block, and should not
    * add to the gettextstrings array.
    *
    * @var boolean 
    * @access public
    */
    var $inGetTextBlock = false;
    
    /**
    * This is the base toString Method, it relays into toString{TokenName}
    *
    * @param    object    HTML_Template_Flexy_Token_*
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  

    function toString($element) 
    {
        static $len = 26; // strlen('HTML_Template_Flexy_Token_');
        if ($this->options['debug']) {
            $x = $element;
            unset($x->children);
            echo htmlspecialchars(print_r($x,true))."<BR>\n";
        }
        if ($element->token == 'GetTextStart') {
            $this->inGetTextBlock = true;
            return '';
        }
        if ($element->token == 'GetTextEnd') {
            $this->inGetTextBlock = false;
            return '';
        }
        
            
        $class = get_class($element);
        if (strlen($class) >= $len) {
            $type = substr($class,$len);
            return $this->{'toString'.$type}($element);
        }
        
        $ret = $element->value;
        $add = $element->compileChildren($this);
        if ($this->is_a($add,'PEAR_Error')) {
            return $add;
        }
        $ret .= $add;
        
        if ($element->close) {
            $add = $element->close->compile($this);
            if ($this->is_a($add,'PEAR_Error')) {
                return $add;
            }
            $ret .= $add;
        }
        
        return $ret;
    }


    /**
    *   HTML_Template_Flexy_Token_Else toString 
    *
    * @param    object    HTML_Template_Flexy_Token_Else
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  

    function toStringElse($element) 
     {
        // pushpull states to make sure we are in an area.. - should really check to see 
        // if the state it is pulling is a if...
        if ($element->pullState() === false) {
            return $this->appendHTML(
                "<font color=\"red\">Unmatched {else:} on line: {$element->line}</font>"
                );
        }
        $element->pushState();
        return $this->appendPhp("} else {");
    }
    
    /**
    *   HTML_Template_Flexy_Token_End toString 
    *
    * @param    object    HTML_Template_Flexy_Token_Else
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  
    function toStringEnd($element) 
    {
        // pushpull states to make sure we are in an area.. - should really check to see 
        // if the state it is pulling is a if...
        if ($element->pullState() === false) {
            return $this->appendHTML(
                "<font color=\"red\">Unmatched {end:} on line: {$element->line}</font>"
                );
        }
         
        return $this->appendPhp("}");
    }

    /**
    *   HTML_Template_Flexy_Token_EndTag toString 
    *
    * @param    object    HTML_Template_Flexy_Token_EndTag
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  


    function toStringEndTag($element) 
    {
        return $this->toStringTag($element);
    }
        
    
    
    /**
    *   HTML_Template_Flexy_Token_Foreach toString 
    *
    * @param    object    HTML_Template_Flexy_Token_Foreach 
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  
    
    function toStringForeach($element) 
    {
    
        $loopon = $element->toVar($element->loopOn);
        if ($this->is_a($loopon,'PEAR_Error')) {
            return $loopon;
        }
        
        $ret = 'if ($this->options[\'strict\'] || ('.
            'is_array('. $loopon. ')  || ' .
            'is_object(' . $loopon  . '))) ' .
            'foreach(' . $loopon  . " ";
            
        $ret .= "as \${$element->key}";
        
        if ($element->value) {
            $ret .=  " => \${$element->value}";
        }
        $ret .= ") {";
        
        $element->pushState();
        $element->pushVar($element->key);
        $element->pushVar($element->value);
        return $this->appendPhp($ret);
    }
    /**
    *   HTML_Template_Flexy_Token_If toString 
    *
    * @param    object    HTML_Template_Flexy_Token_If
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  
    function toStringIf($element) 
    {
        
        $var = $element->toVar($element->condition);
        if ($this->is_a($var,'PEAR_Error')) {
            return $var;
        }
        
        $ret = "if (".$element->isNegative . $var .")  {";
        $element->pushState();
        return $this->appendPhp($ret);
    }

   /**
    *  get Modifier Wrapper 
    *
    * converts :h, :u, :r , .....
    * @param    object    HTML_Template_Flexy_Token_Method|Var
    * 
    * @return   array prefix,suffix
    * @access   public 
    * @see      toString*
    */

    function getModifierWrapper($element) 
    {
        $prefix = 'echo ';
        
        $suffix = '';
        $modifier = strlen(trim($element->modifier)) ? $element->modifier : ' ';
        
        switch ($modifier{0}) {
            case 'h':
                break;
            case 'u':
                $prefix = 'echo urlencode(';
                $suffix = ')';
                break;
            case 'r':
                $prefix = 'echo \'<pre>\'; echo htmlspecialchars(print_r(';
                $suffix = ',true)); echo \'</pre>\';';
                break;                
            case 'n': 
                // blank or value..
                $numberformat = @$GLOBALS['_HTML_TEMPLATE_FLEXY']['currentOptions']['numberFormat'];
                $prefix = 'echo number_format(';
                $suffix = $GLOBALS['_HTML_TEMPLATE_FLEXY']['currentOptions']['numberFormat'] . ')';
                break;
            case 'b': // nl2br + htmlspecialchars
                $prefix = 'echo nl2br(htmlspecialchars(';
                
                // add language ?
                $suffix = '))';
                break;
            case ' ':
                $prefix = 'echo htmlspecialchars(';
                // add language ?
                $suffix = ')';
                break;
            default:
               $prefix = 'echo $this->plugin("'.trim($element->modifier) .'",';
               $suffix = ')'; 
            
            
        }
        
        return array($prefix,$suffix);
    }



  /**
    *   HTML_Template_Flexy_Token_Var toString 
    *
    * @param    object    HTML_Template_Flexy_Token_Method
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  
    function toStringVar($element) 
    {
        // ignore modifier at present!!
        
        $var = $element->toVar($element->value);
        if ($this->is_a($var,'PEAR_Error')) {
            return $var;
        }
        list($prefix,$suffix) = $this->getModifierWrapper($element);
        return $this->appendPhp( $prefix . $var . $suffix .';');
    }
   /**
    *   HTML_Template_Flexy_Token_Method toString 
    *
    * @param    object    HTML_Template_Flexy_Token_Method
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  
    function toStringMethod($element) 
    {

              
        // set up the modifier at present!!
        list($prefix,$suffix) = $this->getModifierWrapper($element);
        
        // add the '!' to if
        
        if ($element->isConditional) {
            $prefix = 'if ('.$element->isNegative;
            $element->pushState();
            $suffix = ')';
        }  
        
        
        // check that method exists..
        // if (method_exists($object,'method');
        $bits = explode('.',$element->method);
        $method = array_pop($bits);
        
        $object = implode('.',$bits);
        
        $var = $element->toVar($object);
        if ($this->is_a($var,'PEAR_Error')) {
            return $var;
        }
        
        if (($object == 'GLOBALS') && 
            $GLOBALS['_HTML_TEMPLATE_FLEXY']['currentOptions']['globalfunctions']) {
            // we should check if they something weird like: GLOBALS.xxxx[sdf](....)
            $var = $method;
        } else {
            $prefix = 'if ($this->options[\'strict\'] || (isset('.$var.
                ') && method_exists('.$var .",'{$method}'))) " . $prefix;
            $var = $element->toVar($element->method);
        }
        

        if ($this->is_a($var,'PEAR_Error')) {
            return $var;
        }
        
        $ret  =  $prefix;
        $ret .=  $var . "(";
        $s =0;
         
       
         
        foreach($element->args as $a) {
             
            if ($s) {
                $ret .= ",";
            }
            $s =1;
            if ($a{0} == '#') {
                $ret .= '"'. addslashes(substr($a,1,-1)) . '"';
                continue;
            }
            
            $var = $element->toVar($a);
            if ($this->is_a($var,'PEAR_Error')) {
                return $var;
            }
            $ret .= $var;
            
        }
        $ret .= ")" . $suffix;
        
        if ($element->isConditional) {
            $ret .= ' { ';
        } else {
            $ret .= ";";
        }
        
        
        
        return $this->appendPhp($ret); 
        
         

   }
   /**
    *   HTML_Template_Flexy_Token_Processing toString 
    *
    * @param    object    HTML_Template_Flexy_Token_Processing 
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */


    function toStringProcessing($element) 
    {
        // if it's XML then quote it..
        if (strtoupper(substr($element->value,2,3)) == 'XML') { 
            return $this->appendPhp("echo '" . str_replace("'","\\"."'", $element->value) . "';");
        }
        // otherwise it's PHP code - so echo it..
        return $element->value;
    }
    
    /**
    *   HTML_Template_Flexy_Token_Text toString 
    *
    * @param    object    HTML_Template_Flexy_Token_Text
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */



    function toStringText($element) 
    {
        
        // first get rid of stuff thats not translated etc.
        // empty strings => output.
        // comments -> just output
        // our special tags -> output..
        
        if (!strlen(trim($element->value) )) {
            return $this->appendHtml($element->value);
        }
        // dont add comments to translation lists.
         
        if (substr($element->value,0,4) == '<!--') {
            return $this->appendHtml($element->value);
        }
        // ignore anything wrapped with {_( .... )_}
        if ($this->inGetTextBlock) {
            return $this->appendHtml($element->value);
        }
        
        // argTokens is built before the tag matching (it combined
        // flexy tags into %s, into the string,
        // and made a list of tokens in argTokens.
        
        if (!count($element->argTokens) && !$element->isWord()) {
            return $this->appendHtml($element->value);
        }
        
        // grab the white space at start and end (and keep it!
        
        $value = ltrim($element->value);
        $front = substr($element->value,0,-strlen($value));
        $value = rtrim($element->value);
        $rear  = substr($element->value,strlen($value));
        $value = trim($element->value);
        
        
        // convert to escaped chars.. (limited..)
        //$value = strtr($value,$cleanArray);
        
        $this->addStringToGettext($value);
        $value = $this->flexy->translateString($value);
        // its a simple word!
        if (!count($element->argTokens)) {
            return $this->appendHtml($front . $value . $rear);
        }
        
        
        // there are subtokens..
        // print_r($element->argTokens );
        $args = array();
        // these should only be text or vars..
        
        foreach($element->argTokens as $i=>$token) {
            $args[] = $token->compile($this);
        }
        
        // we break up the translated string, and put the compiled tags 
        // in between the values here.
        
        $bits = explode('%s',$value);
        $ret  = $front;
        
        foreach($bits as $i=>$v) {
            $ret .= $v . @$args[$i];
        }
        
        return  $ret . $rear;
        
    }
    /**
    * addStringToGettext 
    *
    * Adds a string to the gettext array. 
    * 
    * @param   mixed        preferably.. string to store
    *
    * @return   none
    * @access   public
    */
    
    function addStringToGettext($string) 
    {
    
        
        
        
        if (!is_string($string)) {
            return;
        }
        
        if (!preg_match('/[a-z]+/i', $string)) {
            return;
        }
        $string = trim($string);
        
        if (substr($string,0,4) == '<!--') {
            return;
        }
        
        $GLOBALS['_HTML_TEMPLATE_FLEXY_COMPILER']['gettextStrings'][] = $string;
    }
    
     
     /**
    *   HTML_Template_Flexy_Token_Tag toString 
    *
    * @param    object    HTML_Template_Flexy_Token_Tag
    * 
    * @return   string     string to build a template
    * @access   public 
    * @see      toString*
    */
  
    function toStringTag($element) {
        if (strpos($element->tag,':') === false) {
            $namespace = 'Tag';
        } else {
            $bits =  explode(':',$element->tag);
            $namespace = $bits[0];
        }
        if ($namespace{0} == '/') {
            $namespace = substr($namespace,1);
        }
        if (empty($this->tagHandlers[$namespace])) {
            
            require_once 'HTML/Template/Flexy/Compiler/Standard/Tag.php';
            $this->tagHandlers[$namespace] = &HTML_Template_Flexy_Compiler_Standard_Tag::factory($namespace,$this);
            if (!$this->tagHandlers[$namespace] ) {
                return HTML_Template_Flexy::staticRaiseError('HTML_Template_Flexy::failed to create Namespace Handler '.$namespace . 
                    ' in file ' . $GLOBALS['_HTML_TEMPLATE_FLEXY']['filename'],
                    HTML_TEMPLATE_FLEXY_ERROR_SYNTAX ,HTML_TEMPLATE_FLEXY_ERROR_RETURN);
            }
                
        }
        return $this->tagHandlers[$namespace]->toString($element);
        
        
    }
    

}
