Библиотека для cis, online, cms1

This commit is contained in:
Фёдор Подлеснов 2016-06-29 18:51:32 +03:00
commit 3c2e614d87
269 changed files with 39854 additions and 0 deletions

98
PHPTAL/Php/Attribute.php Normal file
View file

@ -0,0 +1,98 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Dom/Node.php';
/**
* Base class for all PHPTAL attributes.
*
* Attributes are first ordered by PHPTAL then called depending on their
* priority before and after the element printing.
*
* An attribute must implements start() and end().
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
abstract class PHPTAL_Php_Attribute
{
const ECHO_TEXT = 'text';
const ECHO_STRUCTURE = 'structure';
/** Attribute name (ie: 'tal:content'). */
public $name;
/** Attribute value specified by the element. */
public $expression;
/** Element using this attribute (xml node). */
public $tag;
/** Called before element printing. */
public abstract function start();
/** Called after element printing. */
public abstract function end();
/**
* Remove structure|text keyword from expression and stores it for later
* doEcho() usage.
*
* $expression = 'stucture my/path';
* $expression = $this->extractEchoType($expression);
*
* ...
*
* $this->doEcho($code);
*/
protected function extractEchoType($expression)
{
$echoType = self::ECHO_TEXT;
$expression = trim($expression);
if (preg_match('/^(text|structure)\s+(.*?)$/ism', $expression, $m)) {
list(, $echoType, $expression) = $m;
}
$this->_echoType = strtolower($echoType);
return trim($expression);
}
protected function doEcho($code)
{
if ($this->_echoType == self::ECHO_TEXT)
$this->tag->generator->doEcho($code);
else
$this->tag->generator->doEchoRaw($code);
}
protected function parseSetExpression($exp)
{
$exp = trim($exp);
// (dest) (value)
if (preg_match('/^([a-z0-9:\-_]+)\s+(.*?)$/i', $exp, $m)){
array_shift($m);
return $m;
}
// (dest)
return array($exp, null);
}
protected $_echoType = PHPTAL_Php_Attribute::ECHO_TEXT;
}
?>

View file

@ -0,0 +1,107 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:attributes
//
// This attribute will allow us to translate attributes of HTML tags, such
// as the alt attribute in the img tag. The i18n:attributes attribute
// specifies a list of attributes to be translated with optional message
// IDs? for each; if multiple attribute names are given, they must be
// separated by semi-colons. Message IDs? used in this context must not
// include whitespace.
//
// Note that the value of the particular attributes come either from the
// HTML attribute value itself or from the data inserted by tal:attributes.
//
// If an attibute is to be both computed using tal:attributes and translated,
// the translation service is passed the result of the TALES expression for
// that attribute.
//
// An example:
//
// <img src="http://foo.com/logo" alt="Visit us"
// tal:attributes="alt here/greeting"
// i18n:attributes="alt"
// />
//
//
// In this example, let tal:attributes set the value of the alt attribute to
// the text "Stop by for a visit!". This text will be passed to the
// translation service, which uses the result of language negotiation to
// translate "Stop by for a visit!" into the requested language. The example
// text in the template, "Visit us", will simply be discarded.
//
// Another example, with explicit message IDs:
//
// <img src="../icons/uparrow.png" alt="Up"
// i18n:attributes="src up-arrow-icon; alt up-arrow-alttext"
// >
//
// Here, the message ID up-arrow-icon will be used to generate the link to
// an icon image file, and the message ID up-arrow-alttext will be used for
// the "alt" text.
//
/**
* @package phptal.php.attribute
*/
class PHPTAL_Php_Attribute_I18N_Attributes extends PHPTAL_Php_Attribute
{
public function start()
{
// split attributes to translate
$expressions = $this->tag->generator->splitExpression($this->expression);
// foreach attribute
foreach ($expressions as $exp){
list($attribute, $key) = $this->parseSetExpression($exp);
// if the translation key is specified
if ($key != null){
// we use it and replace the tag attribute with the result of
// the translation
$key = str_replace('\'', '\\\'', $key);
$this->tag->attributes[$attribute] = $this->_getTranslationCode("'$key'");
}
else if ($this->tag->isOverwrittenAttribute($attribute)){
$varn = $this->tag->getOverwrittenAttributeVarName($attribute);
$this->tag->attributes[$attribute] = $this->_getTranslationCode($varn);
}
// else if the attribute has a default value
else if ($this->tag->hasAttribute($attribute)){
// we use this default value as the translation key
$key = $this->tag->getAttribute($attribute);
$key = str_replace('\'', '\\\'', $key);
$this->tag->attributes[$attribute] = $this->_getTranslationCode("'$key'");
}
else {
// unable to translate the attribute
throw new PHPTAL_Exception("Unable to translate attribute $attribute");
}
}
}
public function end()
{
}
private function _getTranslationCode($key)
{
$code = '<?php ';
if (preg_match_all('/\$\{(.*?)\}/', $key, $m)){
array_shift($m);
$m = array_shift($m);
foreach ($m as $name){
$code .= "\n".'$_translator->setVar(\''.$name.'\','.phptal_tale($name).');'; // allow more complex TAL expressions
}
$code .= "\n";
}
// notice the false boolean which indicate that the html is escaped
// elsewhere looks like an hack doesn't it ? :)
$result = $this->tag->generator->escapeCode(sprintf('$_translator->translate(%s, false)', $key));
$code .= 'echo '.$result.'?>';
return $code;
}
}
?>

View file

@ -0,0 +1,24 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:data
//
// Since TAL always returns strings, we need a way in ZPT to translate
// objects, the most obvious case being DateTime objects. The data attribute
// will allow us to specify such an object, and i18n:translate will provide
// us with a legal format string for that object. If data is used,
// i18n:translate must be used to give an explicit message ID, rather than
// relying on a message ID computed from the content.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Data extends PHPTAL_Php_Attribute
{
public function start(){}
public function end(){}
}
?>

View file

@ -0,0 +1,41 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:domain
//
// The i18n:domain attribute is used to specify the domain to be used to get
// the translation. If not specified, the translation services will use a
// default domain. The value of the attribute is used directly; it is not
// a TALES expression.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Domain extends PHPTAL_Php_Attribute
{
public function start()
{
// ensure a domain stack exists or create it
$this->tag->generator->doIf('!isset($__i18n_domains)');
$this->tag->generator->pushCode('$__i18n_domains = array()');
$this->tag->generator->doEnd();
//\''.str_replace(array('\\',"'"),array('\\\\',"\\'"),$expression).'\'
$expression = $this->tag->generator->interpolateTalesVarsInString($this->expression);
// push current domain and use new domain
$code = '$__i18n_domains[] = $_translator->useDomain('.$expression.')';
$this->tag->generator->pushCode($code);
}
public function end()
{
// restore domain
$code = '$_translator->useDomain(array_pop($__i18n_domains))';
$this->tag->generator->pushCode($code);
}
}
?>

View file

@ -0,0 +1,40 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:name
//
// Name the content of the current element for use in interpolation within
// translated content. This allows a replaceable component in content to be
// re-ordered by translation. For example:
//
// <span i18n:translate=''>
// <span tal:replace='here/name' i18n:name='name' /> was born in
// <span tal:replace='here/country_of_birth' i18n:name='country' />.
// </span>
//
// would cause this text to be passed to the translation service:
//
// "${name} was born in ${country}."
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Name extends PHPTAL_Php_Attribute
{
public function start()
{
$this->tag->generator->pushCode('ob_start()');
}
public function end()
{
$code = '$_translator->setVar(\'%s\', ob_get_contents())';
$code = sprintf($code, $this->expression);
$this->tag->generator->pushCode($code);
$this->tag->generator->pushCode('ob_end_clean()');
}
}
?>

View file

@ -0,0 +1,38 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:source
//
// The i18n:source attribute specifies the language of the text to be
// translated. The default is "nothing", which means we don't provide
// this information to the translation services.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Source extends PHPTAL_Php_Attribute
{
public function start()
{
// ensure that a sources stack exists or create it
$this->tag->generator->doIf('!isset($__i18n_sources)');
$this->tag->generator->pushCode('$__i18n_sources = array()');
$this->tag->generator->end();
// push current source and use new one
$code = '$__i18n_sources[] = $_translator->setSource(\'%s\')';
$code = sprintf($code, $this->expression);
$this->tag->generator->pushCode($code);
}
public function end()
{
// restore source
$code = '$_translator->setSource(array_pop($__i18n_sources))';
$this->tag->generator->pushCode($code);
}
}
?>

View file

@ -0,0 +1,32 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// i18n:target
//
// The i18n:target attribute specifies the language of the translation we
// want to get. If the value is "default", the language negotiation services
// will be used to choose the destination language. If the value is
// "nothing", no translation will be performed; this can be used to suppress
// translation within a larger translated unit. Any other value must be a
// language code.
//
// The attribute value is a TALES expression; the result of evaluating the
// expression is the language code or one of the reserved values.
//
// Note that i18n:target is primarily used for hints to text extraction
// tools and translation teams. If you had some text that should only be
// translated to e.g. German, then it probably shouldn't be wrapped in an
// i18n:translate span.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Target extends PHPTAL_Php_Attribute
{
public function start(){}
public function end(){}
}
?>

View file

@ -0,0 +1,97 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// ZPTInternationalizationSupport
//
// i18n:translate
//
// This attribute is used to mark units of text for translation. If this
// attribute is specified with an empty string as the value, the message ID
// is computed from the content of the element bearing this attribute.
// Otherwise, the value of the element gives the message ID.
//
/**
* @package phptal.php.attribute.i18n
*/
class PHPTAL_Php_Attribute_I18N_Translate extends PHPTAL_Php_Attribute
{
public function start()
{
$escape = true;
if (preg_match('/^(text|structure)(?:\s+(.*)|\s*$)/',$this->expression,$m))
{
if ($m[1]=='structure') $escape=false;
$this->expression = isset($m[2])?$m[2]:'';
}
// if no expression is given, the content of the node is used as
// a translation key
if (strlen(trim($this->expression)) == 0){
$key = $this->_getTranslationKey($this->tag, !$escape);
$key = trim(preg_replace('/\s+/sm'.($this->tag->generator->getEncoding()=='UTF-8'?'u':''), ' ', $key));
$code = '\'' . str_replace('\'', '\\\'', $key) . '\'';
}
else {
$code = $this->tag->generator->evaluateExpression($this->expression);
}
$this->_prepareNames($this->tag);
$php = sprintf('echo $_translator->translate(%s,%s);', $code, $escape ? 'true':'false');
$this->tag->generator->pushCode($php);
}
public function end()
{
}
private function _getTranslationKey($tag, $preserve_tags)
{
$result = '';
foreach ($tag->children as $child){
if ($child instanceOf PHPTAL_Php_Text){
$result .= $child->node->getValue();
}
else if ($child instanceOf PHPTAL_Php_Element){
if ($child->hasAttribute('i18n:name')){
$value = $child->getAttribute('i18n:name');
$result .= '${' . $value . '}';
}
else {
if ($preserve_tags)
{
$result .= '<'.$child->name;
foreach($child->attributes as $k => $v)
{
$result .= ' '.$k.'="'.$v.'"';
}
$result .= '>'.$this->_getTranslationKey($child, $preserve_tags).'</'.$child->name.'>';
}
else
{
$result .= $this->_getTranslationKey($child, $preserve_tags);
}
}
}
}
return $result;
}
private function _prepareNames($tag)
{
foreach ($tag->children as $child){
if ($child instanceOf PHPTAL_Php_Element){
if ($child->hasAttribute('i18n:name')){
$child->generate();
}
else {
$this->_prepareNames($child);
}
}
}
}
}
?>

