Skip to content

Commit

Permalink
Visitor: no abstract methods -> strict type hints
Browse files Browse the repository at this point in the history
  • Loading branch information
distantnative committed Jan 19, 2025
1 parent d840bdd commit c717f87
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 32 deletions.
4 changes: 2 additions & 2 deletions src/Query/Visitors/Interpreter.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function coalescence(mixed $left, mixed $right): mixed
/**
* Executes global function
*/
public function function(string $name, $arguments): mixed
public function function(string $name, array $arguments = []): mixed
{
$function = $this->functions[$name] ?? null;

Expand All @@ -99,7 +99,7 @@ public function literal(mixed $value): mixed
public function memberAccess(
mixed $object,
string|int $member,
array|string|null $arguments = null,
array|null $arguments = null,
bool $nullSafe = false
): mixed {
if ($this->interceptor !== null) {
Expand Down
18 changes: 10 additions & 8 deletions src/Query/Visitors/Transpiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,18 @@ public function closure(ClosureNode $node): string
/**
* Converts coalescence operator to string representation
*/
public function coalescence(mixed $left, mixed $right): string
public function coalescence(string $left, string $right): string
{
return "($left ?? $right)";
}

/**
* Creates string representation for (global) function
*/
public function function(string $name, $arguments): string
{
public function function(
string $name,
string|null $arguments = null
): string {
if (isset($this->functions[$name]) === false) {
throw new Exception("Invalid global function: $name");
}
Expand All @@ -110,9 +112,9 @@ public function literal(mixed $value): string
* Creates string representation for member access
*/
public function memberAccess(
mixed $object,
string $object,
string|int $member,
array|string|null $arguments = null,
string|null $arguments = null,
bool $nullSafe = false
): string {
$this->uses[Runtime::class] = true;
Expand Down Expand Up @@ -142,9 +144,9 @@ public static function phpName(string $name): string
* Converts ternary operator to string representation
*/
public function ternary(
mixed $condition,
mixed $true,
mixed $false
string $condition,
string|null $true,
string $false
): string {
if ($true === null) {
return "($condition ?: $false)";
Expand Down
26 changes: 15 additions & 11 deletions src/Query/Visitors/Visitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Kirby\Query\Visitors;

use Closure;
use Kirby\Query\AST\ClosureNode;

/**
* @package Kirby Query
Expand All @@ -13,6 +12,21 @@
* @copyright Bastian Allgeier
* @license https://opensource.org/licenses/MIT
* @since 6.0.0
*
* Every visitor class must implement the following methods.
* As PHP won't allow increasing the typing specificity, we
* aren't actually adding them here in the abstract class, so that
* the actual visitor classes can work with much more specific type hints.
*
* @method mixed argumentList(array $arguments)
* @method mixed arrayList(array $elements)
* @method mixed closure($ClosureNode $node))
* @method mixed coalescence($left, $right)
* @method mixed function($name, $arguments)
* @method mixed literal($value)
* @method mixed memberAccess($object, string|int $member, $arguments, bool $nullSafe = false)
* @method mixed ternary($condition, $true, $false)
* @method mixed variable(string $name)
*/
abstract class Visitor
{
Expand All @@ -26,14 +40,4 @@ public function __construct(
protected Closure|null $interceptor = null
) {
}

abstract public function argumentList(array $arguments);
abstract public function arrayList(array $elements);
abstract public function closure(ClosureNode $node);
abstract public function coalescence(mixed $left, mixed $right);
abstract public function function(string $name, $arguments);
abstract public function literal(mixed $value);
abstract public function memberAccess(mixed $object, string|int $member, array|string|null $arguments = null, bool $nullSafe = false);
abstract public function ternary(mixed $condition, mixed $true, mixed $false);
abstract public function variable(string $name);
}
2 changes: 1 addition & 1 deletion tests/Query/Visitors/InterpreterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public function testFunction(): void
$visitor = new Interpreter(
functions: ['foo' => fn () => 'bar']
);
$this->assertSame('bar', $visitor->function('foo', []));
$this->assertSame('bar', $visitor->function('foo'));
}

/**
Expand Down
20 changes: 10 additions & 10 deletions tests/Query/Visitors/TranspilerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public function testClosure(): void
public function testCoalescence(): void
{
$visitor = new Transpiler();
$this->assertSame('(3 ?? 4)', $visitor->coalescence(3, 4));
$this->assertSame('( ?? 4)', $visitor->coalescence(null, 4));
$this->assertSame('(3 ?? 4)', $visitor->coalescence('3', '4'));
$this->assertSame('(NULL ?? 4)', $visitor->coalescence('NULL', '4'));
}

/**
Expand All @@ -70,7 +70,7 @@ public function testFunction(): void
$visitor = new Transpiler(
functions: ['foo' => fn () => 'bar']
);
$this->assertSame('$functions[\'foo\']()', $visitor->function('foo', null));
$this->assertSame('$functions[\'foo\']()', $visitor->function('foo'));
}

/**
Expand All @@ -83,7 +83,7 @@ public function testFunctionInvalid(): void
$this->expectException(Exception::class);
$this->expectExceptionMessage('Invalid global function: fox');

$visitor->function('fox', null);
$visitor->function('fox');
}

/**
Expand All @@ -102,6 +102,8 @@ public function testLiteral(): void
{
$visitor = new Transpiler();
$this->assertSame('3', $visitor->literal(3));
$this->assertSame('\'string\'', $visitor->literal('string'));
$this->assertSame('false', $visitor->literal(false));
$this->assertSame('NULL', $visitor->literal(null));
}

Expand All @@ -128,17 +130,15 @@ public function testPhpName(): void

/**
* @covers ::ternary
*
* TODO: is that right that false/null are missing here
*/
public function testTernary(): void
{
$visitor = new Transpiler();
$this->assertSame('(1 ? 2 : 3)', $visitor->ternary(true, 2, 3));
$this->assertSame('( ? 2 : 3)', $visitor->ternary(false, 2, 3));
$this->assertSame('(true ? 2 : 3)', $visitor->ternary('true', '2', '3'));
$this->assertSame('(false ? 2 : 3)', $visitor->ternary('false', '2', '3'));

$this->assertSame('(truthy ?: 3)', $visitor->ternary('truthy', null, 3));
$this->assertSame('( ?: 3)', $visitor->ternary(null, null, 3));
$this->assertSame('("truthy" ?: 3)', $visitor->ternary('"truthy"', null, '3'));
$this->assertSame('(NULL ?: 3)', $visitor->ternary('NULL', null, 3));
}

/**
Expand Down

0 comments on commit c717f87

Please sign in to comment.