修复 `use function` 导入时完整命名空间解析错误,存储完整FQN而非仅命名空间部分。 修复 `declare` 语句中字符串/整数类型值的解析,支持 `encoding="UTF-8"` 等声明。 新增相关功能的AOT测试覆盖。pull/13/head
parent
c94be9c6a9
commit
bd1a4db9d4
14 changed files with 385 additions and 6 deletions
@ -0,0 +1,26 @@ |
||||
--TEST-- |
||||
array destructuring from function return values |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function make_pair(string $name): array |
||||
{ |
||||
echo "make:$name\n"; |
||||
return [$name, strtoupper($name)]; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
[$source, $upper] = make_pair('alpha'); |
||||
var_dump($source); |
||||
var_dump($upper); |
||||
|
||||
[$left, [$middle, $right]] = ['left', ['middle', 'right']]; |
||||
var_dump($left . ':' . $middle . ':' . $right); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
make:alpha |
||||
string(5) "alpha" |
||||
string(5) "ALPHA" |
||||
string(17) "left:middle:right" |
||||
@ -0,0 +1,13 @@ |
||||
--TEST-- |
||||
declare encoding UTF-8 is accepted |
||||
--FILE-- |
||||
<?php |
||||
declare(encoding="UTF-8"); |
||||
|
||||
function main(): void |
||||
{ |
||||
echo "encoding-ok\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
encoding-ok |
||||
@ -0,0 +1,14 @@ |
||||
--TEST-- |
||||
shell execution operator returns command output |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
$name = 'aot'; |
||||
$out = `printf "hello-%s" $name`; |
||||
var_dump($out); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(9) "hello-aot" |
||||
@ -0,0 +1,37 @@ |
||||
--TEST-- |
||||
static closure captures values and references through use |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
$base = 10; |
||||
$log = []; |
||||
|
||||
$copy = static function (int $value) use ($base): int { |
||||
return $base + $value; |
||||
}; |
||||
|
||||
$push = static function (string $label) use (&$log): int { |
||||
$log[] = $label; |
||||
return count($log); |
||||
}; |
||||
|
||||
$base = 99; |
||||
|
||||
var_dump($copy(5)); |
||||
var_dump($push('first')); |
||||
var_dump($push('second')); |
||||
var_dump($log); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(15) |
||||
int(1) |
||||
int(2) |
||||
array(2) { |
||||
[0]=> |
||||
string(5) "first" |
||||
[1]=> |
||||
string(6) "second" |
||||
} |
||||
@ -0,0 +1,38 @@ |
||||
--TEST-- |
||||
throw expression in coalesce and ternary |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function require_value(?string $value): string |
||||
{ |
||||
return $value ?? throw new InvalidArgumentException('missing'); |
||||
} |
||||
|
||||
function pick_value(bool $ok): string |
||||
{ |
||||
return $ok ? 'ok' : throw new RuntimeException('bad'); |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
var_dump(require_value('present')); |
||||
var_dump(pick_value(true)); |
||||
|
||||
try { |
||||
require_value(null); |
||||
} catch (Throwable $e) { |
||||
echo get_class($e) . ':' . $e->getMessage() . "\n"; |
||||
} |
||||
|
||||
try { |
||||
pick_value(false); |
||||
} catch (Throwable $e) { |
||||
echo get_class($e) . ':' . $e->getMessage() . "\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(7) "present" |
||||
string(2) "ok" |
||||
InvalidArgumentException:missing |
||||
RuntimeException:bad |
||||
@ -0,0 +1,20 @@ |
||||
--TEST-- |
||||
print expression returns 1 and can be composed |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
$ret = print "hello\n"; |
||||
var_dump($ret); |
||||
|
||||
if (print "cond\n") { |
||||
echo "branch\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
hello |
||||
int(1) |
||||
cond |
||||
branch |
||||
@ -0,0 +1,33 @@ |
||||
--TEST-- |
||||
Mixed group use imports for class, function, and constant |
||||
--FILE-- |
||||
<?php |
||||
namespace MixedGroup\Lib { |
||||
class Formatter { |
||||
public static function wrap(string $value): string |
||||
{ |
||||
return '[' . $value . ']'; |
||||
} |
||||
} |
||||
|
||||
function label(string $value): string |
||||
{ |
||||
return 'label:' . $value; |
||||
} |
||||
|
||||
const DEFAULT_VALUE = 'mixed'; |
||||
} |
||||
|
||||
namespace { |
||||
use MixedGroup\Lib\{Formatter, function label, const DEFAULT_VALUE}; |
||||
|
||||
function main(): void |
||||
{ |
||||
var_dump(Formatter::wrap(DEFAULT_VALUE)); |
||||
var_dump(label(DEFAULT_VALUE)); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(7) "[mixed]" |
||||
string(11) "label:mixed" |
||||
@ -0,0 +1,29 @@ |
||||
--TEST-- |
||||
use function alias resolves imported function name |
||||
--FILE-- |
||||
<?php |
||||
namespace FunctionAlias\Lib { |
||||
function normalize(string $value): string |
||||
{ |
||||
return strtolower(trim($value)); |
||||
} |
||||
} |
||||
|
||||
namespace FunctionAlias\App { |
||||
use function FunctionAlias\Lib\normalize as clean_name; |
||||
|
||||
function run_alias(): void |
||||
{ |
||||
var_dump(clean_name(' AOT ')); |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
function main(): void |
||||
{ |
||||
FunctionAlias\App\run_alias(); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(3) "aot" |
||||
@ -0,0 +1,38 @@ |
||||
--TEST-- |
||||
cast operands are evaluated once |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function make_value(string $tag, mixed $value): mixed |
||||
{ |
||||
echo "make:$tag\n"; |
||||
return $value; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
var_dump((int) make_value('int', '42')); |
||||
var_dump((float) make_value('float', '2.5')); |
||||
var_dump((string) make_value('string', 123)); |
||||
var_dump((bool) make_value('bool', [])); |
||||
|
||||
$object = (object) make_value('object', ['name' => 'aot']); |
||||
var_dump($object->name); |
||||
|
||||
$array = (array) make_value('array', $object); |
||||
var_dump($array['name']); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
make:int |
||||
int(42) |
||||
make:float |
||||
float(2.5) |
||||
make:string |
||||
string(3) "123" |
||||
make:bool |
||||
bool(false) |
||||
make:object |
||||
string(3) "aot" |
||||
make:array |
||||
string(3) "aot" |
||||
@ -0,0 +1,42 @@ |
||||
--TEST-- |
||||
multiple static variables initialize independently once |
||||
--FILE-- |
||||
<?php |
||||
|
||||
function init_static(string $name, int $value): int |
||||
{ |
||||
echo "init:$name:$value\n"; |
||||
return $value; |
||||
} |
||||
|
||||
function next_pair(int $seed): void |
||||
{ |
||||
static $a = init_static('a', 10), $b = init_static('b', 20); |
||||
|
||||
$a += $seed; |
||||
$b += $seed * 2; |
||||
|
||||
var_dump([$a, $b]); |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
next_pair(1); |
||||
next_pair(2); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
init:a:10 |
||||
init:b:20 |
||||
array(2) { |
||||
[0]=> |
||||
int(11) |
||||
[1]=> |
||||
int(22) |
||||
} |
||||
array(2) { |
||||
[0]=> |
||||
int(13) |
||||
[1]=> |
||||
int(26) |
||||
} |
||||
@ -0,0 +1,46 @@ |
||||
--TEST-- |
||||
class/interface/trait/enum exists with ::class names |
||||
--FILE-- |
||||
<?php |
||||
namespace ExistsNames { |
||||
|
||||
class ExistingClass {} |
||||
interface ExistingInterface {} |
||||
trait ExistingTrait {} |
||||
enum ExistingEnum { case One; } |
||||
|
||||
function pick_name(array $names, string $key): string |
||||
{ |
||||
echo "pick:$key\n"; |
||||
return $names[$key]; |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
function main(): void |
||||
{ |
||||
$names = [ |
||||
'class' => ExistsNames\ExistingClass::class, |
||||
'interface' => ExistsNames\ExistingInterface::class, |
||||
'trait' => ExistsNames\ExistingTrait::class, |
||||
'enum' => ExistsNames\ExistingEnum::class, |
||||
]; |
||||
|
||||
var_dump(class_exists(ExistsNames\pick_name($names, 'class'))); |
||||
var_dump(interface_exists(ExistsNames\pick_name($names, 'interface'))); |
||||
var_dump(trait_exists(ExistsNames\pick_name($names, 'trait'))); |
||||
var_dump(enum_exists(ExistsNames\pick_name($names, 'enum'))); |
||||
var_dump(class_exists('ExistsNames\\MissingClass', false)); |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
pick:class |
||||
bool(true) |
||||
pick:interface |
||||
bool(true) |
||||
pick:trait |
||||
bool(true) |
||||
pick:enum |
||||
bool(true) |
||||
bool(false) |
||||
@ -0,0 +1,40 @@ |
||||
--TEST-- |
||||
method_exists and property_exists with dynamic names |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class ExistsDynamicTarget |
||||
{ |
||||
public int $count = 1; |
||||
private string $secret = 'x'; |
||||
|
||||
public function run(): string |
||||
{ |
||||
return 'run'; |
||||
} |
||||
} |
||||
|
||||
function choose_name(string $kind): string |
||||
{ |
||||
echo "choose:$kind\n"; |
||||
return $kind === 'method' ? 'run' : 'secret'; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new ExistsDynamicTarget(); |
||||
$class = ExistsDynamicTarget::class; |
||||
|
||||
var_dump(method_exists($object, choose_name('method'))); |
||||
var_dump(method_exists($class, 'missing')); |
||||
var_dump(property_exists($object, choose_name('property'))); |
||||
var_dump(property_exists($class, 'count')); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
choose:method |
||||
bool(true) |
||||
bool(false) |
||||
choose:property |
||||
bool(true) |
||||
bool(true) |
||||
Loading…
Reference in new issue