parent
13344ace72
commit
7d1a90a340
24 changed files with 784 additions and 17 deletions
@ -0,0 +1,8 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
function native_anonymous_class(): void |
||||||
|
{ |
||||||
|
$value = new #[Native] class { |
||||||
|
public int $value = 1; |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativePrivateMethodAccess |
||||||
|
{ |
||||||
|
private function hidden(): void {} |
||||||
|
} |
||||||
|
|
||||||
|
function native_private_method_access(): void |
||||||
|
{ |
||||||
|
$value = new NativePrivateMethodAccess(); |
||||||
|
$value->hidden(); |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeProtectedMethodAccess |
||||||
|
{ |
||||||
|
protected function hidden(): void {} |
||||||
|
} |
||||||
|
|
||||||
|
function native_protected_method_access(): void |
||||||
|
{ |
||||||
|
$value = new NativeProtectedMethodAccess(); |
||||||
|
$value->hidden(); |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeSerializeBoundary {} |
||||||
|
|
||||||
|
function native_serialize_boundary(): void |
||||||
|
{ |
||||||
|
$value = new NativeSerializeBoundary(); |
||||||
|
serialize($value); |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeStaticSignature |
||||||
|
{ |
||||||
|
public function identity(): static |
||||||
|
{ |
||||||
|
return $this; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
trait NativeTraitStaticSignatureProvider |
||||||
|
{ |
||||||
|
public function identity(): static |
||||||
|
{ |
||||||
|
return $this; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeTraitStaticSignature |
||||||
|
{ |
||||||
|
use NativeTraitStaticSignatureProvider; |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeWeakReferenceBoundary {} |
||||||
|
|
||||||
|
function native_weak_reference_boundary(): void |
||||||
|
{ |
||||||
|
$value = new NativeWeakReferenceBoundary(); |
||||||
|
WeakReference::create($value); |
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
final class GenStubVersionFlagsTest extends BaseTest |
||||||
|
{ |
||||||
|
public function testFlagIntroducedBeforeMinimumSupportedVersionRemainsEnabled(): void |
||||||
|
{ |
||||||
|
$flags = new VersionFlags(['ZEND_ACC_PRIVATE']); |
||||||
|
$flags->addForVersionsAbove('ZEND_ACC_STATIC', PHP_70_VERSION_ID); |
||||||
|
|
||||||
|
self::assertSame( |
||||||
|
'ZEND_ACC_PRIVATE|ZEND_ACC_STATIC', |
||||||
|
$flags->generateVersionDependentFlagCode('%s', null), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: constructor arguments are evaluated left-to-right and rooted |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeConstructorArgument |
||||||
|
{ |
||||||
|
public string $name; |
||||||
|
|
||||||
|
public function __construct(string $name) |
||||||
|
{ |
||||||
|
$this->name = $name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeConstructorPressure |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeConstructorPair |
||||||
|
{ |
||||||
|
public NativeConstructorArgument $first; |
||||||
|
public NativeConstructorArgument $second; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
NativeConstructorArgument $first, |
||||||
|
NativeConstructorArgument $second, |
||||||
|
) { |
||||||
|
$this->first = $first; |
||||||
|
$this->second = $second; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function makeNativeConstructorArgument(string $name): NativeConstructorArgument |
||||||
|
{ |
||||||
|
echo 'make:', $name, PHP_EOL; |
||||||
|
if ($name === 'B') { |
||||||
|
for ($i = 0; $i < 300000; $i++) { |
||||||
|
$filler = new NativeConstructorPressure(); |
||||||
|
} |
||||||
|
} |
||||||
|
return new NativeConstructorArgument($name); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$pair = new NativeConstructorPair( |
||||||
|
makeNativeConstructorArgument('A'), |
||||||
|
makeNativeConstructorArgument('B'), |
||||||
|
); |
||||||
|
echo $pair->first->name, ':', $pair->second->name, PHP_EOL; |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
make:A |
||||||
|
make:B |
||||||
|
A:B |
||||||
@ -0,0 +1,150 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: objects published before constructor or clone failure never become dangling |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class NativeFailedLifecycleProbe |
||||||
|
{ |
||||||
|
public function __construct(public string $name) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $released; |
||||||
|
$released[] = $this->name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeFailedConstructionEscape |
||||||
|
{ |
||||||
|
public object $probe; |
||||||
|
public int $value = 42; |
||||||
|
|
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
global $failedConstruction; |
||||||
|
$this->probe = new NativeFailedLifecycleProbe('constructor field'); |
||||||
|
$failedConstruction = $this; |
||||||
|
throw new RuntimeException('constructor failed'); |
||||||
|
} |
||||||
|
|
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $finalized; |
||||||
|
$finalized[] = 'constructor object'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeFailedCloneEscape |
||||||
|
{ |
||||||
|
public object $probe; |
||||||
|
public string $kind = 'source'; |
||||||
|
|
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->probe = new NativeFailedLifecycleProbe('source field'); |
||||||
|
} |
||||||
|
|
||||||
|
public function __clone(): void |
||||||
|
{ |
||||||
|
global $failedClone; |
||||||
|
$this->kind = 'clone'; |
||||||
|
$this->probe = new NativeFailedLifecycleProbe('clone field'); |
||||||
|
$failedClone = $this; |
||||||
|
throw new RuntimeException('clone failed'); |
||||||
|
} |
||||||
|
|
||||||
|
public function __destruct() |
||||||
|
{ |
||||||
|
global $finalized; |
||||||
|
$finalized[] = $this->kind; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeFailedLifecyclePressure |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function forceNativeCollection(): void |
||||||
|
{ |
||||||
|
for ($i = 0; $i < 400000; $i++) { |
||||||
|
$filler = new NativeFailedLifecyclePressure(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
global $failedConstruction, $failedClone, $released, $finalized; |
||||||
|
$released = []; |
||||||
|
$finalized = []; |
||||||
|
|
||||||
|
try { |
||||||
|
$unused = new NativeFailedConstructionEscape(); |
||||||
|
} catch (RuntimeException $error) { |
||||||
|
echo $error->getMessage(), "\n"; |
||||||
|
} |
||||||
|
var_dump($failedConstruction->value, $failedConstruction->probe->name); |
||||||
|
$failedConstruction = null; |
||||||
|
forceNativeCollection(); |
||||||
|
var_dump($released, $finalized); |
||||||
|
|
||||||
|
$source = new NativeFailedCloneEscape(); |
||||||
|
try { |
||||||
|
$unusedClone = clone $source; |
||||||
|
} catch (RuntimeException $error) { |
||||||
|
echo $error->getMessage(), "\n"; |
||||||
|
} |
||||||
|
var_dump($failedClone->kind, $failedClone === $source, $failedClone->probe->name); |
||||||
|
$failedClone = null; |
||||||
|
forceNativeCollection(); |
||||||
|
var_dump($released, $finalized); |
||||||
|
|
||||||
|
$source = null; |
||||||
|
forceNativeCollection(); |
||||||
|
var_dump($released, $finalized); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
constructor failed |
||||||
|
int(42) |
||||||
|
string(17) "constructor field" |
||||||
|
array(1) { |
||||||
|
[0]=> |
||||||
|
string(17) "constructor field" |
||||||
|
} |
||||||
|
array(0) { |
||||||
|
} |
||||||
|
clone failed |
||||||
|
string(5) "clone" |
||||||
|
bool(false) |
||||||
|
string(11) "clone field" |
||||||
|
array(2) { |
||||||
|
[0]=> |
||||||
|
string(17) "constructor field" |
||||||
|
[1]=> |
||||||
|
string(11) "clone field" |
||||||
|
} |
||||||
|
array(1) { |
||||||
|
[0]=> |
||||||
|
string(5) "clone" |
||||||
|
} |
||||||
|
array(3) { |
||||||
|
[0]=> |
||||||
|
string(17) "constructor field" |
||||||
|
[1]=> |
||||||
|
string(11) "clone field" |
||||||
|
[2]=> |
||||||
|
string(12) "source field" |
||||||
|
} |
||||||
|
array(2) { |
||||||
|
[0]=> |
||||||
|
string(5) "clone" |
||||||
|
[1]=> |
||||||
|
string(6) "source" |
||||||
|
} |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: BigInt, BigFloat and Decimal properties retain typed value semantics |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeHighPrecisionValues |
||||||
|
{ |
||||||
|
public BigInt $integer; |
||||||
|
public BigFloat $floating; |
||||||
|
public Decimal $decimal; |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeHighPrecisionPressure |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
function createNativePressure(): void |
||||||
|
{ |
||||||
|
for ($i = 0; $i < 300000; $i++) { |
||||||
|
$filler = new NativeHighPrecisionPressure(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$values = new NativeHighPrecisionValues(); |
||||||
|
var_dump($values->integer, $values->floating, $values->decimal); |
||||||
|
|
||||||
|
$values->integer = std::bigInt('123456789012345678901234567890'); |
||||||
|
$values->floating = std::bigFloat('3.141592653589793238462643383279'); |
||||||
|
$values->decimal = std::decimal('99.125'); |
||||||
|
|
||||||
|
createNativePressure(); |
||||||
|
|
||||||
|
echo ($values->integer + 10)->toString(), "\n"; |
||||||
|
echo $values->floating->toString(), "\n"; |
||||||
|
echo ($values->decimal * 2)->toString(), "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
NULL |
||||||
|
NULL |
||||||
|
NULL |
||||||
|
123456789012345678901234567900 |
||||||
|
3.141592653589793238462643383279 |
||||||
|
198.250 |
||||||
@ -0,0 +1,56 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: method visibility is resolved at compile time across inheritance |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeVisibilityBase |
||||||
|
{ |
||||||
|
private function baseLabel(): string |
||||||
|
{ |
||||||
|
return 'base-private'; |
||||||
|
} |
||||||
|
|
||||||
|
protected function protectedLabel(): string |
||||||
|
{ |
||||||
|
return 'base-protected'; |
||||||
|
} |
||||||
|
|
||||||
|
public function callBasePrivate(): string |
||||||
|
{ |
||||||
|
return $this->baseLabel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeVisibilityChild extends NativeVisibilityBase |
||||||
|
{ |
||||||
|
private function childLabel(): string |
||||||
|
{ |
||||||
|
return 'child-private'; |
||||||
|
} |
||||||
|
|
||||||
|
public function callChildPrivate(): string |
||||||
|
{ |
||||||
|
return $this->childLabel(); |
||||||
|
} |
||||||
|
|
||||||
|
public function callProtected(): string |
||||||
|
{ |
||||||
|
return $this->protectedLabel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$value = new NativeVisibilityChild(); |
||||||
|
echo $value->callBasePrivate(), PHP_EOL; |
||||||
|
echo $value->callChildPrivate(), PHP_EOL; |
||||||
|
echo $value->callProtected(), PHP_EOL; |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
base-private |
||||||
|
child-private |
||||||
|
base-protected |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: property hooks preserve native pointer types and temporary roots |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeHookObjectValue |
||||||
|
{ |
||||||
|
public string $name; |
||||||
|
|
||||||
|
public function __construct(string $name) |
||||||
|
{ |
||||||
|
$this->name = $name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeHookObjectPressure |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeHookObjectOwner |
||||||
|
{ |
||||||
|
private ?NativeHookObjectValue $stored; |
||||||
|
|
||||||
|
public ?NativeHookObjectValue $value { |
||||||
|
get { |
||||||
|
return $this->stored; |
||||||
|
} |
||||||
|
set(?NativeHookObjectValue $value) { |
||||||
|
$this->stored = $value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function makeNativeHookValueAfterPressure(string $name): NativeHookObjectValue |
||||||
|
{ |
||||||
|
for ($i = 0; $i < 300000; $i++) { |
||||||
|
$filler = new NativeHookObjectPressure(); |
||||||
|
} |
||||||
|
return new NativeHookObjectValue($name); |
||||||
|
} |
||||||
|
|
||||||
|
function consumeNativeHookValues( |
||||||
|
NativeHookObjectValue $first, |
||||||
|
NativeHookObjectValue $second, |
||||||
|
): void { |
||||||
|
echo $first->name, ':', $second->name, PHP_EOL; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$owner = new NativeHookObjectOwner(); |
||||||
|
$owner->value = new NativeHookObjectValue('first'); |
||||||
|
consumeNativeHookValues( |
||||||
|
$owner->value, |
||||||
|
makeNativeHookValueAfterPressure('second'), |
||||||
|
); |
||||||
|
$owner->value = null; |
||||||
|
echo $owner->value === null ? "NULL\n" : "not-null\n"; |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
first:second |
||||||
|
NULL |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: self and parent signatures preserve typed pointer semantics |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeSignatureBase |
||||||
|
{ |
||||||
|
public ?self $peer; |
||||||
|
|
||||||
|
public function identity(self $value): self |
||||||
|
{ |
||||||
|
return $value; |
||||||
|
} |
||||||
|
|
||||||
|
public function nullable(?self $value): ?self |
||||||
|
{ |
||||||
|
return $value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeSignatureChild extends NativeSignatureBase |
||||||
|
{ |
||||||
|
public ?parent $parentPeer; |
||||||
|
|
||||||
|
public function childIdentity(self $value): self |
||||||
|
{ |
||||||
|
return $value; |
||||||
|
} |
||||||
|
|
||||||
|
public function parentIdentity(parent $value): parent |
||||||
|
{ |
||||||
|
return $value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$base = new NativeSignatureBase(); |
||||||
|
$child = new NativeSignatureChild(); |
||||||
|
$base->peer = $base; |
||||||
|
$child->parentPeer = $child; |
||||||
|
|
||||||
|
var_dump( |
||||||
|
$base->identity($base) === $base, |
||||||
|
$base->nullable(null) === null, |
||||||
|
$child->childIdentity($child) === $child, |
||||||
|
$child->parentIdentity($child) === $child, |
||||||
|
$base->peer === $base, |
||||||
|
$child->parentPeer === $child, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
--TEST-- |
||||||
|
Native class: $this is a native object pointer in value contexts |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
#[Native] |
||||||
|
class NativeThisValue |
||||||
|
{ |
||||||
|
public int $value; |
||||||
|
|
||||||
|
public function __construct(int $value) |
||||||
|
{ |
||||||
|
$this->value = $value; |
||||||
|
} |
||||||
|
|
||||||
|
public function identity(): self |
||||||
|
{ |
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function sameAs(NativeThisValue $other): bool |
||||||
|
{ |
||||||
|
$alias = $this; |
||||||
|
return $alias === $other; |
||||||
|
} |
||||||
|
|
||||||
|
public function passToFunction(): int |
||||||
|
{ |
||||||
|
return readNativeThisValue($this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function readNativeThisValue(NativeThisValue $value): int |
||||||
|
{ |
||||||
|
return $value->value; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$value = new NativeThisValue(42); |
||||||
|
$other = new NativeThisValue(42); |
||||||
|
|
||||||
|
var_dump($value->identity() === $value); |
||||||
|
var_dump($value->sameAs($value)); |
||||||
|
var_dump($value->sameAs($other)); |
||||||
|
var_dump($value->passToFunction()); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
bool(true) |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
int(42) |
||||||
Loading…
Reference in new issue