View file

@ -0,0 +1,71 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// METAL Specification 1.0
//
// argument ::= Name
//
// Example:
//
// <p metal:define-macro="copyright">
// Copyright 2001, <em>Foobar</em> Inc.
// </p>
//
// PHPTAL:
//
// <?php function XXX_macro_copyright( $tpl ) { ? >
// <p>
// Copyright 2001, <em>Foobar</em> Inc.
// </p>
// <?php } ? >
//
/**
* @package phptal.php.attribute.metal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_METAL_DefineMacro extends PHPTAL_Php_Attribute
{
public function start()
{
$macroname = strtr(trim($this->expression),'-','_');
if (!preg_match('/^[a-z0-9_]+$/i', $macroname)){
throw new PHPTAL_Exception('Bad macro name "'.$macroname.'"', $this->tag->getSourceFile(), $this->tag->getSourceLine());
}
$this->tag->generator->doFunction($macroname, '$tpl, $ctx');
$this->tag->generator->doXmlDeclaration();
$this->tag->generator->doDoctype();
$this->tag->generator->doSetVar('$tpl', 'clone $tpl');
$this->tag->generator->doSetVar('$ctx', '$tpl->getContext()');
$this->tag->generator->doSetVar('$glb', '$tpl->getGlobalContext()');
$this->tag->generator->doSetVar('$_translator', '$tpl->getTranslator()');
}
public function end()
{
$this->tag->generator->doEnd();
}
}

View file

@ -0,0 +1,75 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// METAL Specification 1.0
//
// argument ::= Name
//
// Example:
//
// <table metal:define-macro="sidebar">
// <tr><th>Links</th></tr>
// <tr><td metal:define-slot="links">
// <a href="/">A Link</a>
// </td></tr>
// </table>
//
// PHPTAL: (access to slots may be renamed)
//
// <?php function XXXX_macro_sidebar( $tpl ) { ? >
// <table>
// <tr><th>Links</th></tr>
// <tr>
// <?php if (isset($tpl->slots->links)): ? >
// <?php echo $tpl->slots->links ? >
// <?php else: ? >
// <td>
// <a href="/">A Link</a>
// </td></tr>
// </table>
// <?php } ? >
//
/**
* @package phptal.php.attribute.metal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_METAL_DefineSlot extends PHPTAL_Php_Attribute
{
public function start()
{
$cond = sprintf('$ctx->hasSlot("%s")', $this->expression);
$this->tag->generator->doIf($cond);
$code = sprintf('<?php echo $ctx->getSlot("%s") ?>', $this->expression);
$this->tag->generator->pushHtml($code);
$this->tag->generator->doElse();
}
public function end()
{
$this->tag->generator->doEnd();
}
}
?>

View file

@ -0,0 +1,75 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// METAL Specification 1.0
//
// argument ::= Name
//
// Example:
//
// <table metal:use-macro="here/doc1/macros/sidebar">
// <tr><th>Links</th></tr>
// <tr><td metal:fill-slot="links">
// <a href="http://www.goodplace.com">Good Place</a><br>
// <a href="http://www.badplace.com">Bad Place</a><br>
// <a href="http://www.otherplace.com">Other Place</a>
// </td></tr>
// </table>
//
// PHPTAL:
//
// 1. evaluate slots
//
// <?php ob_start(); ? >
// <td>
// <a href="http://www.goodplace.com">Good Place</a><br>
// <a href="http://www.badplace.com">Bad Place</a><br>
// <a href="http://www.otherplace.com">Other Place</a>
// </td>
// <?php $tpl->slots->links = ob_get_contents(); ob_end_clean(); ? >
//
// 2. call the macro (here not supported)
//
// <?php echo phptal_macro($tpl, 'master_page.html/macros/sidebar'); ? >
//
/**
* @package phptal.php.attribute.metal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_METAL_FillSlot extends PHPTAL_Php_Attribute
{
public function start()
{
$this->tag->generator->pushCode('ob_start()');
}
public function end()
{
$code = '$ctx->fillSlot("'.$this->expression.'", ob_get_clean())';
$this->tag->generator->pushCode($code);
}
}

View file

@ -0,0 +1,131 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// METAL Specification 1.0
//
// argument ::= expression
//
// Example:
//
// <hr />
// <p metal:use-macro="here/master_page/macros/copyright">
// <hr />
//
// PHPTAL: (here not supported)
//
// <?php echo phptal_macro( $tpl, 'master_page.html/macros/copyright'); ? >
//
/**
* @package phptal.php.attribute.metal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_METAL_UseMacro extends PHPTAL_Php_Attribute
{
static $ALLOWED_ATTRIBUTES = array(
'metal:fill-slot',
'metal:define-macro',
'tal:define',
);
public function start()
{
$this->pushSlots();
foreach ($this->tag->children as $child){
$this->generateFillSlots($child);
}
$macroname = strtr($this->expression,'-','_');
// local macro (no filename specified) and non dynamic macro name
if (preg_match('/^[a-z0-9_]+$/i', $macroname)) {
$code = sprintf(
'%s%s($tpl, $ctx)',
$this->tag->generator->getFunctionPrefix(),
$macroname
);
$this->tag->generator->pushCode($code);
}
// external macro or ${macroname}, use PHPTAL at runtime to resolve it
else {
$code = $this->tag->generator->interpolateTalesVarsInString($this->expression);
$code = sprintf('<?php $tpl->executeMacro(%s); ?>', $code);
$this->tag->generator->pushHtml($code);
}
$this->popSlots();
}
public function end()
{
}
private function pushSlots()
{
// reset template slots on each macro call ?
//
// NOTE: defining a macro and using another macro on the same tag
// means inheriting from the used macro, thus slots are shared, it
// is a little tricky to understand but very natural to use.
//
// For example, we may have a main design.html containing our main
// website presentation with some slots (menu, content, etc...) then
// we may define a member.html macro which use the design.html macro
// for the general layout, fill the menu slot and let caller templates
// fill the parent content slot without interfering.
if (!$this->tag->hasAttribute('metal:define-macro')){
$this->tag->generator->pushCode('$ctx->pushSlots()');
}
}
private function popSlots()
{
// restore slots if not inherited macro
if (!$this->tag->hasAttribute('metal:define-macro')){
$this->tag->generator->pushCode('$ctx->popSlots()');
}
}
private function generateFillSlots($tag)
{
if (false == ($tag instanceOf PHPTAL_Php_Tree))
return;
// if the tag contains one of the allowed attribute, we generate it
foreach (self::$ALLOWED_ATTRIBUTES as $attribute){
if ($tag->hasAttribute($attribute)){
$tag->generate();
return;
}
}
// recurse
foreach ($tag->children as $child){
$this->generateFillSlots($child);
}
}
}
?>

View file

@ -0,0 +1,92 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Kornel Lesiński <kornel@aardvarkmedia.co.uk>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/** phptal:cache (note that's not tal:cache) caches element's HTML for a given time. Time is a number with 'd', 'h', 'm' or 's' suffix.
There's optional parameter that defines how cache should be shared. By default cache is not sensitive to template's context at all
- it's shared between all pages that use that template.
You can add per url to have separate copy of given element for every URL.
You can add per expression to have different cache copy for every different value of an expression (which MUST evaluate to a string).
Expression cannot refer to variables defined using tal:define on the same element.
NB:
* phptal:cache blocks can be nested, but outmost block will cache other blocks regardless of their freshness.
* you cannot use metal:fill-slot inside elements with phptal:cache
Examples:
<div phptal:cache="3h">...</div> <!-- <div> to be evaluated at most once per 3 hours. -->
<ul phptal:cache="1d per object/id">...</ul> <!-- <ul> be cached for one day, separately for each object. -->
*/
class PHPTAL_Php_Attribute_PHPTAL_Cache extends PHPTAL_Php_Attribute
{
private $cache_tag;
public function start()
{
if (!preg_match('/^\s*([0-9]+\s*|[a-zA-Z][a-zA-Z0-9_]*\s+)([dhms])\s*(?:\;?\s*per\s+([^;]+)|)\s*$/',$this->expression, $matches))
throw new PHPTAL_Exception("Cache attribute syntax error: ".$this->expression);
$cache_len = $matches[1];
if (!is_numeric($cache_len)) $cache_len = '$ctx->'.$cache_len;
switch($matches[2])
{
case 'd': $cache_len .= '*24'; /* no break */
case 'h': $cache_len .= '*60'; /* no break */
case 'm': $cache_len .= '*60'; /* no break */
}
$this->cache_tag = '"'.addslashes( $this->tag->node->getName() . ':' . $this->tag->node->getSourceLine()).'"';
$cache_per_expression = isset($matches[3])?trim($matches[3]):NULL;
if ($cache_per_expression == 'url')
{
$this->cache_tag .= '.$_SERVER["REQUEST_URI"]';
}
else if ($cache_per_expression == 'nothing') { }
else if ($cache_per_expression)
{
$code = $this->tag->generator->evaluateExpression($cache_per_expression);
if (is_array($code)) { throw new PHPTAL_Exception("Chained expressions in per-cache directive are not supported"); }
$old_cache_tag = $this->cache_tag;
$this->cache_tag = '$ctx->cache_tag_';
$this->tag->generator->doSetVar($this->cache_tag, '('.$code.')."@".' . $old_cache_tag );
}
$cond = '!file_exists(__FILE__.md5('.$this->cache_tag.')) || time() - '.$cache_len.' >= @filemtime(__FILE__.md5('.$this->cache_tag.'))';
$this->tag->generator->doIf($cond);
$this->tag->generator->doEval('ob_start()');
}
public function end()
{
$this->tag->generator->doEval('file_put_contents(__FILE__.md5('.$this->cache_tag.'), ob_get_flush())');
$this->tag->generator->doElse();
$this->tag->generator->doEval('readfile(__FILE__.md5('.$this->cache_tag.'))');
$this->tag->generator->doEnd();
}
}

View file

@ -0,0 +1,44 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* @package phptal.php.attribute.phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_PHPTAL_DEBUG extends PHPTAL_Php_Attribute
{
public function start()
{
$this->_oldMode = $this->tag->generator->setDebug(true);
}
public function end()
{
$this->tag->generator->setDebug( $this->_oldMode );
}
private $_oldMode;
}
?>

View file

@ -0,0 +1,66 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* @package phptal.php.attribute.phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_PHPTAL_ID extends PHPTAL_Php_Attribute
{
private $id;
public function start()
{
$this->id = str_replace('"', '\\\"', $this->expression);
// retrieve trigger
$this->tag->generator->doSetVar(
'$trigger',
'$tpl->getTrigger("'.$this->id.'")'
);
// if trigger found and trigger tells to proceed, we execute
// the node content
$cond = '$trigger && '
. '$trigger->start("%s", $tpl) == PHPTAL_Trigger::PROCEED';
$cond = sprintf($cond, $this->id);
$this->tag->generator->doIf($cond);
}
public function end()
{
// end of if PROCEED
$this->tag->generator->doEnd();
// if trigger found, notify the end of the node
$this->tag->generator->doIf('$trigger');
$this->tag->generator->pushCode(
'$trigger->end("'.$this->id.'", $tpl)'
);
$this->tag->generator->doEnd();
}
}
?>

