feature #1286 Adding ignoreStrictCheck option for call_user_func_array, if throwing exception (fabpot)

This PR was merged into the master branch.

Discussion
----------

Adding ignoreStrictCheck option for call_user_func_array, if throwing exception

Same as #1236 with added unit tests and C extension support (to come).

"I experienced the issue when using FuelPHP's ORM with EAV containers. When I tried to reference a property that was not defined I expected to get null, but instead the whole application stopped because an exception was thrown in the ORM, that no such function is present.

I couldn't find other possible exception that could be thrown when this function is called, so I assumed it's safe to take the ignoreStrictCheck into consideration here."

Commits
-------

1abf5d9 [ext] Mirroring PHP change for call_user_func_array, see #1286
9ab290b added tests for exceptions thrown in __call()
c711d37 Adding ignoreStrictCheck option for call_user_func_array, if throwing exception
This commit is contained in:
Fabien Potencier
2013-12-03 15:08:30 +01:00
4 changed files with 55 additions and 2 deletions
+1
View File
@@ -1,5 +1,6 @@
* 1.15.0 (2013-XX-XX)
* made ignoreStrictCheck in Template::getAttribute() works with __call() methods throwing BadMethodCallException
* added min and max functions
* added the round filter
* fixed a bug that prevented the optimizers to be enabled/disabled selectively
+22 -1
View File
@@ -18,8 +18,10 @@
#include "php.h"
#include "php_twig.h"
#include "ext/standard/php_var.h"
#include "ext/standard/php_string.h"
#include "ext/standard/php_smart_str.h"
#include "ext/spl/spl_exceptions.h"
#include "Zend/zend_object_handlers.h"
#include "Zend/zend_interfaces.h"
@@ -945,6 +947,7 @@ PHP_FUNCTION(twig_template_get_attributes)
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
}
$call = false;
$lcItem = strtolower($item);
if (isset(self::$cache[$class]['methods'][$lcItem])) {
$method = (string) $item;
@@ -954,8 +957,10 @@ PHP_FUNCTION(twig_template_get_attributes)
$method = 'is'.$item;
} elseif (isset(self::$cache[$class]['methods']['__call'])) {
$method = (string) $item;
$call = true;
*/
{
int call = 0;
char *lcItem = TWIG_STRTOLOWER(item, item_len);
int lcItem_length;
char *method = NULL;
@@ -981,6 +986,7 @@ PHP_FUNCTION(twig_template_get_attributes)
method = tmp_method_name_is;
} else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
method = item;
call = 1;
/*
} else {
if ($isDefinedTest) {
@@ -1037,9 +1043,24 @@ PHP_FUNCTION(twig_template_get_attributes)
return;
}
/*
$ret = call_user_func_array(array($object, $method), $arguments);
// Some objects throw exceptions when they have __call, and the method we try
// to call is not supported. If ignoreStrictCheck is true, we should return null.
try {
$ret = call_user_func_array(array($object, $method), $arguments);
} catch (BadMethodCallException $e) {
if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
return null;
}
throw $e;
}
*/
ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
if (EG(exception) && TWIG_INSTANCE_OF(EG(exception), spl_ce_BadMethodCallException TSRMLS_CC)) {
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
zend_clear_exception(TSRMLS_C);
return;
}
}
free_ret = 1;
efree(tmp_method_name_get);
efree(tmp_method_name_is);
+12 -1
View File
@@ -417,6 +417,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
}
$call = false;
$lcItem = strtolower($item);
if (isset(self::$cache[$class]['methods'][$lcItem])) {
$method = (string) $item;
@@ -426,6 +427,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
$method = 'is'.$item;
} elseif (isset(self::$cache[$class]['methods']['__call'])) {
$method = (string) $item;
$call = true;
} else {
if ($isDefinedTest) {
return false;
@@ -446,7 +448,16 @@ abstract class Twig_Template implements Twig_TemplateInterface
$this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
}
$ret = call_user_func_array(array($object, $method), $arguments);
// Some objects throw exceptions when they have __call, and the method we try
// to call is not supported. If ignoreStrictCheck is true, we should return null.
try {
$ret = call_user_func_array(array($object, $method), $arguments);
} catch (BadMethodCallException $e) {
if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
return null;
}
throw $e;
}
// useful when calling a template method from a template
// this is not supported but unfortunately heavily used in the Symfony profiler
+20
View File
@@ -236,6 +236,18 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
$this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true));
}
/**
* @dataProvider getTestsDependingOnExtensionAvailability
*/
public function testGetAttributeCallExceptions($useExt = false)
{
$template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
$object = new Twig_TemplateMagicMethodExceptionObject();
$this->assertEquals(null, $template->getAttribute($object, 'foo'));
}
public function getGetAttributeTests()
{
$array = array(
@@ -603,6 +615,14 @@ class Twig_TemplateMagicMethodObject
}
}
class Twig_TemplateMagicMethodExceptionObject
{
public function __call($method, $arguments)
{
throw new BadMethodCallException(sprintf('Unkown method %s', $method));
}
}
class CExtDisablingNodeVisitor implements Twig_NodeVisitorInterface
{
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)