* @author Kornel LesiƄski * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License * @version SVN: $Id$ * @link http://phptal.org/ */ /** * TAL spec 1.4 for tal:define content * * argument ::= define_scope [';' define_scope]* * define_scope ::= (['local'] | 'global') define_var * define_var ::= variable_name expression * variable_name ::= Name * * Note: If you want to include a semi-colon (;) in an expression, it must be escaped by doubling it (;;).* * * examples: * * tal:define="mytitle template/title; tlen python:len(mytitle)" * tal:define="global company_name string:Digital Creations, Inc." * * * * @package PHPTAL * @subpackage Php.attribute.tal * @author Laurent Bedubourg */ class PHPTAL_Php_Attribute_TAL_Define extends PHPTAL_Php_Attribute implements PHPTAL_Php_TalesChainReader { private $tmp_content_var; private $_buffered = false; private $_defineScope = null; private $_defineVar = null; private $_pushedContext = false; /** * Prevents generation of invalid PHP code when given invalid TALES */ private $_chainPartGenerated=false; public function before(PHPTAL_Php_CodeWriter $codewriter) { $expressions = $codewriter->splitExpression($this->expression); $definesAnyNonGlobalVars = false; foreach ($expressions as $exp) { list($defineScope, $defineVar, $expression) = $this->parseExpression($exp); if (!$defineVar) { continue; } $this->_defineScope = $defineScope; // should be invisible, but not if ($defineScope != 'global') $definesAnyNonGlobalVars = true; if ($this->_defineScope != 'global' && !$this->_pushedContext) { $codewriter->pushContext(); $this->_pushedContext = true; } $this->_defineVar = $defineVar; if ($expression === null) { // no expression give, use content of tag as value for newly defined var. $this->bufferizeContent($codewriter); continue; } $code = $codewriter->evaluateExpression($expression); if (is_array($code)) { $this->chainedDefine($codewriter, $code); } elseif ( $code == PHPTAL_Php_TalesInternal::NOTHING_KEYWORD) { $this->doDefineVarWith($codewriter, 'null'); } else { $this->doDefineVarWith($codewriter, $code); } } // if the content of the tag was buffered or the tag has nothing to tell, we hide it. if ($this->_buffered || (!$definesAnyNonGlobalVars && !$this->phpelement->hasRealContent() && !$this->phpelement->hasRealAttributes())) { $this->phpelement->hidden = true; } } public function after(PHPTAL_Php_CodeWriter $codewriter) { if ($this->tmp_content_var) $codewriter->recycleTempVariable($this->tmp_content_var); if ($this->_pushedContext) { $codewriter->popContext(); } } private function chainedDefine(PHPTAL_Php_CodeWriter $codewriter, $parts) { $executor = new PHPTAL_Php_TalesChainExecutor( $codewriter, $parts, $this ); } public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor) { if (!$this->_chainPartGenerated) throw new PHPTAL_TemplateException("Invalid expression in tal:define", $this->phpelement->getSourceFile(), $this->phpelement->getSourceLine()); $executor->doElse(); $this->doDefineVarWith($executor->getCodeWriter(), 'null'); $executor->breakChain(); } public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor) { if (!$this->_chainPartGenerated) throw new PHPTAL_TemplateException("Invalid expression in tal:define", $this->phpelement->getSourceFile(), $this->phpelement->getSourceLine()); $executor->doElse(); $this->bufferizeContent($executor->getCodeWriter()); $executor->breakChain(); } public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast) { $this->_chainPartGenerated=true; if ($this->_defineScope == 'global') { $var = '$tpl->getGlobalContext()->'.$this->_defineVar; } else { $var = '$ctx->'.$this->_defineVar; } $cw = $executor->getCodeWriter(); if (!$islast) { // must use temp variable, because expression could refer to itself $tmp = $cw->createTempVariable(); $executor->doIf('('.$tmp.' = '.$exp.') !== null'); $cw->doSetVar($var, $tmp); $cw->recycleTempVariable($tmp); } else { $executor->doIf('('.$var.' = '.$exp.') !== null'); } } /** * Parse the define expression, already splitted in sub parts by ';'. */ public function parseExpression($exp) { $defineScope = false; // (local | global) $defineVar = false; // var to define // extract defineScope from expression $exp = trim($exp); if (preg_match('/^(local|global)\s+(.*?)$/ism', $exp, $m)) { list(, $defineScope, $exp) = $m; $exp = trim($exp); } // extract varname and expression from remaining of expression list($defineVar, $exp) = $this->parseSetExpression($exp); if ($exp !== null) $exp = trim($exp); return array($defineScope, $defineVar, $exp); } private function bufferizeContent(PHPTAL_Php_CodeWriter $codewriter) { if (!$this->_buffered) { $this->tmp_content_var = $codewriter->createTempVariable(); $codewriter->pushCode( 'ob_start()' ); $this->phpelement->generateContent($codewriter); $codewriter->doSetVar($this->tmp_content_var, 'ob_get_clean()'); $this->_buffered = true; } $this->doDefineVarWith($codewriter, $this->tmp_content_var); } private function doDefineVarWith(PHPTAL_Php_CodeWriter $codewriter, $code) { if ($this->_defineScope == 'global') { $codewriter->doSetVar('$tpl->getGlobalContext()->'.$this->_defineVar, $code); } else { $codewriter->doSetVar('$ctx->'.$this->_defineVar, $code); } } }