修复 `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