diff --git a/Security/Authorization/Expression/ExpressionCompiler.php b/Security/Authorization/Expression/ExpressionCompiler.php index 6643776..13c75ca 100644 --- a/Security/Authorization/Expression/ExpressionCompiler.php +++ b/Security/Authorization/Expression/ExpressionCompiler.php @@ -129,7 +129,7 @@ public function outdent() $this->indentationLevel -= 1; if ($this->indentationLevel < 0) { - throw new RuntimeException('The identation level cannot be less than zero.'); + throw new RuntimeException('The indentation level cannot be less than zero.'); } return $this; @@ -266,12 +266,12 @@ public function compilePreconditions(ExpressionInterface $expr) } if ($expr instanceof FunctionExpression) { - $this->getFunctionCompiler($expr->name)->compilePreconditions($this, $expr); - foreach ($expr->args as $arg) { $this->compilePreconditions($arg); } + $this->getFunctionCompiler($expr->name)->compilePreconditions($this, $expr); + return $this; } diff --git a/Tests/Security/Authorization/Expression/ExpressionCompilerTest.php b/Tests/Security/Authorization/Expression/ExpressionCompilerTest.php index 1843264..982dc69 100644 --- a/Tests/Security/Authorization/Expression/ExpressionCompilerTest.php +++ b/Tests/Security/Authorization/Expression/ExpressionCompilerTest.php @@ -3,6 +3,8 @@ namespace JMS\SecurityExtraBundle\Tests\Security\Authorization\Expression; use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\ParameterExpressionCompiler; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Ast\FunctionExpression; +use JMS\SecurityExtraBundle\Security\Authorization\Expression\Compiler\Func\FunctionCompilerInterface; use JMS\SecurityExtraBundle\Security\Acl\Expression\HasPermissionFunctionCompiler; @@ -110,6 +112,27 @@ public function testCompileWhenParameterIsWrappedInMethodCall() $this->assertFalse($evaluator($context)); } + public function testCompileInternalInPreconditions() + { + $this->compiler->addTypeCompiler(new ParameterExpressionCompiler()); + $this->compiler->addFunctionCompiler(new TestIssue108FunctionCompiler()); + + // the first call ensure that state is reset correctly + $this->compiler->compileExpression(new Expression( + 'testIssue(#project)')); + $evaluator = eval($this->compiler->compileExpression( + new Expression('testIssue(#project)'))); + + $secureObject = new SecuredObject(); + $project = new Project(); + + $context = array( + 'object' => new MethodInvocation(new \ReflectionMethod($secureObject, 'delete'), $secureObject, array($project), array()), + ); + + $this->assertTrue($evaluator($context)); + } + /** * @dataProvider getUnaryNotTests */ @@ -148,3 +171,34 @@ protected function setUp() $this->compiler = new ExpressionCompiler(); } } + +class TestIssue108FunctionCompiler implements FunctionCompilerInterface +{ + public function getName() + { + return 'testIssue'; + } + + public function compilePreconditions(ExpressionCompiler $compiler, FunctionExpression $function) + { + if (1 !== count($function->args)) { + throw new \RuntimeException(sprintf('The %s() function expects exactly one argument, but got "%s".', $this->getName(), var_export($function->args, true))); + } + + $argName = $compiler->nextName(); + + $compiler + ->write("\$$argName = ") + ->compileInternal($function->args[0]) + ->writeln(';'); + + $compiler->attributes['arg_name'] = $argName; + } + + public function compile(ExpressionCompiler $compiler, FunctionExpression $function) + { + $argName = $compiler->attributes['arg_name']; + + $compiler->write("\$$argName !== null"); + } +}