test(compiler): add comprehensive tests for late static binding and typed array conversions

- Added test for toStdList and toStdDict methods validating PHP array conversion with type preservation
- Included tests for late static calls from instance methods retaining receiver on inherited and overridden methods
- Added tests verifying late static calls distinguish parent and child private methods in instance context
- Covered various scenarios including scalar conversions, object arrays, and error handling cases
- Verified proper behavior with numeric keys, class inheritance, and dynamic method calls
- Tested type validation for list and dictionary conversions with multiple data types
master
韩天峰 3 weeks ago
parent a3b5d61ff7
commit f127dadf5d
  1. 90
      tests/compiler/static/late-static-instance-call.phpt
  2. 68
      tests/compiler/static/late-static-private-method.phpt
  3. 138
      tests/compiler/typed-array/conversion-methods.phpt

@ -0,0 +1,90 @@
--TEST--
Late static calls from instance methods retain the receiver on inherited and overridden methods
--FILE--
<?php
class InstanceCallBase
{
public string $value = 'base-value';
protected function helper(string $suffix = ''): string
{
return $this->value . $suffix;
}
protected static function label(): string
{
return static::class;
}
public function fixed(): string
{
return static::helper();
}
public function withArgs(): string
{
return static::helper('-arg');
}
public function dynamic(): string
{
$name = 'helper';
return static::{$name}('-dynamic');
}
public function staticFixed(): string
{
return static::label();
}
public function staticDynamic(): string
{
$name = 'label';
return static::{$name}();
}
}
class InstanceCallInherited extends InstanceCallBase
{
}
class InstanceCallOverride extends InstanceCallBase
{
protected function helper(string $suffix = ''): string
{
return 'override-' . $this->value . $suffix;
}
protected static function label(): string
{
return 'override:' . static::class;
}
}
function main(): void
{
foreach ([new InstanceCallBase(), new InstanceCallInherited(), new InstanceCallOverride()] as $item) {
echo $item->fixed(), "\n";
echo $item->withArgs(), "\n";
echo $item->dynamic(), "\n";
echo $item->staticFixed(), "\n";
echo $item->staticDynamic(), "\n";
}
}
?>
--EXPECT--
base-value
base-value-arg
base-value-dynamic
InstanceCallBase
InstanceCallBase
base-value
base-value-arg
base-value-dynamic
InstanceCallInherited
InstanceCallInherited
override-base-value
override-base-value-arg
override-base-value-dynamic
override:InstanceCallOverride
override:InstanceCallOverride

@ -0,0 +1,68 @@
--TEST--
Late static calls distinguish parent and child private methods in an instance context
--FILE--
<?php
class LatePrivateBase
{
private function value(): string
{
return 'base';
}
public function viaThis(): string
{
return $this->value();
}
public function viaStatic(): string
{
return static::value();
}
public function viaDynamic(): string
{
$name = 'value';
return static::{$name}();
}
}
class LatePrivateInherited extends LatePrivateBase
{
}
class LatePrivateRedeclared extends LatePrivateBase
{
private function value(): string
{
return 'child';
}
}
function main(): void
{
$inherited = new LatePrivateInherited();
echo $inherited->viaThis(), "\n";
echo $inherited->viaStatic(), "\n";
echo $inherited->viaDynamic(), "\n";
$redeclared = new LatePrivateRedeclared();
echo $redeclared->viaThis(), "\n";
try {
echo $redeclared->viaStatic(), "\n";
} catch (Error $error) {
echo get_class($error), "\n";
}
try {
echo $redeclared->viaDynamic(), "\n";
} catch (Error $error) {
echo get_class($error), "\n";
}
}
?>
--EXPECT--
base
base
base
base
Error
Error

@ -0,0 +1,138 @@
--TEST--
toStdList and toStdDict validate converted PHP arrays and preserve typed assignment
--FILE--
<?php
class ConvertedValue {}
class ConvertedChild extends ConvertedValue {}
class ConvertedSource {
public function toArray(): array { return ['answer' => 42]; }
}
#[Native]
class ConvertedNativeSource {
public function toArray(): array { return [12]; }
}
function make_source(): array {
echo "source called\n";
return [13];
}
function receive_list(#[StdList(Type::Int)] array $values): int {
return $values[0];
}
function main(): void {
$rawList = [4, 5];
$list = $rawList->toStdList(Type::Int);
$copy = $list->toStdList(Type::Int);
$copy[] = 6;
var_dump($list, $copy);
$rawDict = ['answer' => 42];
$dict = $rawDict->toStdDict(Type::Str, Type::Int);
var_dump($dict['answer']);
$number = 9;
$fromScalar = $number->toStdList(Type::Int);
var_dump($fromScalar);
$object = new ConvertedSource();
$fromObject = $object->toStdDict(Type::Str, Type::Int);
var_dump($fromObject);
$dynamic = std::any([7, 8]);
$fromDynamic = $dynamic->toStdList(Type::Int);
var_dump($fromDynamic[1], receive_list($rawList->toStdList(Type::Int)));
$integerKeys = [10 => 'ten'];
$integerDict = $integerKeys->toStdDict(Type::Int, Type::Str);
var_dump($integerDict[10]);
$vector = std::vector(Type::Int);
$vector[] = 11;
$fromVector = $vector->toStdList(Type::Int);
var_dump($fromVector[0]);
$native = new ConvertedNativeSource();
$fromNative = $native->toStdList(Type::Int);
var_dump($fromNative[0]);
$fromCall = make_source()->toStdList(Type::Int);
var_dump($fromCall[0]);
$rawObjects = [new ConvertedValue(), new ConvertedChild()];
$objects = $rawObjects->toStdList(ConvertedValue::class);
var_dump(count($objects), $objects[1] instanceof ConvertedChild);
try {
$badListKeys = ['name' => 1];
$badListKeys->toStdList(Type::Int);
} catch (TypeError $error) { echo "list key rejected\n"; }
try {
$badListValues = [1, 'two'];
$badListValues->toStdList(Type::Int);
} catch (TypeError $error) { echo "list value rejected\n"; }
try {
$badDictKeys = [1 => 1];
$badDictKeys->toStdDict(Type::Str, Type::Int);
} catch (TypeError $error) { echo "dict key rejected\n"; }
try {
$normalizedNumericKey = ['123' => 1];
$normalizedNumericKey->toStdDict(Type::Str, Type::Int);
} catch (TypeError $error) { echo "normalized key rejected\n"; }
try {
$badDictValues = ['one' => 'wrong'];
$badDictValues->toStdDict(Type::Str, Type::Int);
} catch (TypeError $error) { echo "dict value rejected\n"; }
try {
$badObjects = [new stdClass()];
$badObjects->toStdList(ConvertedValue::class);
} catch (TypeError $error) { echo "class rejected\n"; }
try {
$otherType = std::list(Type::Str);
$otherType[] = 'not an integer';
$otherType->toStdList(Type::Int);
} catch (TypeError $error) { echo "different contract rejected\n"; }
}
?>
--EXPECT--
array(2) {
[0]=>
int(4)
[1]=>
int(5)
}
array(3) {
[0]=>
int(4)
[1]=>
int(5)
[2]=>
int(6)
}
int(42)
array(1) {
[0]=>
int(9)
}
array(1) {
["answer"]=>
int(42)
}
int(8)
int(4)
string(3) "ten"
int(11)
int(12)
source called
int(13)
int(2)
bool(true)
list key rejected
list value rejected
dict key rejected
normalized key rejected
dict value rejected
class rejected
different contract rejected
Loading…
Cancel
Save