'strtoupper', 'lower' => 'strtolower', 'escxml' => 'htmlspecialchars', 'escape' => 'Pluf_Template_htmlspecialchars', 'strip_tags' => 'strip_tags', 'escurl' => 'rawurlencode', 'capitalize' => 'ucwords', // Not var_export because of recursive issues. 'debug' => 'print_r', 'fulldebug' => 'var_export', 'count' => 'count', 'nl2br' => 'nl2br', 'trim' => 'trim', 'unsafe' => 'Pluf_Template_unsafe', 'safe' => 'Pluf_Template_unsafe', 'date' => 'Pluf_Template_dateFormat', 'time' => 'Pluf_Template_timeFormat', ); /** * After the compilation is completed, this contains the list of * modifiers used in the template. The GetCompiledTemplate method * will add a series of Pluf::loadFunction at the top to preload * these modifiers. */ public $_usedModifiers = array(); /** * Default allowed extra tags/functions. * * These default tags are merged with the 'template_tags' defined * in the configuration of the application. */ protected $_allowedTags = array( 'url' => 'Pluf_Template_Tag_Url', ); /** * During compilation, all the tags are created once so to query * their interface easily. */ protected $_extraTags = array(); /** * The block stack to see if the blocks are correctly closed. */ protected $_blockStack = array(); /** * Special stack for the translation handling in blocktrans. */ protected $_transStack = array(); protected $_transPlural = false; /** * Current template source file. */ protected $_sourceFile; /** * Current tag. */ protected $_currentTag; /** * Template folders. */ public $templateFolders = array(); /** * Template content. It can be set directly from a string. */ public $templateContent = ''; /** * Construct the compiler. * * @param string Basename of the template file. * @param array Base folders in which the templates files * should be found. (array()) * @param bool Load directly the template content. (true) */ function __construct($template_file, $folders=array(), $load=true) { $allowedtags = Pluf::f('template_tags', array()); $this->_allowedTags = array_merge($allowedtags, $this->_allowedTags); $modifiers = Pluf::f('template_modifiers', array()); $this->_modifier = array_merge($modifiers, $this->_modifier); foreach ($this->_allowedTags as $name=>$model) { $this->_extraTags[$name] = new $model(); } $this->_sourceFile = $template_file; $this->_allowedInVar = array_merge($this->_vartype, $this->_op); $this->_allowedInExpr = array_merge($this->_vartype, $this->_op); $this->_allowedAssign = array_merge($this->_vartype, $this->_assignOp, $this->_op); $this->templateFolders = $folders; if ($load) { $this->loadTemplateFile($template_file); } } /** * Get blocktrans. */ function getBlockTrans() { $tplcontent = $this->templateContent; $tplcontent = preg_replace('!{\*(.*?)\*}!s', '', $tplcontent); $match = array(); preg_match_all('!{blocktrans(.*?){/blocktrans}!s', $tplcontent, $match); $res = array(); foreach ($match[1] as $m) { $res[] = '{blocktrans'.$m.'{/blocktrans}'; } return $res; } /** * Get simple trans call. */ function getSimpleTrans() { $tplcontent = $this->templateContent; $tplcontent = preg_replace('!{\*(.*?)\*}!s', '', $tplcontent); $match = array(); preg_match_all('!{trans(.*?)}!s', $tplcontent, $match); $res = array(); foreach ($match[1] as $m) { $res[] = '{trans'.$m.'}'; } return $res; } /** * Compile the template into a file ready to parse with xgettext. * * @return string PHP code of the compiled template. */ function compile() { $result = ''; $blocktrans = $this->getBlockTrans(); // Parse the blocktrans foreach ($blocktrans as $block) { $match = array(); if (preg_match('!{blocktrans(.*?)}(.*?){plural}(.*?){/blocktrans}!s', $block, $match)) { $sing = $match[2]; $plural = $match[3]; $sing = preg_replace_callback('/{((.).*?)}/s', array($this, '_callbackInTransBlock'), $sing); $plural = preg_replace_callback('/{((.).*?)}/s', array($this, '_callbackInTransBlock'), $plural); $result .= '_n(\''.addcslashes($sing, "'").'\', \''.addcslashes($plural, "'").'\', $n);'."\n"; } elseif (preg_match('!{blocktrans}(.*?){/blocktrans}!s', $block, $match)) { $sing = preg_replace_callback('/{((.).*?)}/s', array($this, '_callbackInTransBlock'), $match[1]); $result .= '__(\''.addcslashes($sing, "'").'\');'."\n"; } } $simpletrans = $this->getSimpleTrans(); foreach ($simpletrans as $content) { $result .= preg_replace_callback('/{((.).*?)}/s', array($this, '_callback'), $content); } return ''; } /** * Load a template file. * * The path to the file to load is relative and the file is found * in one of the $templateFolders array of folders. * * @param string Relative path of the file to load. */ function loadTemplateFile($file) { // FIXME: Very small security check, could be better. if (strpos($file, '..') !== false) { throw new Exception(sprintf(__('Template file contains invalid characters: %s'), $file)); } foreach ($this->templateFolders as $folder) { if (file_exists($folder.'/'.$file)) { $this->templateContent = file_get_contents($folder.'/'.$file); return; } } // File not found in all the folders. throw new Exception(sprintf(__('Template file not found: %s'), $file)); } function _callback($matches) { list(,$tag, $firstcar) = $matches; if ($firstcar != 't') { trigger_error(sprintf(__('Invalid tag in translation extractor: %s'), $tag), E_USER_ERROR); return ''; } $this->_currentTag = $tag; if (!preg_match('/^(\/?[a-zA-Z0-9_]+)(?:(?:\s+(.*))|(?:\((.*)\)))?$/', $tag, $m)) { trigger_error(sprintf(__('Invalid function syntax: %s'), $tag), E_USER_ERROR); return ''; } if (count($m) == 4){ $m[2] = $m[3]; } if (!isset($m[2])) $m[2] = ''; if ($m[1] == 'trans') { return $this->_parseFunction($m[1], $m[2]); } return ''; } function _callbackInTransBlock($matches) { list(,$tag, $firstcar) = $matches; if (!preg_match('/^\$|[\'"]|[a-zA-Z\/]$/', $firstcar)) { trigger_error(sprintf(__('Invalid tag syntax: %s'), $tag), E_USER_ERROR); return ''; } $this->_currentTag = $tag; if ($firstcar == '$') { $tok = explode('|', $tag); $this->_transStack[substr($tok[0], 1)] = $this->_parseVariable($tag); return '%%'.substr($tok[0], 1).'%%'; } return ''; } function _parseVariable($expr) { $tok = explode('|', $expr); $res = $this->_parseFinal(array_shift($tok), $this->_allowedInVar); // We do not take into account the modifiers. return $res; } function _parseFunction($name, $args) { switch ($name) { case 'trans': $argfct = $this->_parseFinal($args, $this->_allowedAssign); $res = '__('.$argfct.');'."\n"; break; default: $res = ''; break; } return $res; } /* ------- if: op, autre, var foreach: T_AS, T_DOUBLE_ARROW, T_VARIABLE, @locale@ for: autre, fin_instruction while: op, autre, var assign: T_VARIABLE puis assign puis autre, ponctuation, T_STRING echo: T_VARIABLE/@locale@ puis autre + ponctuation modificateur: serie de autre séparé par une virgule tous : T_VARIABLE, @locale@ */ function _parseFinal($string, $allowed=array(), $exceptchar=array(';')) { $tokens = token_get_all(''); $result = ''; $first = true; $inDot = false; $firstok = array_shift($tokens); $afterAs = false; $f_key = ''; $f_val = ''; $results = array(); // il y a un bug, parfois le premier token n'est pas T_OPEN_TAG... if ($firstok == '<' && $tokens[0] == '?' && is_array($tokens[1]) && $tokens[1][0] == T_STRING && $tokens[1][1] == 'php') { array_shift($tokens); array_shift($tokens); } foreach ($tokens as $tok) { if (is_array($tok)) { list($type, $str) = $tok; $first = false; if($type == T_CLOSE_TAG){ continue; } if ($type == T_AS) { $afterAs = true; } if ($inDot) { $result .= $str; } elseif ($type == T_VARIABLE) { $result .= '$t->_vars[\''.substr($str, 1).'\']'; } elseif ($type == T_WHITESPACE || in_array($type, $allowed)) { $result .= $str; } else { trigger_error(sprintf(__('Invalid syntax: (%s) %s.'), $this->_currentTag, $str), E_USER_ERROR); return ''; } } else { if (in_array($tok, $exceptchar)) { trigger_error(sprintf(__('Invalid character: (%s) %s.'), $this->_currentTag, $tok), E_USER_ERROR); } elseif ($tok == '.') { $inDot = true; $result .= '->'; } elseif ($tok == '~') { $result .= '.'; } elseif ($tok =='[') { $result.=$tok; } elseif ($tok ==']') { $result.=$tok; } elseif ($tok == ',') { // $getAsArray not defined anywhere... $results[]=$result; $result=''; } else { $result .= $tok; } $first = false; } } return $result; } }