- 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 typesmaster
parent
a3b5d61ff7
commit
f127dadf5d
3 changed files with 296 additions and 0 deletions
@ -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…
Reference in new issue