View file

@ -0,0 +1,60 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* @package phptal.php.attribute.phptal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_PHPTAL_TALES extends PHPTAL_Php_Attribute
{
public function start()
{
$mode = trim($this->expression);
$mode = strtolower($mode);
if ($mode == '' || $mode == 'default')
$mode = 'tales';
if ($mode != 'php' && $mode != 'tales') {
$err = "Unsupported TALES mode '%s'";
$err = sprintf($err, $mode);
throw new PHPTAL_Exception(
$err,
$this->tag->getSourceFile(),
$this->tag->getSourceLine()
);
}
$this->_oldMode = $this->tag->generator->setTalesMode( $mode );
}
public function end()
{
$this->tag->generator->setTalesMode( $this->_oldMode );
}
private $_oldMode;
}
?>

View file

@ -0,0 +1,168 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= attribute_statement [';' attribute_statement]*
// attribute_statement ::= attribute_name expression
// attribute_name ::= [namespace ':'] Name
// namespace ::= Name
//
// examples:
//
// <a href="/sample/link.html"
// tal:attributes="href here/sub/absolute_url">
// <textarea rows="80" cols="20"
// tal:attributes="rows request/rows;cols request/cols">
//
// IN PHPTAL: attributes will not work on structured replace.
//
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Attributes
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
const ATT_FULL_REPLACE = '$__ATT_';
const ATT_VALUE_REPLACE = '$__att_';
// this regex is used to determine if an attribute is entirely replaced
// by a php variable or if only its value is replaced.
const REGEX_FULL_REPLACE = '/<?php echo \$__ATT_.*? ?>/';
public function start()
{
// split attributes using ; delimiter
$attrs = $this->tag->generator->splitExpression($this->expression);
foreach ($attrs as $exp) {
list($attribute, $expression) = $this->parseSetExpression($exp);
if ($expression) {
$this->prepareAttribute($attribute, $expression);
}
}
}
public function end()
{
}
private function prepareAttribute($attribute, $expression)
{
$code = $this->extractEchoType(trim($expression));
$code = $this->tag->generator->evaluateExpression($code);
// if $code is an array then the attribute value is decided by a
// tales chained expression
if (is_array($code)) {
return $this->prepareChainedAttribute2($attribute, $code);
}
// XHTML boolean attribute does not appear when empty of false
if (PHPTAL_Dom_Defs::getInstance()->isBooleanAttribute($attribute)) {
return $this->prepareBooleanAttribute($attribute, $code);
}
// regular attribute which value is the evaluation of $code
$attkey = self::ATT_VALUE_REPLACE . $this->getVarName($attribute);
if ($this->_echoType == PHPTAL_Php_Attribute::ECHO_STRUCTURE)
$value = $code;
else
$value = $this->tag->generator->escapeCode($code);
$this->tag->generator->doSetVar($attkey, $value);
$this->tag->overwriteAttributeWithPhpValue($attribute, $attkey);
}
private function prepareChainedAttribute2($attribute, $chain)
{
$this->_default = false;
$this->_attribute = $attribute;
if (array_key_exists($attribute, $this->tag->attributes)) {
$this->_default = $this->tag->attributes[$attribute];
}
$this->_attkey = self::ATT_FULL_REPLACE.$this->getVarName($attribute);
$executor = new PHPTAL_Php_TalesChainExecutor($this->tag->generator, $chain, $this);
$this->tag->overwriteAttributeWithPhpValue($attribute, $this->_attkey);
}
private function prepareBooleanAttribute($attribute, $code)
{
$attkey = self::ATT_FULL_REPLACE.$this->getVarName($attribute);
$value = "' $attribute=\"$attribute\"'";
$this->tag->generator->doIf($code);
$this->tag->generator->doSetVar($attkey, $value);
$this->tag->generator->doElse();
$this->tag->generator->doSetVar($attkey, '\'\'');
$this->tag->generator->doEnd();
$this->tag->overwriteAttributeWithPhpValue($attribute, $attkey);
}
private function getVarName($attribute)
{
return strtr($attribute,':-', '__');
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->tag->generator->doSetVar(
$this->_attkey,
"''"
);
$executor->breakChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$code = ($this->_default !== false)
? "' $this->_attribute=\"".str_replace("'",'\\\'',$this->_default)."\"'" // default value
: '\'\''; // do not print attribute
$this->tag->generator->doSetVar($this->_attkey, $code);
$executor->breakChain();
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
if (!$islast) {
$condition = "!phptal_isempty($this->_attkey = $exp)";
}
else {
$condition = "NULL !== ($this->_attkey = $exp)";
}
$executor->doIf($condition);
if ($this->_echoType == PHPTAL_Php_Attribute::ECHO_STRUCTURE)
$value = $this->_attkey;
else
$value = $this->tag->generator->escapeCode($this->_attkey);
$this->tag->generator->doSetVar($this->_attkey, "' $this->_attribute=\"'.$value.'\"'");
}
}
?>

View file

@ -0,0 +1,40 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* @package phptal.php.attribute.tal
*/
class PHPTAL_Php_Attribute_TAL_Comment extends PHPTAL_Php_Attribute
{
public function start()
{
$this->tag->generator->doComment($this->expression);
}
public function end()
{
}
}
?>

View file

@ -0,0 +1,101 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
// TAL Specifications 1.4
//
// argument ::= expression
//
// Example:
//
// <p tal:condition="here/copyright"
// tal:content="here/copyright">(c) 2000</p>
//
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Condition
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
private $expressions = array();
public function start()
{
$code = $this->tag->generator->evaluateExpression($this->expression);
// If it's a chained expression build a new code path
if (is_array($code)) {
$this->expressions = array();
$executor = new PHPTAL_Php_TalesChainExecutor( $this->tag->generator, $code, $this );
return;
}
// Force a falsy condition if the nothing keyword is active
if ($code == PHPTAL_TALES_NOTHING_KEYWORD) {
$code = 'false';
}
$this->tag->generator->doIf($code);
}
public function end()
{
$this->tag->generator->doEnd();
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
// check if the expression is empty
if ( $exp !== 'false' ) {
$this->expressions[] = '!phptal_isempty($__content__ = ' . $exp . ')';
}
if ( $islast ) {
// for the last one in the chain build a ORed condition
$this->tag->generator->doIf( implode(' || ', $this->expressions ) );
// The executor will always end an if so we output a dummy if
$executor->doIf('false');
}
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
// end the chain
$this->talesChainPart( $executor, 'false', true );
$executor->breakChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
throw new PHPTAL_Exception('\'default\' keyword not allowed on condition expressions');
}
}

View file

@ -0,0 +1,98 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= (['text'] | 'structure') expression
//
// Example:
//
// <p tal:content="user/name">Fred Farkas</p>
//
//
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Content
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
public function start()
{
$expression = $this->extractEchoType($this->expression);
$code = $this->tag->generator->evaluateExpression($expression);
if (is_array($code)) {
return $this->generateChainedContent($code);
}
if ($code == PHPTAL_TALES_NOTHING_KEYWORD) {
return;
}
if ($code == PHPTAL_TALES_DEFAULT_KEYWORD) {
return $this->generateDefault();
}
$this->doEcho($code);
}
public function end()
{
}
private function generateDefault()
{
$this->tag->generateContent(true);
}
private function generateChainedContent($code)
{
$executor = new PHPTAL_Php_TalesChainExecutor($this->tag->generator, $code, $this);
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
$executor->doIf('!phptal_isempty($__content__ = '.$exp.')');
$this->doEcho('$__content__');
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->breakChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->generateDefault();
$executor->breakChain();
}
}
?>

View file

