Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ PHP NEWS
?? ??? ????, PHP 8.6.0beta1

- Core:
. Allowed readonly properties to declare default values. (NickSdot)
. Changed run-tests.php to run test subprocesses without a shell where
possible. (NickSdot)
. Fixed GH-23083 (SEGV build_trace_args in zend_exceptions.c with
Expand Down
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ PHP 8.6 UPGRADE NOTES
========================================

- Core:
. Readonly properties may now declare default values.
RFC: https://wiki.php.net/rfc/readonly_property_defaults
. It is now possible to use reference assign on WeakMap without the key
needing to be present beforehand.
. It is now possible to define the __debugInfo() magic method on enums.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ Readonly class can extend a readonly class

readonly class Foo
{
public int $prop = 1;
}

readonly class Bar extends Foo
{
public int $prop = 2;
}

readonly class Baz extends Foo {}

var_dump(new Foo()->prop);
var_dump(new Bar()->prop);
var_dump(new Baz()->prop);

?>
--EXPECT--
int(1)
int(2)
int(1)
36 changes: 36 additions & 0 deletions Zend/tests/readonly_classes/readonly_with_property_default.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
Properties of a readonly class may have default values
--FILE--
<?php

readonly class Foo
{
public int $bar = 1;
public ?string $nullable = null;

public function __construct()
{
try {
$this->bar = 2;
} catch (Error $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
}
}

$foo = new Foo();
var_dump($foo->bar);
var_dump($foo->nullable);

try {
$foo->bar = 3;
} catch (Error $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

?>
--EXPECT--
Error: Cannot modify readonly property Foo::$bar
int(1)
NULL
Error: Cannot modify readonly property Foo::$bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Readonly class may use readonly trait property with default value
--FILE--
<?php

trait TDefault {
public readonly int $prop = 2;
}

readonly class A {
use TDefault;
}

var_dump(new A()->prop);

class B {
use TDefault;
}

var_dump(new B()->prop);

?>
--EXPECT--
int(2)
int(2)
59 changes: 59 additions & 0 deletions Zend/tests/readonly_props/readonly_clone_success1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,41 @@ var_dump($foo2);

var_dump(clone $foo2);

class FooWithDefault {
public readonly int $bar = 1;

public function __clone()
{
$this->bar++;
}
}

$fooWithDefault = new FooWithDefault();

var_dump(clone $fooWithDefault);

$fooWithDefault2 = clone $fooWithDefault;
var_dump($fooWithDefault2);

var_dump(clone $fooWithDefault2);

class FooWithDefaultCloneWith {
public readonly int $bar = 1;

public function withBar(int $bar)
{
return clone($this, ['bar' => $bar]);
}
}

$clone = new FooWithDefaultCloneWith();
var_dump($clone);

$clone2 = $clone->withBar(2);
var_dump($clone2);

var_dump($clone2->withBar(0));

?>
--EXPECTF--
object(Foo)#%d (%d) {
Expand All @@ -37,3 +72,27 @@ object(Foo)#%d (%d) {
["bar"]=>
int(3)
}
object(FooWithDefault)#%d (%d) {
["bar"]=>
int(2)
}
object(FooWithDefault)#%d (%d) {
["bar"]=>
int(2)
}
object(FooWithDefault)#%d (%d) {
["bar"]=>
int(3)
}
object(FooWithDefaultCloneWith)#%d (%d) {
["bar"]=>
int(1)
}
object(FooWithDefaultCloneWith)#%d (%d) {
["bar"]=>
int(2)
}
object(FooWithDefaultCloneWith)#%d (%d) {
["bar"]=>
int(0)
}
16 changes: 16 additions & 0 deletions Zend/tests/readonly_props/readonly_modification.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ class Test {
}
}

