<?php

/**
 * The template parser class.
 * @package zpanelx
 * @subpackage dryden -> ui
 * @version 1.0.0
 * @author Bobby Allen (ballen@bobbyallen.me)
 * @copyright ZPanel Project (http://www.zpanelcp.com/)
 * @link http://www.zpanelcp.com/
 * @license GPL (http://www.gnu.org/licenses/gpl.html)
 */
class ui_templateparser
{

    /**
     * Array of all the functions allowed by ZPanelX Template system with the pattern to identify them
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static public $Functions = array(
        'PHPTags' => "/(<\?php)|(<\?)|(\?>)/is",
        'TemplateClass' => "/<# ([\w*_*]*) #>/is",
        'FunctionEcho' => "/<@\s([\w*]*)\s@>/is",
        'Lanuage' => "/<:\s([^>\]\"<\?>]*)\s:>/is",
        'EndLoop' => "/<%\s(endloop)\s%>/is",
        'Loop' => "/<%\sloop\s(\w*)\s%>/is",
        'EchoLoop' => "/<&\s([\w*]*)\s&>/is",
        'If' => " /<%\sif\s([\w*-*_*]*)\s%>/is",
        'Else' => "/<%\s(else)\s%>/is",
        'EndIf' => "/<%\s(endif)\s%>/is"
    );

    /**
     * Location of the cached files relative to root
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static public $storageLocation = 'etc/tmp/storage/';

    /**
     * 1 in X change on page load to check for old cached files for deletetion
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static public $deleteCacheChance = 40;

    /**
     * use eval or file cache for the template loading
     * @author Sam Mottley (smottley@zpanelcp.com)
     * @var 1=file cache 0=eval
     */
    static public $evalOrCache = 1;

