TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
4.5 KiB
193 lines
4.5 KiB
<?php
|
|
/**
|
|
* This file is part of TypePHP(AOT).
|
|
*
|
|
* @link https://www.swoole.com/aot/
|
|
* @contact service@swoole.com
|
|
*/
|
|
|
|
use TypePhp\CompilerTest;
|
|
use TypePhp\Exception\TestError;
|
|
|
|
/**
|
|
* Enum method restrictions are declaration rules. They must fail in the
|
|
* TypePHP front end, including methods injected by Trait composition, rather
|
|
* than reaching Zend class registration during module startup.
|
|
* @internal
|
|
* @coversNothing
|
|
*/
|
|
final class EnumMethodDeclarationRulesTest extends PHPUnit\Framework\TestCase
|
|
{
|
|
private string $testRoot;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->testRoot = sys_get_temp_dir() . '/typephp-enum-method-' . bin2hex(random_bytes(8));
|
|
mkdir($this->testRoot, 0777, true);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
if (!is_dir($this->testRoot)) {
|
|
return;
|
|
}
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($this->testRoot, FilesystemIterator::SKIP_DOTS),
|
|
RecursiveIteratorIterator::CHILD_FIRST,
|
|
);
|
|
foreach ($iterator as $entry) {
|
|
if ($entry->isDir()) {
|
|
rmdir($entry->getPathname());
|
|
} else {
|
|
unlink($entry->getPathname());
|
|
}
|
|
}
|
|
rmdir($this->testRoot);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider forbiddenMagicMethodProvider
|
|
*/
|
|
public function testForbiddenMagicMethodFailsDuringPrepare(string $method): void
|
|
{
|
|
$source = <<<PHP
|
|
<?php
|
|
enum Suit
|
|
{
|
|
case Hearts;
|
|
public function {$method}() {}
|
|
}
|
|
|
|
function main(): void {}
|
|
PHP;
|
|
|
|
[$compiler, $file] = $this->compilerFor($source);
|
|
|
|
$this->expectException(TestError::class);
|
|
$this->expectExceptionMessage("Enum `Suit` cannot include magic method `{$method}`");
|
|
$compiler->prepareFile($file);
|
|
}
|
|
|
|
public static function forbiddenMagicMethodProvider(): iterable
|
|
{
|
|
foreach ([
|
|
'__construct',
|
|
'__destruct',
|
|
'__clone',
|
|
'__get',
|
|
'__set',
|
|
'__unset',
|
|
'__isset',
|
|
'__sleep',
|
|
'__wakeup',
|
|
'__set_state',
|
|
'__serialize',
|
|
'__unserialize',
|
|
'__ToString',
|
|
'__debugInfo',
|
|
] as $method) {
|
|
yield $method => [$method];
|
|
}
|
|
}
|
|
|
|
public function testTraitInjectedForbiddenMethodFailsDuringComposition(): void
|
|
{
|
|
$source = <<<'PHP'
|
|
<?php
|
|
trait Builder
|
|
{
|
|
public function __construct() {}
|
|
}
|
|
|
|
enum Suit
|
|
{
|
|
use Builder;
|
|
case Hearts;
|
|
}
|
|
|
|
function main(): void {}
|
|
PHP;
|
|
|
|
[$compiler, $file] = $this->compilerFor($source);
|
|
$compiler->prepareFile($file);
|
|
|
|
$this->expectException(TestError::class);
|
|
$this->expectExceptionMessage('Enum `Suit` cannot include magic method `__construct`');
|
|
$compiler->composeTraitDeclarations([$file]);
|
|
}
|
|
|
|
public function testTraitAliasToForbiddenMethodFailsDuringComposition(): void
|
|
{
|
|
$source = <<<'PHP'
|
|
<?php
|
|
trait Builder
|
|
{
|
|
public function cleanup(): void {}
|
|
}
|
|
|
|
enum Suit
|
|
{
|
|
use Builder { cleanup as __destruct; }
|
|
case Hearts;
|
|
}
|
|
|
|
function main(): void {}
|
|
PHP;
|
|
|
|
[$compiler, $file] = $this->compilerFor($source);
|
|
$compiler->prepareFile($file);
|
|
|
|
$this->expectException(TestError::class);
|
|
$this->expectExceptionMessage('Enum `Suit` cannot include magic method `__destruct`');
|
|
$compiler->composeTraitDeclarations([$file]);
|
|
}
|
|
|
|
public function testCallCallStaticAndInvokeRemainAllowed(): void
|
|
{
|
|
$source = <<<'PHP'
|
|
<?php
|
|
enum Suit
|
|
{
|
|
case Hearts;
|
|
|
|
public function __call(string $name, array $arguments): mixed
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public static function __callStatic(string $name, array $arguments): mixed
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public function __invoke(): string
|
|
{
|
|
return 'Hearts';
|
|
}
|
|
}
|
|
|
|
function main(): void {}
|
|
PHP;
|
|
|
|
[$compiler, $file] = $this->compilerFor($source);
|
|
$compiler->prepareFile($file);
|
|
$compiler->convertFile($file);
|
|
|
|
self::assertFileExists($compiler->getCppFile($file));
|
|
}
|
|
|
|
/** @return array{CompilerTest, string} */
|
|
private function compilerFor(string $source): array
|
|
{
|
|
$file = $this->testRoot . '/program.php';
|
|
file_put_contents($file, $source);
|
|
|
|
global $translator;
|
|
$compiler = CompilerTest::create($this->testRoot);
|
|
$translator = $compiler;
|
|
$compiler->addFiles([$file]);
|
|
|
|
return [$compiler, $file];
|
|
}
|
|
}
|
|
|