<?php
/**
* PHPTAL templating engine
*
* PHP Version 5
*
* @category HTML
* @package PHPTAL
* @author Laurent Bedubourg <hide@address.com>
* @author Kornel LesiÅski <hide@address.com>
* @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
* @version SVN: $Id: State.php 874 2010-05-31 23:17:24Z kornel $
* @link http://phptal.org/
*/
/**
* @package PHPTAL
* @subpackage Php
*/
class PHPTAL_Php_State
{
private $debug = false;
private $tales_mode = 'tales';
private $encoding;
private $output_mode;
private $phptal;
function __construct(PHPTAL $phptal)
{
$this->phptal = $phptal;
$this->encoding = $phptal->getEncoding();
$this->output_mode = $phptal->getOutputMode();
}
/**
* used by codewriter to get information for phptal:cache
*/
public function getCacheFilesBaseName()
{
return $this->phptal->getCodePath();
}
/**
* controlled by phptal:debug
*/
public function setDebug($bool)
{
$old = $this->debug;
$this->debug = $bool;
return $old;
}
/**
* if true, add additional diagnostic information to generated code
*/
public function isDebugOn()
{
return $this->debug;
}
/**
* Sets new and returns old TALES mode.
* Valid modes are 'tales' and 'php'
*
* @param string $mode
*
* @return string
*/
public function setTalesMode($mode)
{
$old = $this->tales_mode;
$this->tales_mode = $mode;
return $old;
}
public function getTalesMode()
{
return $this->tales_mode;
}
/**
* encoding used for both template input and output
*/
public function getEncoding()
{
return $this->encoding;
}
/**
* Syntax rules to follow in generated code
*
* @return one of PHPTAL::XHTML, PHPTAL::XML, PHPTAL::HTML5
*/
public function getOutputMode()
{
return $this->output_mode;
}
/**
* Load prefilter
*/
public function getPreFilterByName($name)
{
return $this->phptal->getPreFilterByName($name);
}
/**
* compile TALES expression according to current talesMode
* @return string with PHP code or array with expressions for TalesChainExecutor
*/
public function evaluateExpression($expression)
{
if ($this->getTalesMode() === 'php') {
return PHPTAL_Php_TalesInternal::php($expression);
}
return PHPTAL_Php_TalesInternal::compileToPHPExpressions($expression, false);
}
/**
* compile TALES expression according to current talesMode
* @return string with PHP code
*/
private function compileTalesToPHPExpression($expression)
{
if ($this->getTalesMode() === 'php') {
return PHPTAL_Php_TalesInternal::php($expression);
}
return PHPTAL_Php_TalesInternal::compileToPHPExpression($expression, false);
}
/**
* returns PHP code that generates given string, including dynamic replacements
*
* It's almost unused.
*/
public function interpolateTalesVarsInString($string)
{
if ($this->getTalesMode() === 'tales') {
return PHPTAL_Php_TalesInternal::string($string);
}
// replace ${var} found in expression
while (preg_match('/(?<!\$)\$\{([^\}]+)\}/s', $string, $m)) {
list($ori, $exp) = $m;
$php = PHPTAL_Php_TalesInternal::php($exp);
$string = str_replace($ori, '\'.('.$php.').\'', $string); // FIXME: that is not elegant
}
$string = str_replace('$${', '${', $string); // FIXME: that is not elegant
return '(\''.$string.'\')';
}
/**
* replaces ${} in string, expecting HTML-encoded input and HTML-escapes output
*/
public function interpolateTalesVarsInHTML($src)
{
return preg_replace_callback('/((?:\$\$)*)\$\{(structure |text )?(.*?)\}|((?:\$\$)+)\{/isS',
array($this,'_interpolateTalesVarsInHTMLCallback'), $src);
}
/**
* callback for interpolating TALES with HTML-escaping
*/
private function _interpolateTalesVarsInHTMLCallback($matches)
{
return $this->_interpolateTalesVarsCallback($matches, 'html');
}
/**
* replaces ${} in string, expecting CDATA (basically unescaped) input,
* generates output protected against breaking out of CDATA in XML/HTML
* (depending on current output mode).
*/
public function interpolateTalesVarsInCDATA($src)
{
return preg_replace_callback('/((?:\$\$)*)\$\{(structure |text )?(.*?)\}|((?:\$\$)+)\{/isS',
array($this,'_interpolateTalesVarsInCDATACallback'), $src);
}
/**
* callback for interpolating TALES with CDATA escaping
*/
private function _interpolateTalesVarsInCDATACallback($matches)
{
return $this->_interpolateTalesVarsCallback($matches, 'cdata');
}
private function _interpolateTalesVarsCallback($matches, $format)
{
// replaces $${ with literal ${ (or $$$${ with $${ etc)
if (!empty($matches[4])) {
return substr($matches[4], strlen($matches[4])/2).'{';
}
// same replacement, but before executed expression
$dollars = substr($matches[1], strlen($matches[1])/2);
$code = $matches[3];
if ($format == 'html') {
$code = html_entity_decode($code, ENT_QUOTES, $this->getEncoding());
}
$code = $this->compileTalesToPHPExpression($code);
if (rtrim($matches[2]) == 'structure') { // regex captures a space there
return $dollars.'<?php echo '.$this->stringify($code)." ?>\n";
} else {
if ($format == 'html') {
return $dollars.'<?php echo '.$this->htmlchars($code)." ?>\n";
}
if ($format == 'cdata') {
// quite complex for an "unescaped" section, isn't it?
if ($this->getOutputMode() === PHPTAL::HTML5) {
return $dollars."<?php echo str_replace('</','<\\\\/', ".$this->stringify($code).") ?>\n";
} elseif ($this->getOutputMode() === PHPTAL::XHTML) {
// both XML and HMTL, because people will inevitably send it as text/html :(
return $dollars."<?php echo strtr(".$this->stringify($code)." ,array(']]>'=>']]]]><![CDATA[>','</'=>'<\\/')) ?>\n";
} else {
return $dollars."<?php echo str_replace(']]>',']]]]><![CDATA[>', ".$this->stringify($code).") ?>\n";
}
}
assert(0);
}
}
/**
* expects PHP code and returns PHP code that will generate escaped string
* Optimizes case when PHP string is given.
*
* @return php code
*/
public function htmlchars($php)
{
// PHP strings can be escaped at compile time
if (preg_match('/^\'((?:[^\'{]+|\\\\.)*)\'$/s', $php, $m)) {
return "'".htmlspecialchars(str_replace('\\\'', "'", $m[1]), ENT_QUOTES)."'";
}
return 'phptal_escape('.$php.')';
}
/**
* allow proper printing of any object
* (without escaping - for use with structure keyword)
*
* @return php code
*/
public function stringify($php)
{
// PHP strings don't need to be changed
if (preg_match('/^\'(?>[^\'\\\\]+|\\\\.)*\'$|^\s*"(?>[^"\\\\]+|\\\\.)*"\s*$/s', $php)) {
return $php;
}
return 'phptal_tostring('.$php.')';
}
}