    /**
     * Runs though the functions array and loads the relivent function compiler
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static function CompileFunctions($data)
    {
        $temp = $data;
        runtime_hook::Execute('OnBeforeTemplateProcessor');
        foreach (ui_templateparser::$Functions as $Tag => $pattern) {
            $temp = call_user_func_array('ui_templateparser::Compile' . $Tag, array($pattern, $temp));
        }
        runtime_hook::Execute('OnAfterTemplateProcessor');
        return $temp;
    }

    /**
     * Removes any php tags
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompilePHPTags($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if (($match) && (!empty($match[1]))) {
            $i = 0;
            foreach ($match[1] as $string) {
                if ($match[0][$i] == '?>') {
                    $data = str_replace($match[0][$i], ']', $data);
                } else {
                    $data = str_replace($match[0][$i], 'PHP execution is not permitted! Caught: [', $data);
                }
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX loop template tags into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileLoop($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if (($match) && (!empty($match[1]))) {
            $i = 0;
            foreach ($match[1] as $function) {
                $data = str_replace($match[0][$i], '<?php foreach(module_controller::get' . $function . '() as $key => $value){ ?>', $data);
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX echo loop template tags into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileEchoLoop($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if (($match) && (!empty($match[1]))) {
            $i = 0;
            foreach ($match[1] as $key) {
                $data = str_replace($match[0][$i], '<?php echo $value[\'' . $key . '\']; ?>', $data);
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX end loop template tags into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileEndLoop($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if (($match) && (!empty($match[1]))) {
            $i = 0;
            foreach ($match[1] as $string) {
                $data = str_replace($match[0][$i], '<?php } ?>', $data);
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX if template tags into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileIf($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if (($match) && (!empty($match[1]))) {
            $i = 0;
            foreach ($match[1] as $function) {
                $data = str_replace($match[0][$i], '<?php if(module_controller::get' . $function . '()){ ?>', $data);
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX end if template tags into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileEndIf($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if ($match) {
            $i = 0;
            foreach ($match[1] as $string) {
                $data = str_replace($match[0][$i], '<?php } ?>', $data);
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX lanuage template tags into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileLanuage($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if ($match) {
            $i = 0;
            foreach ($match[1] as $string) {
                $output = ui_language::translate(addslashes($string));
                $data = str_replace($match[0][$i], "<?php echo ui_language::translate('" . addslashes($string) . "');?>", $data);
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX function each template tags into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileFunctionEcho($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if ($match) {
            $i = 0;
            foreach ($match[1] as $classes) {
                $method_name = "get" . $classes;
                $output = module_controller::$method_name();
                $data = str_replace($match[0][$i], "<?php echo module_controller::" . $method_name . "(); ?>", $data);
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX end if template tags into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileElse($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if ($match) {
            $i = 0;
            foreach ($match[1] as $string) {
                $data = str_replace($match[0][$i], '<?php }else{ ?>', $data);
                $i++;
            }
        }
        return $data;
    }

    /**
     * Compiles ZPanelX template class tag into valid PHP
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static private function CompileTemplateClass($value, $data)
    {
        $match = null;
        preg_match_all($value, $data, $match);
        if ($match) {
            $i = 0;
            foreach ($match[1] as $classes) {
                if (class_exists('' . $classes . '')) {
                    $moduleTemplate = call_user_func(array($classes, 'Template'));
                    $codeToInsert = ui_templateparser::CompileFunctions($moduleTemplate);
                    $data = str_replace($match[0][$i], $codeToInsert, $data);
                }
                $i++;
            }
        }
        return $data;
    }

    /**
     * Check the cache file for presents and valid/upto date Data
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static function CheckFileCache($phpCode)
    {
        $currentCode = $phpCode;

        //Get the users current theme
        $userDetails = ctrl_users::GetUserDetail();
        $userTheme = $userDetails['usertheme'];

        //The location of the cached php
        if (isset($_GET['module'])) {
            $location = ui_templateparser::$storageLocation . $userTheme . '/' . md5(fs_protector::SanitiseFolderName($_GET['module'])) . '.cache';
        } else {
            $location = ui_templateparser::$storageLocation . $userTheme . '/' . md5('index') . '.cache';
        }

        //check folder exists (First load of 10.1.0 and on new theme)
        $dirname = dirname($location);
        if (!is_dir($dirname)) {
            mkdir($dirname, 0755, true);
        }

        //check file exsists if not make, insert code else just get current code
        if (file_exists($location)) {
            $content = file_get_contents($location);
        } else {
            $handle = fopen($location, 'w');
            file_put_contents($location, $phpCode, LOCK_EX);
            $content = $phpCode;
        }

        //check the file content is the same as the generated content then return file location
        if ($currentCode == $content) {
            return $location;
        } else {
            file_put_contents($location, $phpCode, LOCK_EX);
            return $location;
        }
    }

    /**
     * Check the cache for very old files and clear them
     * @var deathAfter the number of seconds old a cache file can be deafult 7 days
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static function clearOldCache($deathAfter = '604800')
    {
        //dont check every time!
        $rollDice = rand(1, ui_templateparser::$deleteCacheChance);

        //1 in X chance of checking all files
        if ($rollDice == 1) {
            //get all files and folders in storage location
            $contents = glob(ui_templateparser::$storageLocation . "*");

            //loop each file and folder
            foreach ($contents as $content) {
                //Is folder
                if (is_dir($content)) {
                    $cacheFilesArray = glob($content . "/*");
                    foreach ($cacheFilesArray as $cacheFile) {
                        if (!is_dir($cacheFile)) {
                            $time = time() - $deathAfter;
                            if (filemtime($cacheFile) <= $time) {
                                //Delete cache files
                                $path = pathinfo($cacheFile);
                                chdir($path['dirname']);
                                unlink($path['filename'] . '.cache');
                            }
                        }
                    }
                }
            }
            chdir('/');
        }
    }

    /**
     * Set the root of the temp path location
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static public function setLocation()
    {
        self::$storageLocation = ctrl_options::GetSystemOption('zpanel_root') . self::$storageLocation;
    }

    /**
     * Run the php code and return it
     * @var code the php code
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static function runPHP($template_code)
    {
        //load from DB or set a default
        $evalOrCache = ui_templateparser::$evalOrCache;

        //selected eval or file cache
        if ($evalOrCache == 1) {
            $fileLocation = ui_templateparser::CheckFileCache($template_code);
            return include($fileLocation);
        } else {
            return eval('?>' . $template_code);
        }
    }

    /**
     * All br tags to be used using the zpanel br tag loader
     * @author Sam Mottley (smottley@zpanelcp.com)
     */
    static function allowBr($templateCode)
    {
        return str_replace("ZP(br)", '<br/>', $templateCode);
    }

    /**
     * Loads in the template content and parses it to compute the place holder content.
     * @author Bobby Allen (ballen@bobbyallen.me)
     * @param string $template_path The full path to the system template (or user template).
     * @return sting The processed template HTML.
     */
    static function Generate($template_path)
    {
        self::setLocation();
        $template_raw = file_get_contents($template_path . "/master.ztml");
        $template_code = ui_templateparser::allowBr(ui_templateparser::CompileFunctions($template_raw));
        ui_templateparser::clearOldCache();
        return ui_templateparser::runPHP($template_code);
    }

}

?>