- 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