parent
d5f0a113e6
commit
de8fdaf464
57 changed files with 1287 additions and 16 deletions
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeArrayAccessIsset {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeArrayAccessIsset(); |
||||
var_dump(isset($value[0])); |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeArrayAccessUnset {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeArrayAccessUnset(); |
||||
unset($value[0]); |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeArrayAccessWrite {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeArrayAccessWrite(); |
||||
$value[0] = 1; |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeArrayAccessOperand {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeArrayAccessOperand(); |
||||
var_dump($value[0]); |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeArrayDimKey {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$key = new NativeArrayDimKey(); |
||||
$values = []; |
||||
$values[$key] = 'value'; |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeArrayKey {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$key = new NativeArrayKey(); |
||||
$values = [$key => 'value']; |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeBoxProperty |
||||
{ |
||||
public Box $value; |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeCoalesceExpected {} |
||||
|
||||
#[Native] |
||||
class NativeCoalesceWrong {} |
||||
|
||||
function maybeExpected(): ?NativeCoalesceExpected |
||||
{ |
||||
return null; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = maybeExpected(); |
||||
$value ??= new NativeCoalesceWrong(); |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeCoalescePropertyExpected {} |
||||
|
||||
#[Native] |
||||
class NativeCoalescePropertyWrong {} |
||||
|
||||
#[Native] |
||||
class NativeCoalesceHolder |
||||
{ |
||||
public ?NativeCoalescePropertyExpected $value; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$holder = new NativeCoalesceHolder(); |
||||
$holder->value ??= new NativeCoalescePropertyWrong(); |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeCompoundOperand {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeCompoundOperand(); |
||||
$value += 1; |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeDynamicConstantTarget |
||||
{ |
||||
public const VALUE = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$target = new NativeDynamicConstantTarget(); |
||||
var_dump($target::VALUE); |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeDynamicNewTarget {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$target = new NativeDynamicNewTarget(); |
||||
$value = new $target(); |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeDynamicStaticTarget |
||||
{ |
||||
public function method(): void {} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$target = new NativeDynamicStaticTarget(); |
||||
$target::method(); |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeForeachValue |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeForeachValue(); |
||||
foreach ($value as $item) { |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,19 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeGeneratorFactoryValue |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function createNativeGeneratorValue(): NativeGeneratorFactoryValue |
||||
{ |
||||
return new NativeGeneratorFactoryValue(); |
||||
} |
||||
|
||||
function nativeGeneratorFactory(): iterable |
||||
{ |
||||
$value = createNativeGeneratorValue(); |
||||
yield $value->value; |
||||
} |
||||
|
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeGeneratorLocal |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function nativeGeneratorLocal(): iterable |
||||
{ |
||||
$value = new NativeGeneratorLocal(); |
||||
yield $value->value; |
||||
} |
||||
|
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeGeneratorMethod |
||||
{ |
||||
public function values(): iterable |
||||
{ |
||||
yield 1; |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeGeneratorParameter |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function nativeGeneratorParameter(NativeGeneratorParameter $value): iterable |
||||
{ |
||||
yield $value->value; |
||||
} |
||||
|
||||
@ -0,0 +1,17 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeGeneratorYieldValue |
||||
{ |
||||
} |
||||
|
||||
function createNativeGeneratorYieldValue(): NativeGeneratorYieldValue |
||||
{ |
||||
return new NativeGeneratorYieldValue(); |
||||
} |
||||
|
||||
function nativeGeneratorYield(): iterable |
||||
{ |
||||
yield createNativeGeneratorYieldValue(); |
||||
} |
||||
|
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeIncrementOperand {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeIncrementOperand(); |
||||
$value++; |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeReflectionClassValue |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new ReflectionClass(NativeReflectionClassValue::class); |
||||
} |
||||
|
||||
@ -0,0 +1,18 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeReflectionMemberValue |
||||
{ |
||||
public int $value; |
||||
|
||||
public function read(): int |
||||
{ |
||||
return $this->value; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new ReflectionMethod(NativeReflectionMemberValue::class, 'read'); |
||||
} |
||||
|
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeSwitchCase {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeSwitchCase(); |
||||
switch (1) { |
||||
case $value: |
||||
break; |
||||
} |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeSwitchOperand {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeSwitchOperand(); |
||||
switch ($value) { |
||||
case null: |
||||
break; |
||||
} |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeThrownValue |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeThrownValue(); |
||||
throw $value; |
||||
} |
||||
|
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeUnaryOperand {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeUnaryOperand(); |
||||
$result = -$value; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeVariableMethod |
||||
{ |
||||
public function method(): void {} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeVariableMethod(); |
||||
$method = 'method'; |
||||
$value->$method(); |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeVariableProperty |
||||
{ |
||||
public int $property = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeVariableProperty(); |
||||
$property = 'property'; |
||||
var_dump($value->$property); |
||||
} |
||||
@ -0,0 +1,22 @@ |
||||
--TEST-- |
||||
Native class: exit converts through the native string method |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeExitValue |
||||
{ |
||||
public function toString(): string |
||||
{ |
||||
return 'native exit'; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
exit(new NativeExitValue()); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
native exit |
||||
@ -0,0 +1,43 @@ |
||||
--TEST-- |
||||
Native class: root frames remain valid across non-LIFO Fiber suspension |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeFiberValue |
||||
{ |
||||
public string $name; |
||||
|
||||
public function __construct(string $name) |
||||
{ |
||||
$this->name = $name; |
||||
} |
||||
} |
||||
|
||||
function suspendedNativeFrame(): void |
||||
{ |
||||
$value = new NativeFiberValue('suspended'); |
||||
Fiber::suspend(); |
||||
echo $value->name, "\n"; |
||||
} |
||||
|
||||
function resumeFromNewerNativeFrame(Fiber $fiber): void |
||||
{ |
||||
$value = new NativeFiberValue('newer'); |
||||
$fiber->resume(); |
||||
echo $value->name, "\n"; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$fiber = new Fiber(static function (): void { |
||||
suspendedNativeFrame(); |
||||
}); |
||||
$fiber->start(); |
||||
resumeFromNewerNativeFrame($fiber); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
suspended |
||||
newer |
||||
@ -0,0 +1,40 @@ |
||||
--TEST-- |
||||
Native class: request shutdown safely detaches roots owned by suspended Fibers |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeFiberShutdownValue |
||||
{ |
||||
public string $name; |
||||
|
||||
public function __construct(string $name) |
||||
{ |
||||
$this->name = $name; |
||||
} |
||||
} |
||||
|
||||
function suspendUntilRequestShutdown(): void |
||||
{ |
||||
$value = new NativeFiberShutdownValue('alive'); |
||||
Fiber::suspend(); |
||||
|
||||
// This frame intentionally remains suspended until request shutdown. |
||||
echo $value->name, "\n"; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
global $suspendedFiber; |
||||
|
||||
$suspendedFiber = new Fiber(static function (): void { |
||||
suspendUntilRequestShutdown(); |
||||
}); |
||||
$suspendedFiber->start(); |
||||
|
||||
echo "suspended\n"; |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
suspended |
||||
@ -0,0 +1,28 @@ |
||||
--TEST-- |
||||
Native class: long-running allocation triggers automatic tracing collection |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeGcThresholdValue |
||||
{ |
||||
public function __destruct() |
||||
{ |
||||
global $nativeGcFinalized; |
||||
$nativeGcFinalized++; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
global $nativeGcFinalized; |
||||
$nativeGcFinalized = 0; |
||||
for ($i = 0; $i < 300000; $i++) { |
||||
$value = new NativeGcThresholdValue(); |
||||
} |
||||
var_dump($nativeGcFinalized > 0); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
@ -0,0 +1,4 @@ |
||||
<?php |
||||
|
||||
var_dump(isset($nativeValue)); |
||||
|
||||
@ -0,0 +1,19 @@ |
||||
--TEST-- |
||||
Native class: include scope does not expose raw native pointers to ZendVM |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeIncludeScopeValue {} |
||||
|
||||
function main(): void |
||||
{ |
||||
$nativeValue = new NativeIncludeScopeValue(); |
||||
include __DIR__ . '/include-native-scope.inc'; |
||||
var_dump($nativeValue instanceof NativeIncludeScopeValue); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
bool(false) |
||||
bool(true) |
||||
@ -0,0 +1,60 @@ |
||||
--TEST-- |
||||
Native class: match compares object identity without entering ZendVM |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeMatchValue {} |
||||
|
||||
#[Native] |
||||
class NativeMatchUnrelated {} |
||||
|
||||
function nativeMatchIdentity(NativeMatchValue $value): NativeMatchValue |
||||
{ |
||||
echo "subject\n"; |
||||
return $value; |
||||
} |
||||
|
||||
function unrelatedMatchValue(): NativeMatchUnrelated |
||||
{ |
||||
echo "unrelated\n"; |
||||
return new NativeMatchUnrelated(); |
||||
} |
||||
|
||||
function scalarMatchValue(): int |
||||
{ |
||||
echo "scalar\n"; |
||||
return 42; |
||||
} |
||||
|
||||
function choose(?NativeMatchValue $subject, NativeMatchValue $same, NativeMatchValue $other): string |
||||
{ |
||||
return match ($subject) { |
||||
$other => 'other', |
||||
$same => 'same', |
||||
null => 'null', |
||||
}; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeMatchValue(); |
||||
$other = new NativeMatchValue(); |
||||
|
||||
var_dump(choose($value, $value, $other)); |
||||
var_dump(choose(null, $value, $other)); |
||||
var_dump(match (nativeMatchIdentity($value)) { |
||||
unrelatedMatchValue() => 'unrelated', |
||||
scalarMatchValue() => 'scalar', |
||||
$value => 'identity', |
||||
}); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
string(4) "same" |
||||
string(4) "null" |
||||
subject |
||||
unrelated |
||||
scalar |
||||
string(8) "identity" |
||||
@ -0,0 +1,46 @@ |
||||
--TEST-- |
||||
Native class: strict identity never coerces raw pointers to PHP values |
||||
--FILE-- |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativeStrictIdentity {} |
||||
|
||||
function nativeIdentityOperand(NativeStrictIdentity $value): NativeStrictIdentity |
||||
{ |
||||
echo "native\n"; |
||||
return $value; |
||||
} |
||||
|
||||
function zendIdentityOperand(): bool |
||||
{ |
||||
echo "zend\n"; |
||||
return true; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = new NativeStrictIdentity(); |
||||
$alias = $value; |
||||
$other = new NativeStrictIdentity(); |
||||
|
||||
var_dump($value === $alias); |
||||
var_dump($value === $other); |
||||
var_dump($value === true); |
||||
var_dump($value !== true); |
||||
var_dump(nativeIdentityOperand($value) === zendIdentityOperand()); |
||||
var_dump(zendIdentityOperand() === nativeIdentityOperand($value)); |
||||
} |
||||
|
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
bool(false) |
||||
bool(false) |
||||
bool(true) |
||||
native |
||||
zend |
||||
bool(false) |
||||
zend |
||||
native |
||||
bool(false) |
||||
Loading…
Reference in new issue