@ -0,0 +1,183 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// 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."
//
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Define
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
public function start()
{
$expressions = $this->tag->generator->splitExpression($this->expression);
$definesAnyNonGlobalVars = false;
foreach ($expressions as $exp){
list($defineScope, $defineVar, $expression) = $this->parseExpression($exp);
if (!$defineVar) {
continue;
}
$this->_defineScope = $defineScope;
if ($defineScope != 'global') $definesAnyNonGlobalVars = true; // <span tal:define="global foo" /> should be invisible, but <img tal:define="bar baz" /> not
if ($this->_defineScope != 'global' && !$this->_pushedContext){
$this->tag->generator->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();
continue;
}
$code = $this->tag->generator->evaluateExpression($expression);
if (is_array($code)){
$this->chainedDefine($code);
}
elseif ($code == PHPTAL_TALES_NOTHING_KEYWORD) {
$this->doDefineVarWith('null');
}
else {
$this->doDefineVarWith($code);
}
}
// if the content of the tag was buffered or the tag has nothing to tell, we hide it.
if ($this->_buffered || (!$definesAnyNonGlobalVars && !$this->tag->hasRealContent() && !$this->tag->hasRealAttributes())){
$this->tag->hidden = true;
}
}
public function end()
{
if ($this->_pushedContext){
$this->tag->generator->popContext();
}
}
private function chainedDefine($parts)
{
$executor = new PHPTAL_Php_TalesChainExecutor(
$this->tag->generator, $parts, $this
);
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->doDefineVarWith('null');
$executor->breakChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->bufferizeContent();
$executor->breakChain();
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
if ($this->_defineScope == 'global'){
$executor->doIf('($glb->'.$this->_defineVar.' = '.$exp.') !== null');
}
else {
$executor->doIf('($ctx->'.$this->_defineVar.' = '.$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()
{
if (!$this->_buffered){
$this->tag->generator->pushCode( 'ob_start()' );
$this->tag->generateContent();
$this->tag->generator->pushCode('$__tmp_content__ = ob_get_clean()');
$this->_buffered = true;
}
$this->doDefineVarWith('$__tmp_content__');
}
private function doDefineVarWith($code)
{
if ($this->_defineScope == 'global'){
$this->tag->generator->doSetVar('$glb->'.$this->_defineVar, $code);
}
else {
$this->tag->generator->doSetVar('$ctx->'.$this->_defineVar, $code);
}
}
private $_buffered = false;
private $_defineScope = null;
private $_defineVar = null;
private $_pushedContext = false;
}
?>

View file

@ -0,0 +1,74 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= [expression]
//
// Example:
//
// <div tal:omit-tag="" comment="This tag will be removed">
// <i>...but this text will remain.</i>
// </div>
//
// <b tal:omit-tag="not:bold">I may not be bold.</b>
//
// To leave the contents of a tag in place while omitting the surrounding
// start and end tag, use the omit-tag statement.
//
// If its expression evaluates to a false value, then normal processing
// of the element continues.
//
// If the expression evaluates to a true value, or there is no
// expression, the statement tag is replaced with its contents. It is up to
// the interface between TAL and the expression engine to determine the
// value of true and false. For these purposes, the value nothing is false,
// and cancellation of the action has the same effect as returning a
// false value.
//
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_OmitTag extends PHPTAL_Php_Attribute
{
public function start()
{
if (trim($this->expression) == ''){
$this->tag->headFootDisabled = true;
}
else {
// print tag header/foot only if condition is false
$cond = $this->tag->generator->evaluateExpression($this->expression);
$this->tag->headFootPrintCondition = '!('.$cond.')';
}
}
public function end()
{
}
}
?>

View file

@ -0,0 +1,81 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= (['text'] | 'structure') expression
//
// Example:
//
// <p tal:on-error="string: Error! This paragraph is buggy!">
// My name is <span tal:replace="here/SlimShady" />.<br />
// (My login name is
// <b tal:on-error="string: Username is not defined!"
// tal:content="user">Unknown</b>)
// </p>
//
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_OnError extends PHPTAL_Php_Attribute
{
const ERR_VAR = '$__err__';
public function start()
{
$this->tag->generator->doTry();
$this->tag->generator->pushCode('ob_start()');
}
public function end()
{
$this->tag->generator->pushCode('ob_end_flush()');
$this->tag->generator->doCatch('Exception '.self::ERR_VAR);
$this->tag->generator->pushCode('$tpl->addError('.self::ERR_VAR.')');
$this->tag->generator->pushCode('ob_end_clean()');
$expression = $this->extractEchoType($this->expression);
$code = $this->tag->generator->evaluateExpression($expression);
switch ($code) {
case PHPTAL_TALES_NOTHING_KEYWORD:
break;
case PHPTAL_TALES_DEFAULT_KEYWORD:
$this->tag->generator->pushHtml('<pre class="phptalError"');
$this->tag->generator->doEcho(self::ERR_VAR);
$this->tag->generator->pushHtml('</pre>');
break;
default:
$this->doEcho($code);
break;
}
$this->tag->generator->doEnd();
}
}
?>

View file

@ -0,0 +1,115 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= variable_name expression
// variable_name ::= Name
//
// Example:
//
// <p tal:repeat="txt python:'one', 'two', 'three'">
// <span tal:replace="txt" />
// </p>
// <table>
// <tr tal:repeat="item here/cart">
// <td tal:content="repeat/item/index">1</td>
// <td tal:content="item/description">Widget</td>
// <td tal:content="item/price">$1.50</td>
// </tr>
// </table>
//
// The following information is available from an Iterator:
//
// * index - repetition number, starting from zero.
// * number - repetition number, starting from one.
// * even - true for even-indexed repetitions (0, 2, 4, ...).
// * odd - true for odd-indexed repetitions (1, 3, 5, ...).
// * start - true for the starting repetition (index 0).
// * end - true for the ending, or final, repetition.
// * length - length of the sequence, which will be the total number of repetitions.
//
// * letter - count reps with lower-case letters: "a" - "z", "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz", and so forth.
// * Letter - upper-case version of letter.
// * roman - count reps with lower-case roman numerals: "i", "ii", "iii", "iv", "v", "vi" ...
// * Roman - upper-case version of roman numerals.
///
// * first - true for the first item in a group - see note below
// * lasst - true for the last item in a group - see note below
//
// Note: first and last are intended for use with sorted sequences. They try to
// divide the sequence into group of items with the same value. If you provide
// a path, then the value obtained by following that path from a sequence item
// is used for grouping, otherwise the value of the item is used. You can
// provide the path by appending it to the path from the repeat variable,
// as in "repeat/item/first/color".
//
// PHPTAL: index, number, even, etc... will be stored in the
// $ctx->repeat->'item' object. Thus $ctx->repeat->item->odd
//
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Repeat extends PHPTAL_Php_Attribute
{
const REPEAT = '$__repeat__';
public function start()
{
// alias to repeats handler to avoid calling extra getters on each variable access
$this->tag->generator->doSetVar( self::REPEAT, '$ctx->repeat' );
list( $varName, $expression ) = $this->parseSetExpression( $this->expression );
$code = $this->tag->generator->evaluateExpression( $expression );
$item = '$ctx->' . $varName;
$controller = self::REPEAT . '->' . $varName;
// reset item var into template context
/* // Is this actually needed?
$this->tag->generator->doIf( '!isset('.$this->item.')' );
$this->tag->generator->doSetVar( $this->item, 'false' );
$this->tag->generator->doEnd();
*/
// instantiate controller using expression
$this->tag->generator->doSetVar( $controller, 'new PHPTAL_RepeatController('.$code.')' );
// Lets loop the iterator with a foreach construct
$this->tag->generator->doForeach( $item, $controller );
}
public function end()
{
$this->tag->generator->doEnd();
}
private $item;
private $controller;
}
?>

View file

@ -0,0 +1,117 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
// TAL Specifications 1.4
//
// argument ::= (['text'] | 'structure') expression
//
// Default behaviour : text
//
// <span tal:replace="template/title">Title</span>
// <span tal:replace="text template/title">Title</span>
// <span tal:replace="structure table" />
// <span tal:replace="nothing">This element is a comment.</span>
//
require_once PHPTAL_DIR.'PHPTAL/Php/TalesChainExecutor.php';
/**
* @package phptal.php.attribute.tal
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Attribute_TAL_Replace
extends PHPTAL_Php_Attribute
implements PHPTAL_Php_TalesChainReader
{
const REPLACE_VAR = '$__replace__';
public function start()
{
// tal:replace="" => do nothing and ignore node
if (trim($this->expression) == ""){
return;
}
$expression = $this->extractEchoType($this->expression);
$code = $this->tag->generator->evaluateExpression($expression);
// chained expression
if (is_array($code)){
return $this->replaceByChainedExpression($code);
}
// nothing do nothing
if ($code == PHPTAL_TALES_NOTHING_KEYWORD) {
return;
}
// default generate default tag content
if ($code == PHPTAL_TALES_DEFAULT_KEYWORD) {
return $this->generateDefault();
}
// replace tag with result of expression
$this->doEcho($code);
}
public function end()
{
}
private function replaceByChainedExpression($expArray)
{
$executor = new PHPTAL_Php_TalesChainExecutor(
$this->tag->generator, $expArray, $this
);
}
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->continueChain();
}
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor)
{
$executor->doElse();
$this->generateDefault();
$executor->breakChain();
}
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $exp, $islast)
{
$executor->doIf('!phptal_isempty('.self::REPLACE_VAR.' = '.$exp.')');
$this->doEcho(self::REPLACE_VAR);
}
private function generateDefault()
{
$this->tag->generateSurroundHead();
$this->tag->generateHead();
$this->tag->generateContent();
$this->tag->generateFoot();
$this->tag->generateSurroundFoot();
}
}
?>

View file

@ -0,0 +1,55 @@
<?php
require_once PHPTAL_DIR.'PHPTAL/Php/Node.php';
require_once PHPTAL_DIR.'PHPTAL/Php/State.php';
require_once PHPTAL_DIR.'PHPTAL/Php/CodeWriter.php';
/**
* @package phptal.php
*/
class PHPTAL_Php_CodeGenerator
{
public function __construct($function_name, $source_path)
{
$this->_functionName = $function_name;
$this->_sourceFile = $source_path;
$this->_state = new PHPTAL_Php_State();
$this->_writer = new PHPTAL_Php_CodeWriter($this->_state);
}
public function setOutputMode($mode)
{
$this->_state->setOutputMode($mode);
}
public function setEncoding($enc)
{
$this->_state->setEncoding($enc);
}
public function generate(PHPTAL_Dom_Tree $tree)
{
$treeGen = new PHPTAL_Php_Tree($this->_writer, $tree);
$this->_writer->doComment('Generated by PHPTAL from '.$this->_sourceFile);
$this->_writer->doFunction($this->_functionName, '$tpl, $ctx');
$this->_writer->setFunctionPrefix($this->_functionName . "_");
$this->_writer->doSetVar('$glb', '$tpl->getGlobalContext()');
$this->_writer->doSetVar('$_translator', '$tpl->getTranslator()');
$treeGen->generate();
$this->_writer->doEnd();
}
public function getResult()
{
return $this->_writer->getResult();
}
private $_functionName;
private $_sourceFile;
private $_writer;
private $_state;
}
?>

394
PHPTAL/Php/CodeWriter.php Normal file
View file

@ -0,0 +1,394 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* Helps generate php representation of a template.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_CodeWriter
{
public $_indentation = 0;
public function __construct(PHPTAL_Php_State $state)
{
$this->_state = $state;
}
public function getResult()
{
$this->flush();
$this->_result = trim($this->_result);
return $this->_result;
}
public function setDocType(PHPTAL_Php_Doctype $dt)
{
$this->_doctype = str_replace('\'', '\\\'', $dt->node->getValue());
}
public function setXmlDeclaration(PHPTAL_Php_XmlDeclaration $dt)
{
$this->_xmldeclaration = str_replace('\'', '\\\'', $dt->node->getValue());
}
public function setFunctionPrefix($prefix)
{
$this->_functionPrefix = $prefix;
}
public function getFunctionPrefix()
{
return $this->_functionPrefix;
}
/**
* Returns old tales mode.
*/
public function setTalesMode($mode)
{
return $this->_state->setTalesMode($mode);
}
public function splitExpression($src)
{
preg_match_all('/(?:[^;]+|;;)+/sm', $src, $array);
$array = $array[0];
foreach($array as &$a) $a = str_replace(';;',';',$a);
return $array;
}
public function evaluateExpression($src)
{
return $this->_state->evalTalesExpression($src);
}
public function indent()
{
$this->_indentation ++;
}
public function unindent()
{
$this->_indentation --;
}
public function flush()
{
$this->flushCode();
$this->flushHtml();
}
public function noThrow($bool)
{
if ($bool){
$this->pushCode('$ctx->noThrow(true)');
}
else {
$this->pushCode('$ctx->noThrow(false)');
}
}
public function flushCode()
{
if (count($this->_codeBuffer) == 0)
return;
// special treatment for one code line
if (count($this->_codeBuffer) == 1){
$codeLine = $this->_codeBuffer[0];
// avoid adding ; after } and {
if (!preg_match('/\}|\{\s+$/', $codeLine))
$this->_result .= '<?php '.$codeLine.'; ?>';
else
$this->_result .= '<?php '.$codeLine.' ?>';
$this->_codeBuffer = array();
return;
}
$this->_result .= '<?php '."\n";
foreach ($this->_codeBuffer as $codeLine) {
// avoid adding ; after } and {
if (!preg_match('/\}|\{\s+$/', $codeLine))
$this->_result .= $codeLine . ' ;'."\n";
else
$this->_result .= $codeLine;
}
$this->_result .= '?>';
$this->_codeBuffer = array();
}
public function flushHtml()
{
if (count($this->_htmlBuffer) == 0) return;
$this->_result .= join( '', $this->_htmlBuffer );
$this->_htmlBuffer = array();
}
public function doDoctype()
{
if ($this->_doctype){
$code = '$ctx->setDocType(\''.$this->_doctype.'\')';
$this->pushCode($code);
}
}
public function doXmlDeclaration()
{
if ($this->_xmldeclaration){
$code = '$ctx->setXmlDeclaration(\''.$this->_xmldeclaration.'\')';
$this->pushCode($code);
}
}
public function doFunction($name, $params)
{
$name = $this->_functionPrefix . $name;
$this->pushGeneratorContext();
$this->pushCode("function $name( $params ) {\n");
$this->indent();
array_push($this->_segments, 'function');
}
public function doComment($comment)
{
$comment = str_replace('*/', '* /', $comment);
$this->pushCode("/* $comment */");
}
public function doEval($code)
{
$this->pushCode($code);
}
public function doForeach($out, $source)
{
array_push($this->_segments, 'foreach');
$this->pushCode("foreach ($source as \$__key__ => $out ):");
$this->indent();
}
public function doEnd()
{
$segment = array_pop($this->_segments);
$this->unindent();
if ($segment == 'function') {
$this->pushCode("\n}\n\n");
$functionCode = $this->getResult();
$this->popGeneratorContext();
$this->_result = $functionCode . $this->_result;
}
else if ($segment == 'try')
$this->pushCode('}');
else if ($segment == 'catch')
$this->pushCode('}');
else
$this->pushCode("end$segment");
}
public function doTry()
{
array_push($this->_segments, 'try');
$this->pushCode('try {');
$this->indent();
}
public function doSetVar($varname, $code)
{
$this->pushCode($varname.' = '.$code);
}
public function doCatch($catch)
{
$this->doEnd();
array_push($this->_segments, 'catch');
$code = 'catch(%s) {';
$this->pushCode(sprintf($code, $catch));
$this->indent();
}
public function doIf($condition)
{
array_push($this->_segments, 'if');
$this->pushCode('if ('.$condition.'): ');
$this->indent();
}
public function doElseIf($condition)
{
$this->unindent();
$this->pushCode('elseif ('.$condition.'): ');
$this->indent();
}
public function doElse()
{
$this->unindent();
$this->pushCode('else: ');
$this->indent();
}
public function doEcho($code)
{
$this->flush();
$html = '<?php echo %s ?>';
$html = sprintf($html, $this->escapeCode($code));
$this->pushHtml($html);
}
public function doEchoRaw($code)
{
$this->pushHtml('<?php echo '.$code.' ?>');
}
public function pushHtml($html)
{
$html = $this->_state->interpolateTalesVarsInHtml($html);
$this->flushCode();
array_push($this->_htmlBuffer, $html);
}
public function pushRawHtml($html)
{
$this->flushCode();
array_push($this->_htmlBuffer, $html);
}
public function pushString($str)
{
$this->flushCode();
// replace ${var} inside strings
while (preg_match('/^(.*?)((?<!\$)\$\{[^\}]*?\})(.*?)$/s', $str, $m)){
list(,$before,$expression,$after) = $m;
$before = $this->escapeLTandGT($before);
array_push($this->_htmlBuffer, $before);
$expression = $this->_state->interpolateTalesVarsInHtml($expression);
array_push($this->_htmlBuffer, $expression);
$str = $after;
}
$str = str_replace('$${', '${', $str);
if (strlen($str) > 0){
$str = $this->escapeLTandGT($str);
array_push($this->_htmlBuffer, $str);
}
}
public function pushCode($codeLine)
{
$this->flushHtml();
$codeLine = $this->indentSpaces() . $codeLine;
array_push($this->_codeBuffer, $codeLine);
}
public function escapeLTandGT($str){
$str = str_replace('<', '&lt;', $str);
$str = str_replace('>', '&gt;', $str);
return $str;
}
public function escapeCode($code)
{
return $this->_state->htmlchars($code);
}
public function getEncoding()
{
return $this->_state->getEncoding();
}
public function interpolateTalesVarsInString($src)
{
return $this->_state->interpolateTalesVarsInString($src);
}
public function setDebug($bool)
{
return $this->_state->setDebug($bool);
}
public function isDebugOn()
{
return $this->_state->isDebugOn();
}
public function getOutputMode()
{
return $this->_state->getOutputMode();
}
public function pushContext()
{
$this->pushCode('$ctx = $tpl->pushContext()');
}
public function popContext()
{
$this->pushCode('$ctx = $tpl->popContext()');
}
// ~~~~~ Private members ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private function indentSpaces()
{
return str_repeat("\t", $this->_indent);
}
private function pushGeneratorContext()
{
array_push($this->_contexts, clone $this);
$this->_result = "";
$this->_indent = 0;
$this->_codeBuffer = array();
$this->_htmlBuffer = array();
$this->_segments = array();
}
private function popGeneratorContext()
{
$oldContext = array_pop($this->_contexts);
$this->_result = $oldContext->_result;
$this->_indent = $oldContext->_indent;
$this->_codeBuffer = $oldContext->_codeBuffer;
$this->_htmlBuffer = $oldContext->_htmlBuffer;
$this->_segments = $oldContext->_segments;
}
private $_state;
private $_result = "";
private $_indent = 0;
private $_codeBuffer = array();
private $_htmlBuffer = array();
private $_segments = array();
private $_contexts = array();
private $_functionPrefix = "";
private $_doctype = "";
private $_xmldeclaration = "";
}
?>

View file

@ -0,0 +1,101 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* @package phptal.php
*/
class PHPTAL_Php_ElementWriter
{
public function __construct(PHPTAL_Php_CodeWriter $writer, PHPTAL_Php_Element $tag)
{
$this->_writer = $writer;
$this->_tag = $tag;
}
public function writeHead()
{
if ($this->_tag->headFootDisabled)
return;
if ($this->_tag->headFootPrintCondition){
$this->_writer->doIf($this->_tag->headFootPrintCondition);
}
$this->_writer->pushHtml('<'.$this->_tag->name);
$this->_writeAttributes();
if ($this->_tag->isEmptyNode()){
$this->_writer->pushHtml('/>');
}
else {
$this->_writer->pushHtml('>');
}
if ($this->_tag->headFootPrintCondition){
$this->_writer->doEnd();
}
}
public function writeFoot()
{
if ($this->_tag->headFootDisabled)
return;
if ($this->_tag->isEmptyNode())
return;
if ($this->_tag->headFootPrintCondition){
$this->_writer->doIf($this->_tag->headFootPrintCondition);
}
$this->_writer->pushHtml('</'.$this->_tag->name.'>');
if ($this->_tag->headFootPrintCondition){
$this->_writer->doEnd();
}
}
public function writeAttributes()
{
$fullreplaceRx = PHPTAL_Php_Attribute_TAL_Attributes::REGEX_FULL_REPLACE;
foreach ($this->_tag->attributes as $key=>$value) {
if (preg_match($fullreplaceRx, $value)){
$this->_writer->pushHtml($value);
}
/*
else if (strpos('<?php', $value) === 0){
$this->_writer->pushHtml(' '.$key.'="');
$this->_writer->pushRawHtml($value);
$this->_writer->pushHtml('"');
}
*/
else {
$this->_writer->pushHtml(' '.$key.'="'.$value.'"');
}
}
}
private $_tag;
private $_writer;
}
?>

507
PHPTAL/Php/Node.php Normal file
View file

@ -0,0 +1,507 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Dom/Defs.php';
require_once PHPTAL_DIR.'PHPTAL/Php/CodeWriter.php';
require_once PHPTAL_DIR.'PHPTAL/Php/Attribute.php';
/**
* Document node abstract class.
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
abstract class PHPTAL_Php_Node
{
public $node;
public $generator;
public function __construct(PHPTAL_Php_CodeWriter $generator, PHPTAL_Dom_Node $node)
{
$this->generator = $generator;
$this->node = $node;
}
public function getSourceFile()
{
return $this->node->getSourceFile();
}
public function getSourceLine()
{
return $this->node->getSourceLine();
}
public abstract function generate();
}
/**
* Node container.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Tree extends PHPTAL_Php_Node
{
public $children;
public function __construct(PHPTAL_Php_CodeWriter $gen, $node)
{
parent::__construct($gen,$node);
$this->children = array();
foreach ($node->getChildren() as $child){
if ($child instanceOf PHPTAL_Dom_Element){
$gen = new PHPTAL_Php_Element($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_Text){
$gen = new PHPTAL_Php_Text($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_Doctype){
$gen = new PHPTAL_Php_Doctype($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_XmlDeclaration){
$gen = new PHPTAL_Php_XmlDeclaration($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_Specific){
$gen = new PHPTAL_Php_Specific($this->generator, $child);
}
else if ($child instanceOf PHPTAL_Dom_Comment){
$gen = new PHPTAL_Php_Comment($this->generator, $child);
}
else {
throw new PHPTAL_Exception('Unhandled node class '.get_class($child));
}
array_push($this->children, $gen);
}
}
public function generate()
{
try
{
foreach ($this->children as $child){
$child->generate();
}
}
catch(PHPTAL_Exception $e)
{
$e->hintSrcPosition($this->getSourceFile(), $this->getSourceLine());
throw $e;
}
}
}
/**
* Document Tag representation.
*
* This is the main class used by PHPTAL because TAL is a Template Attribute
* Language, other Node kinds are (usefull) toys.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Element extends PHPTAL_Php_Tree
{
const ERR_ATTRIBUTES_CONFLICT =
"Attribute conflict in '%s' at line '%d', '%s' cannot appear with '%s'";
public $name;
public $attributes = array();
public $talAttributes = array();
public $overwrittenAttributes = array();
public $replaceAttributes = array();
public $contentAttributes = array();
public $surroundAttributes = array();
public $headFootDisabled = false;
public $headFootPrintCondition = false;
public $hidden = false;
public function __construct(PHPTAL_Php_CodeWriter $generator, $node)
{
parent::__construct($generator, $node);
$this->name = $node->getName();
$this->attributes = $node->attributes;
$this->xmlns = $node->getXmlnsState();
$this->prepare();
}
private function prepare()
{
$this->prepareAttributes();
$this->separateAttributes();
$this->orderTalAttributes();
}
public function generate()
{
if ($this->generator->isDebugOn()){
$this->generator->pushCode('$ctx->__line = '.$this->getSourceLine());
$this->generator->doComment('tag "'.$this->name.'" from line '.$this->getSourceLine());
}
if (count($this->replaceAttributes) > 0) {
$this->generateSurroundHead();
foreach ($this->replaceAttributes as $att) {
$att->start();
$att->end();
}
$this->generateSurroundFoot();
return;
}
$this->generateSurroundHead();
// a surround tag may decide to hide us (tal:define for example)
if (!$this->hidden){
$this->generateHead();
$this->generateContent();
$this->generateFoot();
}
$this->generateSurroundFoot();
}
/** Returns true if the element contains specified PHPTAL attribute. */
public function hasAttribute($name)
{
return $this->node->hasAttribute($name);
}
/** Returns the value of specified PHPTAL attribute. */
public function getAttribute($name)
{
return $this->node->getAttribute($name);
}
public function isOverwrittenAttribute($name)
{
return array_key_exists($name, $this->overwrittenAttributes);
}
public function getOverwrittenAttributeVarName($name)
{
return $this->overwrittenAttributes[$name];
}
public function overwriteAttributeWithPhpValue($name, $phpVariable)
{
$this->attributes[$name] = '<?php echo '.$phpVariable.' ?>';
$this->overwrittenAttributes[$name] = $phpVariable;
}
/**
* Returns true if this element or one of its PHPTAL attributes has some
* content to print (an empty text node child does not count).
*/
public function hasRealContent()
{
return $this->node->hasRealContent()
|| count($this->contentAttributes) > 0;
}
public function hasRealAttributes()
{
return ((count($this->attributes) - count($this->talAttributes)) > 0) || $this->hasAttribute('tal:attributes');
}
// ~~~~~ Generation methods may be called by some PHPTAL attributes ~~~~~
public function generateSurroundHead()
{
foreach ($this->surroundAttributes as $att) {
$att->start();
}
}
public function generateHead()
{
if ($this->headFootDisabled) return;
if ($this->headFootPrintCondition) {
$this->generator->doIf($this->headFootPrintCondition);
}
$this->generator->pushHtml('<'.$this->name);
$this->generateAttributes();
if ($this->isEmptyNode()){
$this->generator->pushHtml('/>');
}
else {
$this->generator->pushHtml('>');
}
if ($this->headFootPrintCondition) {
$this->generator->doEnd();
}
}
public function generateContent($realContent=false)
{
if ($this->isEmptyNode()){
return;
}
if (!$realContent && count($this->contentAttributes) > 0) {
foreach ($this->contentAttributes as $att) {
$att->start();
$att->end();
}
return;
}
parent::generate();
}
public function generateFoot()
{
if ($this->headFootDisabled)
return;
if ($this->isEmptyNode())
return;
if ($this->headFootPrintCondition) {
$this->generator->doIf($this->headFootPrintCondition);
}
$this->generator->pushHtml( '</'.$this->name.'>' );
if ($this->headFootPrintCondition) {
$this->generator->doEnd();
}
}
public function generateSurroundFoot()
{
for ($i = (count($this->surroundAttributes)-1); $i >= 0; $i--) {
$this->surroundAttributes[$i]->end();
}
}
// ~~~~~ Private members ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private function generateAttributes()
{
// A phptal attribute can modify any node attribute replacing
// its value by a <?php echo $somevalue ?\ >.
//
// The entire attribute (key="value") can be replaced using the
// '$__ATT_' value code, it is very usefull for xhtml boolean
// attributes like selected, checked, etc...
//
// example:
//
// $tag->generator->pushCode(
// '$__ATT_checked = $somecondition ? \'checked="checked"\' : \'\''
// );
// $tag->attributes['checked'] = '<?php echo $__ATT_checked ?\>';
//
$fullreplaceRx = PHPTAL_Php_Attribute_TAL_Attributes::REGEX_FULL_REPLACE;
foreach ($this->attributes as $key=>$value) {
if (preg_match($fullreplaceRx, $value)){
$this->generator->pushHtml($value);
}
else if (strpos($value,'<?php') === 0){
$this->generator->pushHtml(' '.$key.'="');
$this->generator->pushRawHtml($value);
$this->generator->pushHtml('"');
}
else {
$this->generator->pushHtml(' '.$key.'="'.$value.'"');
}
}
}
private function getNodePrefix()
{
$result = false;
if (preg_match('/^(.*?):block$/', $this->name, $m)){
list(,$result) = $m;
}
return $result;
}
private function isEmptyNode()
{
return ($this->generator->getOutputMode() == PHPTAL::XHTML && PHPTAL_Dom_Defs::getInstance()->isEmptyTag($this->name)) ||
($this->generator->getOutputMode() == PHPTAL::XML && !$this->hasContent());
}
private function hasContent()
{
return count($this->children) > 0 || count($this->contentAttributes) > 0;
}
private function prepareAttributes()
{
//TODO: use registered namespaces instead of the raw list
if (preg_match('/^(tal|metal|phptal|i18n):block$/', $this->name, $m)) {
$this->headFootDisabled = true;
list(,$ns) = $m;
$attributes = array();
foreach ($this->attributes as $key=>$value) {
if ($this->xmlns->isPhpTalAttribute("$ns:$key")) {
$attributes["$ns:$key"] = $value;
}
else {
$attributes[$key] = $value;
}
}
$this->attributes = $attributes;
}
}
private function separateAttributes()
{
$attributes = array();
$this->talAttributes = array();
foreach ($this->attributes as $key=>$value) {
// remove handled xml namespaces
if (PHPTAL_Dom_Defs::getInstance()->isHandledXmlNs($key,$value)){
}
else if ($this->xmlns->isPhpTalAttribute($key)) {
$this->talAttributes[$key] = $value;
}
else if (PHPTAL_Dom_Defs::getInstance()->isBooleanAttribute($key)) {
$attributes[$key] = $key;
}
else {
$attributes[$key] = $value;
}
}
$this->attributes = $attributes;
}
private function orderTalAttributes()
{
$attributes = array();
foreach ($this->talAttributes as $key=>$exp){
$name = $this->xmlns->unAliasAttribute($key);
$att = PHPTAL_Dom_Defs::getInstance()->getNamespaceAttribute($name);
if (array_key_exists($att->getPriority(), $attributes)){
$err = sprintf(self::ERR_ATTRIBUTES_CONFLICT,
$this->name,
$this->getSourceLine(),
$key,
$attributes[$att->getPriority()][0]
);
throw new PHPTAL_Exception($err);
}
$attributes[$att->getPriority()] = array($key, $att, $exp);
}
ksort($attributes);
$this->talHandlers = array();
foreach ($attributes as $prio => $dat){
list($key, $att, $exp) = $dat;
$handler = $att->createAttributeHandler($this, $exp);
$this->talHandlers[$prio] = $handler;
if ($att instanceOf PHPTAL_NamespaceAttributeSurround)
$this->surroundAttributes[] = $handler;
else if ($att instanceOf PHPTAL_NamespaceAttributeReplace)
$this->replaceAttributes[] = $handler;
else if ($att instanceOf PHPTAL_NamespaceAttributeContent)
$this->contentAttributes[] = $handler;
else
throw new PHPTAL_Exception("Unknown namespace attribute class ".get_class($att));
}
}
}
/**
* @package phptal.php
*/
class PHPTAL_Php_Comment extends PHPTAL_Php_Node
{
public function generate()
{
$this->generator->pushRawHtml($this->node->getValue());
}
}
/**
* Document text data representation.
* @package phptal.php
*/
class PHPTAL_Php_Text extends PHPTAL_Php_Node
{
public function generate()
{
$this->generator->pushString($this->node->getValue());
}
}
/**
* Comment, preprocessor, etc... representation.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Specific extends PHPTAL_Php_Node
{
public function generate()
{
$this->generator->pushHtml($this->node->getValue());
}
}
/**
* Document doctype representation.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Doctype extends PHPTAL_Php_Node
{
public function __construct(PHPTAL_Php_CodeWriter $generator, $node)
{
parent::__construct($generator, $node);
$this->generator->setDocType($this);
}
public function generate()
{;
$this->generator->doDoctype();
}
}
/**
* XML declaration node.
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_XmlDeclaration extends PHPTAL_Php_Node
{
public function __construct(PHPTAL_Php_CodeWriter $gen, $node)
{
parent::__construct($gen, $node);
$this->generator->setXmlDeclaration($this);
}
public function generate()
{
$this->generator->doXmlDeclaration();
}
}
?>

152
PHPTAL/Php/State.php Normal file
View file

@ -0,0 +1,152 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
require_once PHPTAL_DIR.'PHPTAL/Php/Tales.php';
/**
* @package phptal.php
*/
class PHPTAL_Php_State
{
public function __construct()
{
$this->_debug = false;
$this->_talesMode = 'tales';
$this->_encoding = 'UTF-8';
$this->_outputMode = '';
}
public function setDebug($bool)
{
$old = $this->_debug;
$this->_debug = $bool;
return $old;
}
public function isDebugOn()
{
return $this->_debug;
}
public function setTalesMode($mode)
{
$old = $this->_talesMode;
$this->_talesMode = $mode;
return $old;
}
public function getTalesMode()
{
return $this->_talesMode;
}
public function setEncoding($enc)
{
$this->_encoding = $enc;
}
public function getEncoding()
{
return $this->_encoding;
}
public function setOutputMode($mode)
{
$this->_outputMode = $mode;
}
public function getOutputMode()
{
return $this->_outputMode;
}
public function evalTalesExpression($expression)
{
if ($this->_talesMode == 'php')
return PHPTAL_TalesInternal::php($expression);
return phptal_tales($expression);
}
public function interpolateTalesVarsInString($string)
{
if ($this->_talesMode == 'tales'){
return PHPTAL_TalesInternal::string($string);
}
// replace ${var} found in expression
while (preg_match('/(?<!\$)\$\{([^\}]+)\}/ism', $string, $m)){
list($ori, $exp) = $m;
$php = PHPTAL_TalesInternal::php($exp);
$repl = '\'.%s.\'';
$repl = sprintf($repl, $php, $this->_encoding);
$string = str_replace($ori, $repl, $string);
}
$string = str_replace('$${', '${', $string);
return '\''.$string.'\'';
}
private function _interpolateTalesVarsStructure($matches) {
return '<?php echo '.phptal_tale($matches[1]).' ?>';
}
private function _interpolateTalesVarsEscaped($matches) {
return '<?php echo phptal_escape('.phptal_tale($matches[1]).', ENT_QUOTES, \''.$this->_encoding.'\');?>';
}
public function interpolateTalesVarsInHtml($src)
{
if ($this->_talesMode == 'tales'){
$result = preg_replace_callback('/(?<!\$)\$\{structure (.*?)\}/ism', array($this,'_interpolateTalesVarsStructure'), $src);
$result = preg_replace_callback('/(?<!\$)\$\{(.*?)\}/ism', array($this,'_interpolateTalesVarsEscaped'), $result);
$result = str_replace('$${', '${', $result);
return $result;
}
while (preg_match('/(?<!\$)\${(structure )?([^\}]+)\}/ism', $src, $m)){
list($ori, $struct, $exp) = $m;
$php = PHPTAL_TalesInternal::php($exp);
// when structure keyword is specified the output is not html
// escaped
if ($struct){
$repl = '<?php echo '.$php.'; ?>';
}
else {
$repl = '<?php echo '.$this->htmlchars($php).'; ?>';
}
$src = str_replace($ori, $repl, $src);
}
return str_replace('$${','${', $src);
}
public function htmlchars($php)
{
return 'phptal_escape('.$php.', ENT_QUOTES, \''.$this->_encoding.'\')';
}
private $_debug;
private $_talesMode;
private $_encoding;
private $_outputMode;
}
?>

135
PHPTAL/Php/Tales.php Normal file
View file

@ -0,0 +1,135 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
define('PHPTAL_TALES_DEFAULT_KEYWORD', '_DEFAULT_DEFAULT_DEFAULT_DEFAULT_');
define('PHPTAL_TALES_NOTHING_KEYWORD', '_NOTHING_NOTHING_NOTHING_NOTHING_');
// TALES Specification 1.3
//
// Expression ::= [type_prefix ':'] String
// type_prefix ::= Name
//
// Examples:
//
// a/b/c
// path:a/b/c
// nothing
// path:nothing
// python: 1 + 2
// string:Hello, ${username}
//
//
// Builtin Names in Page Templates (for PHPTAL)
//
// * nothing - special singleton value used by TAL to represent a
// non-value (e.g. void, None, Nil, NULL).
//
// * default - special singleton value used by TAL to specify that
// existing text should not be replaced.
//
// * repeat - the repeat variables (see RepeatVariable).
//
function _phptal_tale_wrap($array, $nothrow)
{
if (count($array)==1) return '($ctx->noThrow('.($nothrow?'true':'false').')||1?('.
($array[0]==PHPTAL_TALES_NOTHING_KEYWORD?'NULL':$array[0]).
'):"")';
$expr = array_shift($array);
return "((\$tmp5=$expr) && (\$ctx->noThrow(false)||1)?\$tmp5:"._phptal_tale_wrap($array, $nothrow).')';
}
/** translates array of alternative expressions into single PHP expression. Identical to phptal_tales() for singular expressions. */
function phptal_tale($expression, $nothrow=false)
{
$r = phptal_tales($expression,true);
if (!is_array($r)) return $r;
// this weird ternary operator construct is to execute noThrow inside the expression
return '($ctx->noThrow(true)||1?'._phptal_tale_wrap($r, $nothrow).':"")';
}
function phptal_tales($expression, $nothrow=false)
{
$expression = trim($expression);
// Look for tales modifier (string:, exists:, etc...)
//if (preg_match('/^([-a-z]+):(.*?)$/', $expression, $m)) {
if (preg_match('/^([a-z][-.a-z]*[a-z]):(.*?)$/i', $expression, $m)) {
list(,$typePrefix,$expression) = $m;
}
// may be a 'string'
else if (preg_match('/^\'((?:[^\']|\\\\.)*)\'$/', $expression, $m)) {
$expression = stripslashes($m[1]);
$typePrefix = 'string';
}
// failback to path:
else {
$typePrefix = 'path';
}
// is a registered TALES expression modifier
if(PHPTAL_TalesRegistry::getInstance()->isRegistered($typePrefix)) {
$callback = PHPTAL_TalesRegistry::getInstance()->getCallback($typePrefix);
return call_user_func($callback, $expression, $nothrow);
}
// class method
if (strpos($typePrefix, '.')){
$classCallback = explode('.', $typePrefix, 2);
$callbackName = NULL;
if(!is_callable($classCallback, FALSE, $callbackName)) {
$err = 'Unknown phptal modifier %s function %s does not exists or is not statically callable.';
$err = sprintf($err, $typePrefix, $callbackName);
throw new PHPTAL_Exception($err);
}
$ref = new ReflectionClass($classCallback[0]);
if(!$ref->implementsInterface('PHPTAL_Tales')){
$err = 'Unable to use phptal modifier %s as the class %s does not implement the PHPTAL_Tales interface.';
$err = sprintf($err, $typePrefix, $callbackName);
throw new PHPTAL_Exception($err);
}
return call_user_func($classCallback, $expression, $nothrow);
}
// check if it is implemented via code-generating function
$func = 'phptal_tales_'.str_replace('-','_',$typePrefix);
if (function_exists($func)) {
return $func($expression, $nothrow);
}
// check if it is implemented via runtime function
$runfunc = 'phptal_runtime_tales_'.str_replace('-','_',$typePrefix);
if (function_exists($runfunc)) {
return "$runfunc(".phptal_tale($expression, $nothrow).")";
}
throw new PHPTAL_Exception("Unknown phptal modifier '$typePrefix'. Function '$func' does not exist");
}
// Register internal Tales expression modifiers
require_once PHPTAL_DIR.'PHPTAL/Php/TalesInternal.php';
PHPTAL_TalesInternal::registerInternalTales();
?>

View file

@ -0,0 +1,99 @@
<?php
/**
* @package phptal.php
*/
interface PHPTAL_Php_TalesChainReader
{
public function talesChainNothingKeyword(PHPTAL_Php_TalesChainExecutor $executor);
public function talesChainDefaultKeyword(PHPTAL_Php_TalesChainExecutor $executor);
public function talesChainPart(PHPTAL_Php_TalesChainExecutor $executor, $expression, $islast);
}
/**
* @package phptal.php
*/
class PHPTAL_Php_TalesChainExecutor
{
const CHAIN_BREAK = 1;
const CHAIN_CONT = 2;
public function __construct($generator, $chain, $reader)
{
assert(is_array($chain));
$this->_chain = $chain;
$this->_chainStarted = false;
$this->_chainGenerator = $generator;
$this->_reader = $reader;
$this->_executeChain();
}
public function doIf($condition)
{
if ($this->_chainStarted == false){
$this->_chainStarted = true;
$this->_chainGenerator->doIf($condition);
}
else {
$this->_chainGenerator->doElseIf($condition);
}
}
public function doElse()
{
if ($this->_chainStarted){
$this->_chainGenerator->doElse();
}
}
public function breakChain()
{
$this->_state = self::CHAIN_BREAK;
}
public function continueChain()
{
$this->_state = self::CHAIN_CONT;
}
private function _executeChain()
{
$this->_chainGenerator->noThrow(true);
end($this->_chain); $lastkey = key($this->_chain);
foreach ($this->_chain as $key => $exp){
$this->_state = 0;
if ($exp == PHPTAL_TALES_NOTHING_KEYWORD){
$this->_reader->talesChainNothingKeyword($this);
if ($this->_state == self::CHAIN_BREAK)
break;
if ($this->_state == self::CHAIN_CONT)
continue;
}
else if ($exp == PHPTAL_TALES_DEFAULT_KEYWORD){
$this->_reader->talesChainDefaultKeyword($this);
if ($this->_state == self::CHAIN_BREAK)
break;
if ($this->_state == self::CHAIN_CONT)
continue;
}
else {
$this->_reader->talesChainPart($this, $exp, $lastkey === $key);
if ($this->_state == self::CHAIN_BREAK)
break;
if ($this->_state == self::CHAIN_CONT)
continue;
}
}
$this->_chainGenerator->doEnd();
$this->_chainGenerator->noThrow(false);
}
private $_state = 0;
private $_chain;
private $_chainStarted = false;
private $_chainGenerator = null;
}
?>

View file

@ -0,0 +1,331 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
// Moritz Bechler <mbechler@eenterphace.org>
//
require_once PHPTAL_DIR.'PHPTAL/TalesRegistry.php';
class PHPTAL_TalesInternal implements PHPTAL_Tales {
//
// This function registers all internal expression modifiers
//
static public function registerInternalTales() {
static $registered = false;
if($registered) {
return;
}
$registry = PHPTAL_TalesRegistry::getInstance();
$registry->registerPrefix('not', array(__CLASS__, 'not'));
$registry->registerPrefix('path', array(__CLASS__, 'path'));
$registry->registerPrefix('string', array(__CLASS__, 'string'));
$registry->registerPrefix('php', array(__CLASS__, 'php'));
$registry->registerPrefix('exists', array(__CLASS__, 'exists'));
$registry->registerPrefix('number', array(__CLASS__, 'number'));
$registry->registerPrefix('true', array(__CLASS__, 'true'));
$registered = true;
}
static public function true($src, $nothrow)
{
return sprintf('phptal_true($ctx, %s)', self::string(trim($src), $nothrow));
}
//
// not:
//
// not: Expression
//
// evaluate the expression string (recursively) as a full expression,
// and returns the boolean negation of its value
//
// return boolean based on the following rules:
//
// 1. integer 0 is false
// 2. integer > 0 is true
// 3. an empty string or other sequence is false
// 4. a non-empty string or other sequence is true
// 5. a non-value (e.g. void, None, Nil, NULL, etc) is false
// 6. all other values are implementation-dependent.
//
// Examples:
//
// not: exists: foo/bar/baz
// not: php: object.hasChildren()
// not: string:${foo}
// not: foo/bar/booleancomparable
//
static public function not($expression, $nothrow)
{
return '!(' . phptal_tales($expression, $nothrow) . ')';
}
//
// path:
//
// PathExpr ::= Path [ '|' Path ]*
// Path ::= variable [ '/' URL_Segment ]*
// variable ::= Name
//
// Examples:
//
// path: username
// path: user/name
// path: object/method/10/method/member
// path: object/${dynamicmembername}/method
// path: maybethis | path: maybethat | path: default
//
// PHPTAL:
//
// 'default' may lead to some 'difficult' attributes implementation
//
// For example, the tal:content will have to insert php code like:
//
// if (isset($ctx->maybethis)) {
// echo $ctx->maybethis;
// }
// else if (isset($ctx->maybethat) {
// echo $ctx->maybethat;
// }
// else {
// // process default tag content
// }
//
// @returns string or array
//
static public function path($expression, $nothrow=false)
{
$expression = trim($expression);
if ($expression == 'default') return PHPTAL_TALES_DEFAULT_KEYWORD;
if ($expression == 'nothing') return PHPTAL_TALES_NOTHING_KEYWORD;
if ($expression == '') return PHPTAL_TALES_NOTHING_KEYWORD;
// split OR expressions terminated by a string
if (preg_match('/^(.*?)\s*\|\s*?(string:.*)$/sm', $expression, $m)){
list(, $expression, $string) = $m;
}
// split OR expressions terminated by a 'fast' string
else if (preg_match('/^(.*?)\s*\|\s*\'((?:[^\'\\\\]|\\\\.)*)\'\s*$/sm', $expression, $m)){
list(, $expression, $string) = $m;
$string = 'string:'.stripslashes($string);
}
// split OR expressions
$exps = preg_split('/\s*\|\s*/sm', $expression);
// if (many expressions) or (expressions or terminating string) found then
// generate the array of sub expressions and return it.
if (count($exps) > 1 || isset($string)) {
$result = array();
foreach ($exps as $exp) {
$result[] = phptal_tales(trim($exp), true);
}
if (isset($string)){
$result[] = phptal_tales($string, true);
}
return $result;
}
// only one expression to process
// first evaluate ${foo} inside the expression and threat the expression
// as if it was a string to interpolate
$expression = self::string($expression);
$expression = substr($expression, 1, -1);
$pos = strpos($expression, '/');
// if no sub part for this expression, just optimize the generated code
// and access the $ctx->var
if ($pos === false) {
if (!self::checkExpressionPart($expression)) throw new PHPTAL_Exception("Invalid TALES path: '$expression', expected variable name");
return '$ctx->'.$expression;
}
// otherwise we have to call phptal_path() to resolve the path at runtime
// extract the first part of the expression (it will be the phptal_path()
// $base and pass the remaining of the path to phptal_path()
$next = substr($expression, 0, $pos);
$expression = substr($expression, $pos+1);
if (!self::checkExpressionPart($next)) throw new PHPTAL_Exception("Invalid TALES path: '$next/$expression', expected '$next' to be variable name");
// return php code invoking phptal_path($next, $expression, $notrhow)
return 'phptal_path($ctx->'.$next.', \''.$expression.'\''.($nothrow ? ', true' : '').')';
}
private static function checkExpressionPart($expression)
{
return preg_match('/^(\$?[a-z_][a-z0-9_]*|{.*})$/i',$expression);
}
//
// string:
//
// string_expression ::= ( plain_string | [ varsub ] )*
// varsub ::= ( '$' Path ) | ( '${' Path '}' )
// plain_string ::= ( '$$' | non_dollar )*
// non_dollar ::= any character except '$'
//
// Examples:
//
// string:my string
// string:hello, $username how are you
// string:hello, ${user/name}
// string:you have $$130 in your bank account
//
static public function string($expression, $nothrow=false)
{
// This is a simple parser which evaluates ${foo} inside
// 'string:foo ${foo} bar' expressions, it returns the php code which will
// print the string with correct interpollations.
// Nothing special there :)
$inPath = false;
$inAccoladePath = false;
$lastWasDollar = false;
$result = '';
$len = strlen($expression);
for ($i=0; $i<$len; $i++) {
$c = $expression[$i];
switch ($c) {
case '$':
if ($lastWasDollar) {
$lastWasDollar = false;
}
else {
$lastWasDollar = true;
$c = '';
}
break;
case '\\':
$c = '\\\\';
break;
case '\'':
$c = '\\\'';
break;
case '{':
if ($lastWasDollar) {
$lastWasDollar = false;
$inAccoladePath = true;
$subPath = '';
$c = '';
}
break;
case '}':
if ($inAccoladePath) {
$inAccoladePath = false;
$subEval = self::path($subPath);
if (is_array($subEval)) {
$err = 'cannot use | operator in evaluated expressions';
throw new PHPTAL_Exception($err);
}
$result .= "'." . $subEval . ".'";
$subPath = '';
$lastWasDollar = false;
$c = '';
}
break;
default:
if ($lastWasDollar) {
$lastWasDollar = false;
$inPath = true;
$subPath = $c;
$c = '';
}
else if ($inAccoladePath) {
$subPath .= $c;
$c = '';
}
else if ($inPath) {
$t = strtolower($c);
if (($t >= 'a' && $t <= 'z') || ($t >= '0' && $t <= '9') || ($t == '_')){
$subPath .= $c;
$c = '';
}
else {
$inPath = false;
$subEval = self::path($subPath);
if (is_array($subEval)) {
$err = 'cannot use | operator in evaluated expressions';
throw new PHPTAL_Exception($err);
}
$result .= "'." . $subEval . ".'";
}
}
break;
}
$result .= $c;
}
if ($inPath){
$subEval = self::path($subPath);
if (is_array($subEval)){
$err = 'cannot use | operator in evaluated expressions';
throw new PHPTAL_Exception($err);
}
$result .= "'." . $subEval . ".'";
}
return '\''.$result.'\'';
}
/**
* php: modifier.
*
* Transform the expression into a regular PHP expression.
*/
static public function php($src)
{
require_once PHPTAL_DIR.'PHPTAL/Php/Transformer.php';
return PHPTAL_Php_Transformer::transform($src, '$ctx->');
}
/**
* exists: modifier.
*
* Returns the code required to invoke phptal_exists() on specified path.
*/
static public function exists($src, $nothrow)
{
return sprintf('phptal_exists($ctx, %s)', self::string(trim($src), $nothrow));
}
/**
* number: modifier.
*
* Returns the number as is.
*/
static public function number($src, $nothrow)
{
return trim($src);
}
}
?>

384
PHPTAL/Php/Transformer.php Normal file
View file

@ -0,0 +1,384 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
//
// Copyright (c) 2004-2005 Laurent Bedubourg
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
//
/**
* Tranform php: expressions into their php equivalent.
*
* This transformer produce php code for expressions like :
*
* - a.b["key"].c().someVar[10].foo()
* - (a or b) and (c or d)
* - not myBool
* - ...
*
* The $prefix variable may be changed to change the context lookup.
*
* example:
*
* $res = PHPTAL_Php_Transformer::transform('a.b.c[x]', '$ctx->');
* $res == '$ctx->a->b->c[$ctx->x]';
*
* @package phptal.php
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
*/
class PHPTAL_Php_Transformer
{
const ST_NONE = 0;
const ST_STR = 1; // 'foo'
const ST_ESTR = 2; // "foo ${x} bar"
const ST_VAR = 3; // abcd
const ST_NUM = 4; // 123.02
const ST_EVAL = 5; // ${somevar}
const ST_MEMBER = 6; // abcd.x
const ST_STATIC = 7; // class::[$]static|const
const ST_DEFINE = 8; // @MY_DEFINE
public static function transform( $str, $prefix='$' )
{
//
// Here comes the good old state machine.
// TODO: benchmark this version and then benchmark a refactored version
// with states behaviour separated into methods, keep the fastest.
//
$len = strlen($str);
$state = self::ST_NONE;
$result = '';
$i = 0;
$inString = false;
$backslashed = false;
$instanceOf = false;
$eval = false;
for ($i = 0; $i <= $len; $i++) {
if ($i == $len) $c = "\0";
else $c = $str[$i];
switch ($state) {
// no state defined, just eat char and see what to do with it.
case self::ST_NONE:
// begin of eval without {
if ($c == '$' && $i < $len && self::isAlpha($str[$i+1])){
$state = self::ST_EVAL;
$mark = $i+1;
$result .= $prefix.'{';
}
// that an alphabetic char, then it should be the begining
// of a var
else if (self::isAlpha($c) || $c==='_') {
$state = self::ST_VAR;
$mark = $i;
}
// begining of double quoted string
else if ($c == '"') {
$state = self::ST_ESTR;
$mark = $i;
$inString = true;
}
// begining of single quoted string
else if ($c == '\'') {
$state = self::ST_STR;
$mark = $i;
$inString = true;
}
// closing a method, an array access or an evaluation
else if ($c == ')' || $c == ']' || $c == '}') {
$result .= $c;
// if next char is dot then an object member must
// follow
if ($i < $len-1 && $str[$i+1] == '.') {
$result .= '->';
$state = self::ST_MEMBER;
$mark = $i+2;
$i+=2;
}
}
// @ is an access to some defined variable
else if ($c == '@') {
$state = self::ST_DEFINE;
$mark = $i+1;
}
// character we don't mind about
else {
$result .= $c;
}
break;
// $xxx
case self::ST_EVAL:
if (!self::isVarNameChar($c)){
$result .= $prefix . substr($str, $mark, $i-$mark);
$result .= '}';
$state = self::ST_NONE;
}
break;
// single quoted string
case self::ST_STR:
if ($c == '\\') {
$backslashed = true;
}
else if ($backslashed) {
$backslashed = false;
}
// end of string, back to none state
else if ($c == '\'') {
$result .= substr( $str, $mark, $i-$mark+1 );
$inString = false;
$state = self::ST_NONE;
}
break;
// double quoted string
case self::ST_ESTR:
if ($c == '\\') {
$backslashed = true;
}
else if ($backslashed) {
$backslashed = false;
}
// end of string, back to none state
else if ($c == '"') {
$result .= substr( $str, $mark, $i-$mark+1 );
$inString = false;
$state = self::ST_NONE;
}
// instring interpolation, search } and transform the
// interpollation to insert it into the string
else if ($c == '$' && $i < $len && $str[$i+1] == '{') {
$result .= substr( $str, $mark, $i-$mark ) . '{';
$sub = 0;
for ($j = $i; $j<$len; $j++) {
if ($str[$j] == '{') {
$sub++;
}
elseif ($str[$j] == '}' && (--$sub) == 0) {
$part = substr( $str, $i+2, $j-$i-2 );
$result .= self::transform($part, $prefix);
$i = $j;
$mark = $i;
}
}
}
break;
// var state
case self::ST_VAR:
if (self::isVarNameChar($c)) {
}
// end of var, begin of member (method or var)
else if ($c == '.') {
$result .= $prefix . substr( $str, $mark, $i-$mark );
$result .= '->';
$state = self::ST_MEMBER;
$mark = $i+1;
}
// static call, the var is a class name
else if ($c == ':') {
$result .= substr( $str, $mark, $i-$mark+1 );
$mark = $i+1;
$i++;
$state = self::ST_STATIC;
break;
}
// function invocation, the var is a function name
else if ($c == '(') {
$result .= substr( $str, $mark, $i-$mark+1 );
$state = self::ST_NONE;
}
// array index, the var is done
else if ($c == '[') {
if ($str[$mark]==='_') { // superglobal?
$result .= '$' . substr( $str, $mark, $i-$mark+1 );
}
else {
$result .= $prefix . substr( $str, $mark, $i-$mark+1 );
}
$state = self::ST_NONE;
}
// end of var with non-var-name character, handle keywords
// and populate the var name
else {
$var = substr( $str, $mark, $i-$mark );
$low = strtolower($var);
// boolean and null
if ($low == 'true' || $low == 'false' || $low == 'null') {
$result .= $var;
}
// lt, gt, ge, eq, ...
else if (array_key_exists($low, self::$TranslationTable)){
$result .= self::$TranslationTable[$low];
}
// instanceof keyword
else if ($low == 'instanceof'){
$result .= $var;
$instanceOf = true;
}
// previous was instanceof
else if ($instanceOf){
// last was instanceof, this var is a class name
$result .= $var;
$instanceOf = false;
}
// regular variable
else {
$result .= $prefix . $var;
}
$i--;
$state = self::ST_NONE;
}
break;
// object member
case self::ST_MEMBER:
if (self::isVarNameChar($c)) {
}
// eval mode ${foo}
else if ($c == '$') {
$result .= '{' . $prefix;
$mark++;
$eval = true;
}
// end of var member var, begin of new member
else if ($c == '.') {
$result .= substr( $str, $mark, $i-$mark );
if ($eval) { $result .='}'; $eval = false; }
$result .= '->';
$mark = $i+1;
$state = self::ST_MEMBER;
}
// begin of static access
else if ($c == ':') {
$result .= substr( $str, $mark, $i-$mark+1 );
if ($eval) { $result .='}'; $eval = false; }
$state = self::ST_STATIC;
break;
}
// the member is a method or an array
else if ($c == '(' || $c == '[') {
$result .= substr( $str, $mark, $i-$mark+1 );
if ($eval) { $result .='}'; $eval = false; }
$state = self::ST_NONE;
}
// regular end of member, it is a var
else {
$result .= substr( $str, $mark, $i-$mark );
if ($eval) { $result .='}'; $eval = false; }
$state = self::ST_NONE;
$i--;
}
break;
// wait for separator
case self::ST_DEFINE:
if (self::isVarNameChar($c)) {
}
else {
$state = self::ST_NONE;
$result .= substr( $str, $mark, $i-$mark );
$i--;
}
break;
// static call, can be const, static var, static method
// Klass::$static
// Klass::const
// Kclass::staticMethod()
//
case self::ST_STATIC:
if (self::isVarNameChar($c)) {
}
// static var
else if ($c == '$') {
}
// end of static var which is an object and begin of member
else if ($c == '.') {
$result .= substr( $str, $mark, $i-$mark );
$result .= '->';
$mark = $i+1;
$state = self::ST_MEMBER;
}
// end of static var which is a class name
else if ($c == ':') {
$result .= substr( $str, $mark, $i-$mark+1 );
$state = self::ST_STATIC;
break;
}
// static method or array
else if ($c == '(' || $c == '[') {
$result .= substr( $str, $mark, $i-$mark+1 );
$state = self::ST_NONE;
}
// end of static var or const
else {
$result .= substr( $str, $mark, $i-$mark );
$state = self::ST_NONE;
$i--;
}
break;
// numeric value
case self::ST_NUM:
if (!self::isDigitCompound($c)) {
$result .= substr( $str, $mark, $i-$mark );
$state = self::ST_NONE;
}
break;
}
}
return trim($result);
}
private static function isAlpha($c)
{
$c = strtolower($c);
return $c >= 'a' && $c <= 'z';
}
private static function isDigitCompound($c)
{
return ($c >= '0' && $c <= '9' || $c == '.');
}
private static function isVarNameChar($c)
{
return self::isAlpha($c) || ($c >= '0' && $c <= '9') || $c == '_';
}
private static $TranslationTable = array(
'not' => '!',
'ne' => '!=',
'and' => '&&',
'or' => '||',
'lt' => '<',
'gt' => '>',
'ge' => '>=',
'le' => '<=',
'eq' => '==',
);
}
?>