- Implement validation for promoted asymmetric properties requiring explicit types - Add error handling for promoted asymmetric properties with wider set visibility - Support constructor property promotion with asymmetric visibility modifiers - Enable proper scope checking for promoted asymmetric properties in Zend-backed objects - Preserve promoted/set visibility and implicit final reflection flags - Add comprehensive test coverage for promoted asymmetric property scenarios - Implement native object compilation support for asymmetric property access checks - Remove pending status for ReflectionProperty::isPromoted() implementation - Update documentation to reflect complete asymmetric property visibility supportmaster
parent
36fd0228f0
commit
9f1cf07511
13 changed files with 331 additions and 4 deletions
@ -0,0 +1,18 @@ |
||||
<?php |
||||
|
||||
class FinalPropertySetHookParent |
||||
{ |
||||
public string $value { |
||||
get => 'parent'; |
||||
final set { |
||||
} |
||||
} |
||||
} |
||||
|
||||
class FinalPropertySetHookChild extends FinalPropertySetHookParent |
||||
{ |
||||
public string $value { |
||||
set { |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
class PromotedPrivateSetParent |
||||
{ |
||||
public function __construct( |
||||
public private(set) string $value, |
||||
) { |
||||
} |
||||
} |
||||
|
||||
class PromotedPrivateSetChild extends PromotedPrivateSetParent |
||||
{ |
||||
public string $value = 'child'; |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
<?php |
||||
|
||||
#[Native] |
||||
class NativePromotedPrivateSetExternalWrite |
||||
{ |
||||
public function __construct( |
||||
public private(set) int $value, |
||||
) { |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new NativePromotedPrivateSetExternalWrite(1); |
||||
$object->value = 2; |
||||
} |
||||
@ -0,0 +1,9 @@ |
||||
<?php |
||||
|
||||
class PromotedAsymmetricUntyped |
||||
{ |
||||
public function __construct( |
||||
public private(set) $value, |
||||
) { |
||||
} |
||||
} |
||||
@ -0,0 +1,9 @@ |
||||
<?php |
||||
|
||||
class PromotedAsymmetricWiderSet |
||||
{ |
||||
public function __construct( |
||||
private protected(set) string $value, |
||||
) { |
||||
} |
||||
} |
||||
@ -0,0 +1,171 @@ |
||||
--TEST-- |
||||
PHP 8.4 promoted properties support asymmetric set visibility |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class PromotedPrivateSet |
||||
{ |
||||
public function __construct( |
||||
public private(set) string $name = 'initial', |
||||
) { |
||||
} |
||||
|
||||
public function rename(string $name): void |
||||
{ |
||||
$this->name = $name; |
||||
} |
||||
} |
||||
|
||||
class PromotedProtectedSet |
||||
{ |
||||
public function __construct( |
||||
public protected(set) int $score = 0, |
||||
) { |
||||
} |
||||
|
||||
public function setFromParent(int $score): void |
||||
{ |
||||
$this->score = $score; |
||||
} |
||||
} |
||||
|
||||
class PromotedProtectedSetChild extends PromotedProtectedSet |
||||
{ |
||||
public function setFromChild(int $score): void |
||||
{ |
||||
$this->score = $score; |
||||
} |
||||
} |
||||
|
||||
class PromotedImplicitPublicPrivateSet |
||||
{ |
||||
public function __construct( |
||||
private(set) string $token, |
||||
) { |
||||
} |
||||
|
||||
public function replace(string $token): void |
||||
{ |
||||
$this->token = $token; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativePromotedPrivateSet |
||||
{ |
||||
public function __construct( |
||||
public private(set) int $value, |
||||
) { |
||||
} |
||||
|
||||
public function increment(): void |
||||
{ |
||||
$this->value++; |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativePromotedProtectedSet |
||||
{ |
||||
public function __construct( |
||||
public protected(set) int $score, |
||||
) { |
||||
} |
||||
} |
||||
|
||||
#[Native] |
||||
class NativePromotedProtectedSetChild extends NativePromotedProtectedSet |
||||
{ |
||||
public function update(int $score): void |
||||
{ |
||||
$this->score = $score; |
||||
} |
||||
} |
||||
|
||||
function rejectExternalWrites(mixed $private, mixed $protected): void |
||||
{ |
||||
try { |
||||
$private->name = 'external'; |
||||
} catch (Error) { |
||||
echo "private blocked\n"; |
||||
} |
||||
try { |
||||
$protected->score = 99; |
||||
} catch (Error) { |
||||
echo "protected blocked\n"; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$defaultPrivate = new PromotedPrivateSet(); |
||||
var_dump($defaultPrivate->name); |
||||
|
||||
$private = new PromotedPrivateSet('constructor'); |
||||
var_dump($private->name); |
||||
$private->rename('class'); |
||||
var_dump($private->name); |
||||
|
||||
$protected = new PromotedProtectedSetChild(10); |
||||
$protected->setFromParent(20); |
||||
$protected->setFromChild(30); |
||||
var_dump($protected->score); |
||||
|
||||
$implicit = new PromotedImplicitPublicPrivateSet('implicit'); |
||||
var_dump($implicit->token); |
||||
$implicit->replace('replaced'); |
||||
var_dump($implicit->token); |
||||
|
||||
$native = new NativePromotedPrivateSet(40); |
||||
$native->increment(); |
||||
var_dump($native->value); |
||||
|
||||
$nativeChild = new NativePromotedProtectedSetChild(50); |
||||
$nativeChild->update(51); |
||||
var_dump($nativeChild->score); |
||||
|
||||
rejectExternalWrites($private, $protected); |
||||
var_dump($private->name, $protected->score); |
||||
|
||||
$privateProperty = new ReflectionProperty(PromotedPrivateSet::class, 'name'); |
||||
var_dump( |
||||
$privateProperty->isPromoted(), |
||||
$privateProperty->isPrivateSet(), |
||||
$privateProperty->isFinal(), |
||||
); |
||||
$protectedProperty = new ReflectionProperty(PromotedProtectedSet::class, 'score'); |
||||
var_dump( |
||||
$protectedProperty->isPromoted(), |
||||
$protectedProperty->isProtectedSet(), |
||||
$protectedProperty->isFinal(), |
||||
); |
||||
$implicitProperty = new ReflectionProperty(PromotedImplicitPublicPrivateSet::class, 'token'); |
||||
var_dump( |
||||
$implicitProperty->isPublic(), |
||||
$implicitProperty->isPrivateSet(), |
||||
$implicitProperty->isFinal(), |
||||
); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(7) "initial" |
||||
string(11) "constructor" |
||||
string(5) "class" |
||||
int(30) |
||||
string(8) "implicit" |
||||
string(8) "replaced" |
||||
int(41) |
||||
int(51) |
||||
private blocked |
||||
protected blocked |
||||
string(5) "class" |
||||
int(30) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
bool(false) |
||||
bool(true) |
||||
bool(true) |
||||
bool(true) |
||||
Loading…
Reference in new issue