class TestWithDefault {
public readonly int $prop = 1;

public function __construct() {
try {
$this->prop = 2;
} catch (Error $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
}
}

function byRef(&$ref) {}

$test = new Test;
Expand Down Expand Up @@ -66,6 +78,8 @@ try {
echo $e->getMessage(), "\n";
}

var_dump(new TestWithDefault()->prop);

?>
--EXPECT--
int(1)
Expand All @@ -80,3 +94,5 @@ array(0) {
}
Cannot indirectly modify readonly property Test::$prop2
Cannot indirectly modify readonly property Test::$prop2
Error: Cannot modify readonly property TestWithDefault::$prop
int(1)
13 changes: 13 additions & 0 deletions Zend/tests/readonly_props/readonly_trait_match.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,20 @@ class C {
use T1, T2;
}

trait TDefault1 {
public readonly int $prop = 1;
}
trait TDefault2 {
public readonly int $prop = 1;
}
class CDefault {
use TDefault1, TDefault2;
}

var_dump(new CDefault()->prop);

?>
===DONE===
--EXPECT--
int(1)
===DONE===
32 changes: 29 additions & 3 deletions Zend/tests/readonly_props/readonly_with_default.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,43 @@ Readonly property with default value
--FILE--
<?php

enum E {
case Case;
}

class Test {
public readonly int $prop = 1;
public readonly string $className = self::class;
public readonly ?string $nullable = null;
public readonly array $array = [1, "two" => 2];
public readonly E $enum = E::Case;
public readonly string $enumString = E::Case->name;
}

$test = new Test;
var_dump($test->prop);
var_dump($test->className);
var_dump($test->nullable);
var_dump($test->array);
var_dump($test->enum);
var_dump($test->enumString);
try {
$test->prop = 2;
} catch (Error $e) {
echo $e->getMessage(), "\n";
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}

?>
--EXPECTF--
Fatal error: Readonly property Test::$prop cannot have default value in %s on line %d
--EXPECT--
int(1)
string(4) "Test"
NULL
array(2) {
[0]=>
int(1)
["two"]=>
int(2)
}
enum(E::Case)
string(4) "Case"
Error: Cannot modify readonly property Test::$prop
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Readonly property with default value has restricted set visibility for get/set abstract property
--DESCRIPTION--
The error message should be improved, the set access level comes from readonly.
--FILE--
<?php

abstract class P {
public abstract int $prop { get; set; }
}

class C extends P {
public readonly int $prop = 42;
}

?>
--EXPECTF--
Fatal error: Set access level of C::$prop must be omitted (as in class P) in %s on line %d
Comment thread
NickSdot marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
Readonly property with default value and asymmetric visibility
--FILE--
<?php

class Test {
public readonly int $default = 1;
public private(set) readonly int $private = 2;
public protected(set) readonly int $protected = 3;
public public(set) readonly int $public = 4;
}

$test = new Test();

foreach (['default', 'private', 'protected', 'public'] as $prop) {
$before = $test->$prop;
try {
$test->$prop = 42;
} catch (Error $e) {
echo $e::class, ': ', $e->getMessage(), PHP_EOL;
}
echo "$$prop before $before, after {$test->$prop}", PHP_EOL;
}

?>
--EXPECT--
Error: Cannot modify readonly property Test::$default
$default before 1, after 1
Error: Cannot modify readonly property Test::$private
$private before 2, after 2
Error: Cannot modify readonly property Test::$protected
$protected before 3, after 3
Error: Cannot modify readonly property Test::$public
$public before 4, after 4
40 changes: 40 additions & 0 deletions Zend/tests/readonly_props/readonly_with_default_inheritance.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
Readonly property with default value and inheritance
--FILE--
<?php

class ParentDefault {
public readonly int $prop = 1;
}

class ChildInherits extends ParentDefault {}

class ChildOverrides extends ParentDefault {
public readonly int $prop = 2;
}

class PrivateParent {
private readonly int $prop = 3;

public function getParentProp(): int {
return $this->prop;
}
}

class PrivateChild extends PrivateParent {
public readonly int $prop = 4;
}

var_dump(new ChildInherits()->prop);
var_dump(new ChildOverrides()->prop);

$privateChild = new PrivateChild();
var_dump($privateChild->getParentProp());
var_dump($privateChild->prop);

?>
--EXPECT--
int(1)
int(2)
int(3)
int(4)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Readonly property with default value satisfies get-only interface property
--FILE--
<?php

interface I {
public int $prop { get; }
}

class C implements I {
public readonly int $prop = 42;
}

var_dump(new C()->prop);
?>
--EXPECTF--
int(42)
Loading
Loading