After $tpl->getContext()->noThrow(true) was invoked, I wanna the exception
will not occure when a variable/member was not found, but the exception
still be thrown. 

Here is the example files. 'name' and 'age' are both not existed.

---play.html---
<html>
<body>
<span tal:content="name|default">name</span>
<span tal:content="age">age</span>
</body>
</html>

---nothrow.php---
<?php
$path = '/home/weck/OpenSource/phptal/classes/'; // not patched
//$path = '/home/weck/OpenSource/phptal2/classes/'; // patched

set_include_path($path . PATH_SEPARATOR . get_include_path());

define(PHPTAL_TEMPLATE_REPOSITORY, dirname(__FILE__));
require_once 'PHPTAL.php';

$tpl = new PHPTAL('play.html');
$tpl->setForceReparse(true);
$tpl->getContext()->noThrow(true);
define(PHPTAL_TEMPLATE_REPOSITORY, ".");
try {
    echo $tpl->execute();
}
catch (Exception $e){
    echo $e;
}
?>

When exception occure, Stack trace is:
>From /home/weck/server/trunk/nectar/app/www/play.html around line 0
exception 'PHPTAL_Exception' with message 'Unable to find path age in
current scope' in
/home/weck/OpenSource/phptal/classes/PHPTAL/Context.php:198
Stack trace:
#0 /tmp/tpl_1_1_12fb7af02dd4f575c35b84b48e19e709d6.php(14):
PHPTAL_Context->__get('age')
#1 /home/weck/OpenSource/phptal/classes/PHPTAL.php(403):
tpl_1_1_12fb7af02dd4f575c35b84b48e19e709d6(Object(PHPTAL),
Object(PHPTAL_Context))
#2 /home/weck/server/trunk/nectar/app/www/nothrow.php(15): PHPTAL->execute()
#3 {main}

The clue existed in generated tpl file:
 
<?php
function tpl_1_1_12fb7af02dd4f575c35b84b48e19e709d6( $tpl, $ctx ) {
/* Generated by PHPTAL from /home/weck/server/trunk/nectar/app/www/play.html
*/
;
$glb = $tpl->getGlobalContext() ;
?><html>^M
<body>^M
<span><?php
$ctx->noThrow(true) ;
if (!phptal_isempty($__content__ = $ctx->name)):  ;
?><?php echo phptal_escape($__content__, ENT_QUOTES, 'UTF-8') ?><?php else:
; ?>
name<?php
endif ;
$ctx->noThrow(false) ;
?></span>^M
<span><?php echo phptal_escape($ctx->age, ENT_QUOTES, 'UTF-8') ?></span>^M
</body>^M
</html><?php
}

We can found that 
tal:content="name|default" 
will be converted to
<?php
$ctx->noThrow(true) ;
if (!phptal_isempty($__content__ = $ctx->name)):  ;
?><?php echo phptal_escape($__content__, ENT_QUOTES, 'UTF-8') ?><?php else:
; ?>
name<?php
endif ;
$ctx->noThrow(false) ;
?>

After these statement, the current __nothrow will be discard. 

To resolve this problem, just save current __nothrow before these statement
and restore __nothrow after them.

This bug also existed in function phptal_true and phptal_exists.

The following is the patch:

Index: classes/PHPTAL/Context.php

===================================================================

--- classes/PHPTAL/Context.php  (revision 348)

+++ classes/PHPTAL/Context.php  (working copy)

@@ -35,6 +35,7 @@

     public $__docType;
     public $__nothrow;
     public $__translator;
+    public $__throws;
 
     public function __construct()
     {
@@ -118,6 +119,16 @@

         $this->__nothrow = $bool;
     }
 
+    public function pushThrow()
+    {
+        $this->__throws[] = $this->__nothrow;
+    }
+
+    public function popThrow()
+    {
+        $this->__nothrow = array_pop($this->__throws);
+    }
+
     /**
      * Returns true if specified slot is filled.
      */
@@ -352,9 +363,10 @@

 
 function phptal_true($ctx, $path)
 {
+    $flag = $ctx->__nothrow;
     $ctx->noThrow(true);
     $res = phptal_path($ctx, $path, true);
-    $ctx->noThrow(false);
+    $ctx->noThrow($flag);
     return !!$res;
 }
 
@@ -366,9 +378,10 @@

     // special note: this method may requires to be extended to a full
     // phptal_path() sibling to avoid calling latest path part if it is a
     // method or a function...
+    $flag = $ctx->__nothrow;
     $ctx->noThrow(true);
     $res = phptal_path($ctx, $path, true);
-    $ctx->noThrow(false);
+    $ctx->noThrow($flag);
     return $res !== NULL;
 }
 
Index: classes/PHPTAL/Php/CodeWriter.php

===================================================================

--- classes/PHPTAL/Php/CodeWriter.php   (revision 348)

+++ classes/PHPTAL/Php/CodeWriter.php   (working copy)

@@ -94,6 +94,16 @@

         $this->flushHtml();
     }
 
+    public function pushThrow()
+    {        
+        $this->pushCode('$ctx->pushThrow()');
+    }
+
+    public function popThrow()
+    {
+        $this->pushCode('$ctx->popThrow()');
+    }
+
     public function noThrow($bool)
     {
         if ($bool){
Index: classes/PHPTAL/Php/TalesChainExecutor.php

===================================================================

--- classes/PHPTAL/Php/TalesChainExecutor.php   (revision 348)

+++ classes/PHPTAL/Php/TalesChainExecutor.php   (working copy)

@@ -58,6 +58,7 @@

 
     private function _executeChain()
     {
+        $this->_chainGenerator->pushThrow();
         $this->_chainGenerator->noThrow(true);
         
         end($this->_chain); $lastkey = key($this->_chain);
@@ -88,6 +89,7 @@

         }
         $this->_chainGenerator->doEnd();
         $this->_chainGenerator->noThrow(false);
+        $this->_chainGenerator->popThrow();
     }
     
     private $_state = 0;
Index: classes/PHPTAL/Php/TalesInternal.php

===================================================================

--- classes/PHPTAL/Php/TalesInternal.php        (revision 348)

+++ classes/PHPTAL/Php/TalesInternal.php        (working copy)

@@ -174,7 +174,7 @@

         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' : '').')';
+           return 'phptal_path($ctx->'.$next.', \''.$expression.'\''.',
$ctx->__nothrow'.')';
        }
 
     private static function checkExpressionPart($expression)



Zou guangxian .



_______________________________________________
PHPTAL mailing list
PHPTAL@lists.motion-twin.com
http://lists.motion-twin.com/mailman/listinfo/phptal

Reply via email to