-
Notifications
You must be signed in to change notification settings - Fork 7.9k
[RFC] Allow hooks for backed readonly
properties
#18757
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
--TEST-- | ||
Backed property in readonly class may have hooks | ||
--FILE-- | ||
<?php | ||
|
||
// readonly class | ||
readonly class Test { | ||
public int $prop { | ||
get => $this->prop; | ||
set => $value; | ||
} | ||
|
||
public function __construct(int $v) { | ||
$this->prop = $v; | ||
} | ||
|
||
public function set($v) | ||
{ | ||
$this->prop = $v; | ||
} | ||
} | ||
|
||
$t = new Test(42); | ||
var_dump($t->prop); | ||
try { | ||
$t->set(43); | ||
} catch (Error $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
try { | ||
$t->prop = 43; | ||
} catch (Error $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
var_dump($t->prop); | ||
?> | ||
--EXPECT-- | ||
int(42) | ||
Cannot modify readonly property Test::$prop | ||
Cannot modify protected(set) readonly property Test::$prop from global scope | ||
int(42) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test does not appear to belong to this PR. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--TEST-- | ||
Non-readonly class cannot extend readonly class | ||
--FILE-- | ||
<?php | ||
|
||
readonly class ParentClass { | ||
public int $prop; | ||
} | ||
|
||
class Test extends ParentClass { | ||
public function __construct( | ||
public int $prop { | ||
get => $this->prop; | ||
set => $value; | ||
} | ||
) {} | ||
} | ||
|
||
?> | ||
--EXPECTF-- | ||
Fatal error: Non-readonly class Test cannot extend readonly class ParentClass in %s on line %d |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--TEST-- | ||
Readonly class cannot extend non-readonly class | ||
--FILE-- | ||
<?php | ||
|
||
class ParentClass { | ||
public int $prop; | ||
} | ||
|
||
readonly class Test extends ParentClass { | ||
public function __construct( | ||
public int $prop { | ||
get => $this->prop; | ||
set => $value; | ||
} | ||
) {} | ||
} | ||
|
||
?> | ||
--EXPECTF-- | ||
Fatal error: Readonly class Test cannot extend non-readonly class ParentClass in %s on line %d |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,40 @@ | ||||||
--TEST-- | ||||||
Backed promoted property in readonly class may have hooks | ||||||
--FILE-- | ||||||
<?php | ||||||
|
||||||
// readonly class, promoted | ||||||
readonly class Test { | ||||||
public function __construct( | ||||||
public int $prop { | ||||||
get => $this->prop; | ||||||
set => $value; | ||||||
} | ||||||
) {} | ||||||
|
||||||
public function set($v) | ||||||
{ | ||||||
$this->prop = $v; | ||||||
} | ||||||
} | ||||||
|
||||||
$t = new Test(42); | ||||||
var_dump($t->prop); | ||||||
try { | ||||||
$t->set(43); | ||||||
} catch (Error $e) { | ||||||
echo $e->getMessage(), "\n"; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
For consistency with other tests (applies to the entire PR). |
||||||
} | ||||||
try { | ||||||
$t->prop = 43; | ||||||
} catch (Error $e) { | ||||||
echo $e->getMessage(), "\n"; | ||||||
} | ||||||
var_dump($t->prop); | ||||||
?> | ||||||
--EXPECT-- | ||||||
int(42) | ||||||
Cannot modify readonly property Test::$prop | ||||||
Cannot modify protected(set) readonly property Test::$prop from global scope | ||||||
int(42) | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this appears to be a newline too many. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
--TEST-- | ||
Virtual promoted property in readonly class cannot have hooks | ||
--FILE-- | ||
<?php | ||
|
||
readonly class Test { | ||
public function __construct( | ||
public int $prop { | ||
get => 42; | ||
} | ||
) {} | ||
} | ||
|
||
?> | ||
--EXPECTF-- | ||
Fatal error: Hooked virtual properties cannot be readonly in %s on line %d |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
--TEST-- | ||
Backed readonly property may have hooks | ||
--FILE-- | ||
<?php | ||
|
||
// readonly property | ||
class Test { | ||
public readonly int $prop { | ||
get => $this->prop; | ||
set => $value; | ||
} | ||
|
||
public function __construct(int $v) { | ||
$this->prop = $v; | ||
} | ||
|
||
public function set($v) | ||
{ | ||
$this->prop = $v; | ||
} | ||
} | ||
|
||
$t = new Test(42); | ||
var_dump($t->prop); | ||
try { | ||
$t->set(43); | ||
} catch (Error $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
try { | ||
$t->prop = 43; | ||
} catch (Error $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
var_dump($t->prop); | ||
|
||
// class readonly | ||
final readonly class Foo | ||
{ | ||
public function __construct( | ||
public array $values { | ||
set(array $value) => array_map(strtoupper(...), $value); | ||
}, | ||
) {} | ||
} | ||
|
||
// property readonly | ||
final class Foo2 | ||
{ | ||
public function __construct( | ||
public readonly array $values { | ||
set(array $value) => array_map(strtoupper(...), $value); | ||
}, | ||
) {} | ||
} | ||
|
||
// redundant readonly | ||
final readonly class Foo3 | ||
{ | ||
public function __construct( | ||
public readonly array $values { | ||
set(array $value) => array_map(strtoupper(...), $value); | ||
get => $this->makeNicer($this->values); | ||
}, | ||
) {} | ||
|
||
public function makeNicer(array $entries): array | ||
{ | ||
return array_map( | ||
fn($i, $entry) => $entry . strtoupper(['', 'r', 'st'][$i]), array_keys($entries), | ||
$entries | ||
); | ||
} | ||
} | ||
|
||
\var_dump(new Foo(['yo,', 'you', 'can'])->values); | ||
\var_dump(new Foo2(['just', 'do', 'things'])->values); | ||
\var_dump(new Foo3(['nice', 'nice', 'nice'])->values); | ||
?> | ||
--EXPECT-- | ||
int(42) | ||
Cannot modify readonly property Test::$prop | ||
Cannot modify protected(set) readonly property Test::$prop from global scope | ||
int(42) | ||
array(3) { | ||
[0]=> | ||
string(3) "YO," | ||
[1]=> | ||
string(3) "YOU" | ||
[2]=> | ||
string(3) "CAN" | ||
} | ||
array(3) { | ||
[0]=> | ||
string(4) "JUST" | ||
[1]=> | ||
string(2) "DO" | ||
[2]=> | ||
string(6) "THINGS" | ||
} | ||
array(3) { | ||
[0]=> | ||
string(4) "NICE" | ||
[1]=> | ||
string(5) "NICER" | ||
[2]=> | ||
string(6) "NICEST" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--TEST-- | ||
Backed property cannot redeclare readonly as non-readonly property | ||
--FILE-- | ||
<?php | ||
|
||
class ParentClass { | ||
public readonly int $prop; | ||
} | ||
|
||
class Test extends ParentClass { | ||
public function __construct( | ||
public int $prop { | ||
get => $this->prop; | ||
set => $value; | ||
} | ||
) {} | ||
} | ||
|
||
?> | ||
--EXPECTF-- | ||
Fatal error: Cannot redeclare readonly property ParentClass::$prop as non-readonly Test::$prop %s on line %d |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--TEST-- | ||
Backed property cannot redeclare non-readonly as readonly property | ||
--FILE-- | ||
<?php | ||
|
||
class ParentClass { | ||
public int $prop; | ||
} | ||
|
||
class Test extends ParentClass { | ||
public function __construct( | ||
public readonly int $prop { | ||
get => $this->prop; | ||
set => $value; | ||
} | ||
) {} | ||
} | ||
|
||
?> | ||
--EXPECTF-- | ||
Fatal error: Cannot redeclare non-readonly property ParentClass::$prop as readonly Test::$prop in %s on line %d |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure to include the trailing newline at EOF (also applies to other tests).