Skip to content

Commit

Permalink
Add support for custom object calls
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Oelgart committed Nov 28, 2019
1 parent bb0ee07 commit 03243e1
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 19 deletions.
53 changes: 38 additions & 15 deletions src/TokenStream/AST.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Closure;
use InvalidArgumentException;
use nicoSWD\Rule\Grammar\CallableUserFunctionInterface;
use nicoSWD\Rule\Parser\Exception\ParserException;
use nicoSWD\Rule\TokenStream\Exception\UndefinedVariableException;
use nicoSWD\Rule\TokenStream\Token\BaseToken;
use nicoSWD\Rule\TokenStream\Token\TokenFactory;
Expand Down Expand Up @@ -49,7 +50,7 @@ public function getStream(string $rule): TokenStream
public function getMethod(string $methodName, BaseToken $token): CallableUserFunctionInterface
{
if ($token instanceof TokenObject) {
return $this->getUserObjectCallable($token, $methodName);
return $this->getCallableUserObject($token, $methodName);
}

if (empty($this->methods)) {
Expand Down Expand Up @@ -126,33 +127,55 @@ private function registerFunctions()
}
}

private function getUserObjectCallable(BaseToken $token, string $methodName): CallableUserFunctionInterface
private function getCallableUserObject(BaseToken $token, string $methodName): CallableUserFunctionInterface
{
return new class ($token, $this->tokenFactory, $methodName) implements CallableUserFunctionInterface
{
/** @var BaseToken */
private $token;
return new class ($token, $this->tokenFactory, $methodName) implements CallableUserFunctionInterface {
/** @var TokenFactory */
private $tokenFactory;
/** @var string */
private $methodName;
/** @var Closure */
private $callable;
/** @var array */
private $variations = ['get', 'is', ''];

public function __construct(BaseToken $token, TokenFactory $tokenFactory, string $methodName)
{
$this->token = $token;
$this->tokenFactory = $tokenFactory;
$this->methodName = $methodName;
$this->callable = $this->getCallable($token, $methodName);
}

public function call(BaseToken $param = null): BaseToken
private function getCallable(BaseToken $token, string $methodName): Closure
{
$object = [$this->token->getValue(), $this->methodName];
$object = $token->getValue();

if (!is_callable($object)) {
throw new \Exception();
if (property_exists($object, $methodName)) {
return function () use ($object, $methodName) {
return $object->{$methodName};
};
}

return $this->tokenFactory->createFromPHPType($object());
$method[0] = $object;
$index = 0;

do {
if (!isset($this->variations[$index])) {
throw ParserException::undefinedMethod($methodName, $token);
}

$method[1] = $this->variations[$index] . $methodName;
} while (!is_callable($method) && isset($this->variations[$index++]));

return function (BaseToken $param = null) use ($method) {
return $method($param ? $param->getValue() : null);
};
}

public function call(BaseToken $param = null): BaseToken
{
$callable = $this->callable;

return $this->tokenFactory->createFromPHPType(
$callable($param)
);
}
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/TokenStream/Token/TokenObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

final class TokenObject extends BaseToken
{
public function getType() : int
public function getType(): int
{
return TokenType::OBJECT;
return TokenType::VALUE;
}
}
78 changes: 76 additions & 2 deletions tests/integration/ObjectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <[email protected]>
*/

use nicoSWD\Rule\Rule;
use nicoSWD\Rule\tests\integration\AbstractTestBase;

final class ObjectTest extends AbstractTestBase
Expand All @@ -15,14 +17,86 @@ public function testObjects()
function test() {
return 'test one two';
}

function test2() {
return new class ()
{
function miau() {
return 'miau';
}
};
}
};

$variables = [
'my_obj' => $myObj,
'my_string' => 'some test'
];

$this->assertTrue($this->evaluate('my_obj.test() === "test one two"', $variables));
$this->assertFalse($this->evaluate('my_obj.test() === "oh no"', $variables));
$this->assertTrue($this->evaluate('my_obj.test2().miau() === "miau"', $variables));
}

public function testPublicPropertyShouldBeAccessible()
{
$myObj = new class {
public $test = 'my string';
};

$variables = [
'my_obj' => $myObj,
];

$this->assertTrue($this->evaluate('my_obj.test() === "my string"', $variables));
}

public function testPublicMethodsShouldBeAccessibleMagicallyViaGet()
{
$myObj = new class {
public function getString()
{
return 'some string';
}
};

$variables = [
'my_obj' => $myObj,
];

$this->assertTrue($this->evaluate('my_obj.string() === "some string"', $variables));
}

public function testPublicMethodsShouldBeAccessibleMagicallyViaIs()
{
$myObj = new class {
public function isString($string)
{
return $string;
}

public function yes()
{
return 'yes';
}
};

$variables = [
'my_obj' => $myObj,
];

$this->assertTrue($this->evaluate('my_obj.string(my_obj.yes()) === "yes"', $variables));
}

public function testUndefinedMethodsShouldThrowAnError()
{
$myObj = new class {};

$variables = [
'my_obj' => $myObj,
];

$rule = new Rule('my_obj.nope() === false', $variables);

$this->assertFalse($rule->isValid());
$this->assertSame('Undefined method "nope" at position 0', $rule->getError());
}
}

0 comments on commit 03243e1

Please sign in to comment.