Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions phpunit/code/arrayaccess-coalesce-assign-codegen.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

class CodegenArrayAccessBag implements ArrayAccess
{
public function offsetExists(mixed $offset): bool { return false; }
public function offsetGet(mixed $offset): mixed { return null; }
public function offsetSet(mixed $offset, mixed $value): void {}
public function offsetUnset(mixed $offset): void {}
}

class CodegenArrayAccessHolder
{
public function __construct(public mixed $value) {}

public function __get(string $name): mixed
{
return $this->value;
}
}

function coalesceArrayAccess(CodegenArrayAccessBag $bag, string $key): mixed
{
return $bag[$key] ??= 42;
}

function coalesceMixedArrayAccess(mixed &$container, string $key): mixed
{
return $container[$key] ??= 43;
}

function coalesceMagicArrayAccess(CodegenArrayAccessHolder $holder, string $key): mixed
{
return $holder->virtual[$key] ??= 44;
}

function coalesceFixedArray(string $key): mixed
{
$container = [];
return $container[$key] ??= 45;
}

function replaceCodegenArray(mixed &$container, string &$key): int
{
$container = new CodegenArrayAccessBag();
$key = 'replacement';
return 46;
}

function coalesceMutableFixedArray(string $key): mixed
{
$container = [];
return $container[$key] ??= replaceCodegenArray($container, $key);
}
90 changes: 90 additions & 0 deletions phpunit/src/ArrayAccessCoalesceAssignCodegenTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php
/**
* This file is part of TypePHP(AOT).
*
* @link https://www.swoole.com/aot/
* @contact service@swoole.com
*/

use TypePhp\CompilerTest;

/**
* @internal
* @coversNothing
*/
final class ArrayAccessCoalesceAssignCodegenTest extends BaseTest
{
public function testObjectTargetSeparatesPresenceReadAndWrite(): void
{
$code = $this->compileFixture();
$body = $this->extractFunctionBody($code, 'php::Var php_coalescearrayaccess(');

self::assertSame(1, substr_count($body, '.offsetExists('));
self::assertSame(1, substr_count($body, '.offsetGet('));
self::assertSame(1, substr_count($body, '.offsetSet(key,'));
self::assertStringContainsString('.isObject()', $body);
self::assertStringContainsString('.assignKeyedDimension(key,', $body);
}

public function testMixedTargetRetainsArrayAndObjectDispatch(): void
{
$code = $this->compileFixture();
$body = $this->extractFunctionBody($code, 'php::Var php_coalescemixedarrayaccess(');

self::assertStringContainsString('.isObject()', $body);
self::assertStringContainsString('php::exists(', $body);
self::assertStringContainsString('.offsetSet(key,', $body);
self::assertStringContainsString('.assignKeyedDimension(key,', $body);
}

public function testMagicContainerIsEvaluatedOncePerReadAndWritePhase(): void
{
$code = $this->compileFixture();
$body = $this->extractFunctionBody($code, 'php::Var php_coalescemagicarrayaccess(');

self::assertSame(2, substr_count($body, 'typephp_read_property_cached(holder,'));
self::assertStringContainsString('[&](auto &&', $body);
}

public function testFixedArrayKeepsDirectFastPathWhenItsTypeCannotChange(): void
{
$code = $this->compileFixture();
$body = $this->extractFunctionBody($code, 'php::Var php_coalescefixedarray(');

self::assertStringContainsString('.item(key, true)', $body);
self::assertStringNotContainsString('.isObject()', $body);
self::assertStringNotContainsString('.isArray()', $body);
}

public function testFixedArrayUsesRuntimeDispatchWhenRhsCanReplaceIt(): void
{
$code = $this->compileFixture();
$body = $this->extractFunctionBody($code, 'php::Var php_coalescemutablefixedarray(');

self::assertStringContainsString('.isObject()', $body);
self::assertStringContainsString('.offsetSet(key,', $body);
self::assertStringContainsString('.assignKeyedDimension(key,', $body);
}

private function extractFunctionBody(string $code, string $signature): string
{
$start = strpos($code, $signature);
self::assertIsInt($start, "missing function: {$signature}");
$end = strpos($code, "\n}", $start);
self::assertIsInt($end);
return substr($code, $start, $end - $start);
}

private function compileFixture(): string
{
$compiler = CompilerTest::create(TYPEPHP_ROOT_PATH);
$source = TYPEPHP_ROOT_PATH . '/phpunit/code/arrayaccess-coalesce-assign-codegen.php';
$compiler->addFiles([$source]);
$compiler->prepareFile($source);
$generated = $compiler->convertFile($source);
$code = file_get_contents($generated);

self::assertIsString($code);
return $code;
}
}
Loading
Loading