mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
[RFC] Asymmetric visibility v2 (GH-15063)
Co-authored-by: Larry Garfield <larry@garfieldtech.com>
This commit is contained in:
parent
fef55bc8e4
commit
8df557ac42
70 changed files with 1768 additions and 155 deletions
1
NEWS
1
NEWS
|
@ -16,6 +16,7 @@ PHP NEWS
|
||||||
undefined). (Peter Kokot)
|
undefined). (Peter Kokot)
|
||||||
. Fixed bug GH-15565 (--disable-ipv6 during compilation produces error
|
. Fixed bug GH-15565 (--disable-ipv6 during compilation produces error
|
||||||
EAI_SYSTEM not found). (nielsdos)
|
EAI_SYSTEM not found). (nielsdos)
|
||||||
|
. Implemented asymmetric visibility for properties. (ilutov)
|
||||||
|
|
||||||
- Date:
|
- Date:
|
||||||
. Fixed bug GH-13773 (DatePeriod not taking into account microseconds for end
|
. Fixed bug GH-13773 (DatePeriod not taking into account microseconds for end
|
||||||
|
|
43
Zend/tests/asymmetric_visibility/__set.phpt
Normal file
43
Zend/tests/asymmetric_visibility/__set.phpt
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility __set
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) string $bar;
|
||||||
|
|
||||||
|
public function setBar($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unsetBar() {
|
||||||
|
unset($this->bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set(string $name, mixed $value) {
|
||||||
|
echo __CLASS__, '::', __METHOD__, "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new Foo();
|
||||||
|
try {
|
||||||
|
$foo->bar = 'baz';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo->setBar('baz');
|
||||||
|
try {
|
||||||
|
$foo->bar = 'baz';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo->unsetBar();
|
||||||
|
$foo->bar = 'baz';
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot modify private(set) property Foo::$bar from global scope
|
||||||
|
Cannot modify private(set) property Foo::$bar from global scope
|
||||||
|
Foo::Foo::__set
|
47
Zend/tests/asymmetric_visibility/__unset.phpt
Normal file
47
Zend/tests/asymmetric_visibility/__unset.phpt
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility __unset
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) string $bar;
|
||||||
|
|
||||||
|
public function setBar($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unsetBar() {
|
||||||
|
unset($this->bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __unset($name) {
|
||||||
|
echo __METHOD__, "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test($foo) {
|
||||||
|
try {
|
||||||
|
unset($foo->bar);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new Foo();
|
||||||
|
test($foo);
|
||||||
|
|
||||||
|
$foo->unsetBar();
|
||||||
|
test($foo);
|
||||||
|
|
||||||
|
$foo->setBar('bar');
|
||||||
|
test($foo);
|
||||||
|
|
||||||
|
$foo->unsetBar();
|
||||||
|
test($foo);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot unset private(set) property Foo::$bar from global scope
|
||||||
|
Foo::__unset
|
||||||
|
Cannot unset private(set) property Foo::$bar from global scope
|
||||||
|
Foo::__unset
|
28
Zend/tests/asymmetric_visibility/ast_printing.phpt
Normal file
28
Zend/tests/asymmetric_visibility/ast_printing.phpt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
--TEST--
|
||||||
|
private(set) protected(set) ast printing
|
||||||
|
--INI--
|
||||||
|
zend.assertions=1
|
||||||
|
assert.exception=1
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
try {
|
||||||
|
assert(function () {
|
||||||
|
class Foo {
|
||||||
|
public private(set) string $bar;
|
||||||
|
public protected(set) string $baz;
|
||||||
|
}
|
||||||
|
} && false);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
assert(function () {
|
||||||
|
class Foo {
|
||||||
|
public private(set) string $bar;
|
||||||
|
public protected(set) string $baz;
|
||||||
|
}
|
||||||
|
|
||||||
|
} && false)
|
33
Zend/tests/asymmetric_visibility/bug001.phpt
Normal file
33
Zend/tests/asymmetric_visibility/bug001.phpt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
--TEST--
|
||||||
|
Unset from __unset respects set visibility
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class C {
|
||||||
|
public private(set) int $a = 1;
|
||||||
|
public function __construct() {
|
||||||
|
unset($this->a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class D extends C {
|
||||||
|
public function __unset($name) {
|
||||||
|
unset($this->a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$c = new D();
|
||||||
|
try {
|
||||||
|
unset($c->a);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
var_dump($c);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Cannot unset private(set) property C::$a from scope D
|
||||||
|
object(D)#%d (0) {
|
||||||
|
["a"]=>
|
||||||
|
uninitialized(int)
|
||||||
|
}
|
33
Zend/tests/asymmetric_visibility/bug002.phpt
Normal file
33
Zend/tests/asymmetric_visibility/bug002.phpt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
--TEST--
|
||||||
|
Set from __set respects set visibility
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class C {
|
||||||
|
public private(set) int $a = 1;
|
||||||
|
public function __construct() {
|
||||||
|
unset($this->a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class D extends C {
|
||||||
|
public function __set($name, $value) {
|
||||||
|
$this->a = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$c = new D();
|
||||||
|
try {
|
||||||
|
$c->a = 2;
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
var_dump($c);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Cannot modify private(set) property C::$a from scope D
|
||||||
|
object(D)#%d (0) {
|
||||||
|
["a"]=>
|
||||||
|
uninitialized(int)
|
||||||
|
}
|
28
Zend/tests/asymmetric_visibility/bug003.phpt
Normal file
28
Zend/tests/asymmetric_visibility/bug003.phpt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
--TEST--
|
||||||
|
Explicitly unset property with a-vis still respects set visibility
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class C {
|
||||||
|
public private(set) int $a = 1;
|
||||||
|
public function __construct() {
|
||||||
|
unset($this->a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$c = new C();
|
||||||
|
try {
|
||||||
|
$c->a = 2;
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
unset($c->a);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot modify private(set) property C::$a from global scope
|
||||||
|
Cannot unset private(set) property C::$a from global scope
|
18
Zend/tests/asymmetric_visibility/bug004.phpt
Normal file
18
Zend/tests/asymmetric_visibility/bug004.phpt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
--TEST--
|
||||||
|
Missing property initialization for private(set) constructor promoted property
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class T {
|
||||||
|
public function __construct(
|
||||||
|
private(set) string $prop,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
var_dump(new T('Test'));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
object(T)#%d (1) {
|
||||||
|
["prop"]=>
|
||||||
|
string(4) "Test"
|
||||||
|
}
|
14
Zend/tests/asymmetric_visibility/cpp_no_type.phpt
Normal file
14
Zend/tests/asymmetric_visibility/cpp_no_type.phpt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility in CPP with no type
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public function __construct(
|
||||||
|
public private(set) $bar,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Property with asymmetric visibility Foo::$bar must have type in %s on line %d
|
32
Zend/tests/asymmetric_visibility/cpp_private.phpt
Normal file
32
Zend/tests/asymmetric_visibility/cpp_private.phpt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility private(set) CPP
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public function __construct(
|
||||||
|
public private(set) string $bar,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function setBar($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new Foo('bar');
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foo->bar = 'baz';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo->setBar('baz');
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "bar"
|
||||||
|
Cannot modify private(set) property Foo::$bar from global scope
|
||||||
|
string(3) "baz"
|
42
Zend/tests/asymmetric_visibility/cpp_protected.phpt
Normal file
42
Zend/tests/asymmetric_visibility/cpp_protected.phpt
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility protected(set) CPP
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public function __construct(
|
||||||
|
public protected(set) string $bar,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function setBarPrivate($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FooChild extends Foo {
|
||||||
|
public function setBarProtected($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new FooChild('bar');
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foo->bar = 'baz';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo->setBarPrivate('baz');
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
$foo->setBarProtected('qux');
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "bar"
|
||||||
|
Cannot modify protected(set) property Foo::$bar from global scope
|
||||||
|
string(3) "baz"
|
||||||
|
string(3) "qux"
|
14
Zend/tests/asymmetric_visibility/cpp_wider_set_scope.phpt
Normal file
14
Zend/tests/asymmetric_visibility/cpp_wider_set_scope.phpt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility private(get) protected(set) in CPP is not allowed
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public function __construct(
|
||||||
|
private protected(set) string $bar
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Visibility of property Foo::$bar must not be weaker than set visibility in %s on line %d
|
|
@ -0,0 +1,13 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility private(get) private(set) is allowed
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
private private(set) string $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
===DONE===
|
||||||
|
--EXPECT--
|
||||||
|
===DONE===
|
|
@ -0,0 +1,12 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility private(get) protected(set) not allowed
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
private protected(set) string $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Visibility of property Foo::$bar must not be weaker than set visibility in %s on line %d
|
|
@ -0,0 +1,13 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility protected(get) protected(set) is allowed
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
protected protected(set) string $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
===DONE===
|
||||||
|
--EXPECT--
|
||||||
|
===DONE===
|
34
Zend/tests/asymmetric_visibility/dim_add.phpt
Normal file
34
Zend/tests/asymmetric_visibility/dim_add.phpt
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility DIM add
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) array $bars = [];
|
||||||
|
|
||||||
|
public function addBar($bar) {
|
||||||
|
$this->bars[] = $bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new Foo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foo->bars[] = 'baz';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
var_dump($foo->bars);
|
||||||
|
|
||||||
|
$foo->addBar('baz');
|
||||||
|
var_dump($foo->bars);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot indirectly modify private(set) property Foo::$bars from global scope
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
|
array(1) {
|
||||||
|
[0]=>
|
||||||
|
string(3) "baz"
|
||||||
|
}
|
12
Zend/tests/asymmetric_visibility/duplicate_modifier.phpt
Normal file
12
Zend/tests/asymmetric_visibility/duplicate_modifier.phpt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
--TEST--
|
||||||
|
Duplicate asymmetric visibility modifier
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
private(set) protected(set) string $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Multiple access type modifiers are not allowed in %s on line %d
|
12
Zend/tests/asymmetric_visibility/duplicate_modifier_2.phpt
Normal file
12
Zend/tests/asymmetric_visibility/duplicate_modifier_2.phpt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
--TEST--
|
||||||
|
Duplicate asymmetric visibility modifier
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public(set) protected(set) string $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Multiple access type modifiers are not allowed in %s on line %d
|
24
Zend/tests/asymmetric_visibility/nested_write.phpt
Normal file
24
Zend/tests/asymmetric_visibility/nested_write.phpt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility nested write
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) Bar $bar;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->bar = new Bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar {
|
||||||
|
public int $baz;
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new Foo();
|
||||||
|
$foo->bar->baz = 42;
|
||||||
|
var_dump($foo->bar->baz);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(42)
|
12
Zend/tests/asymmetric_visibility/no_type.phpt
Normal file
12
Zend/tests/asymmetric_visibility/no_type.phpt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility with no type
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Property with asymmetric visibility Foo::$bar must have type in %s on line %d
|
37
Zend/tests/asymmetric_visibility/object_reference.phpt
Normal file
37
Zend/tests/asymmetric_visibility/object_reference.phpt
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
--TEST--
|
||||||
|
Attempting to obtain reference of object of private(set) object returns a copy instead
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) Bar $bar;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->bar = new Bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar {}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
$foo = new Foo();
|
||||||
|
$bar = &$foo->bar;
|
||||||
|
var_dump($foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
test();
|
||||||
|
// Test zend_fetch_property_address with warmed cache
|
||||||
|
test();
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
object(Foo)#1 (1) {
|
||||||
|
["bar"]=>
|
||||||
|
object(Bar)#2 (0) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object(Foo)#2 (1) {
|
||||||
|
["bar"]=>
|
||||||
|
object(Bar)#1 (0) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
--TEST--
|
||||||
|
private(set) property is implicitly final
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public private(set) string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Cannot override final property A::$foo in %s on line %d
|
|
@ -0,0 +1,16 @@
|
||||||
|
--TEST--
|
||||||
|
Overwritten protected asymmetric property with private asymmetric property
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public protected(set) string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public private(set) string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Set access level of B::$foo must be protected(set) (as in class A) or weaker in %s on line %d
|
|
@ -0,0 +1,20 @@
|
||||||
|
--TEST--
|
||||||
|
Overwritten protected asymmetric property with public property
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public protected(set) string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
$b = new B();
|
||||||
|
$b->foo = 'foo';
|
||||||
|
echo $b->foo, "\n";
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
foo
|
|
@ -0,0 +1,16 @@
|
||||||
|
--TEST--
|
||||||
|
Overwritten public property with private asymmetric property
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public private(set) string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Set access level of B::$foo must be omitted (as in class A) in %s on line %d
|
|
@ -0,0 +1,16 @@
|
||||||
|
--TEST--
|
||||||
|
Overwritten public property with protected asymmetric property
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
public protected(set) string $foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Set access level of B::$foo must be omitted (as in class A) in %s on line %d
|
61
Zend/tests/asymmetric_visibility/private.phpt
Normal file
61
Zend/tests/asymmetric_visibility/private.phpt
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility private(set)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) string $bar = 'bar';
|
||||||
|
|
||||||
|
private(set) string $baz = 'baz';
|
||||||
|
|
||||||
|
public function setBar($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBaz($baz) {
|
||||||
|
$this->baz = $baz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FooChild extends Foo {
|
||||||
|
public function modifyBar($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new Foo();
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foo->bar = 'baz';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo->setBar('baz');
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foo->baz = 'baz2';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo->setBaz('baz2');
|
||||||
|
var_dump($foo->baz);
|
||||||
|
|
||||||
|
$child = new FooChild();
|
||||||
|
try {
|
||||||
|
$child->modifyBar('baz');
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "bar"
|
||||||
|
Cannot modify private(set) property Foo::$bar from global scope
|
||||||
|
string(3) "baz"
|
||||||
|
Cannot modify private(set) property Foo::$baz from global scope
|
||||||
|
string(4) "baz2"
|
||||||
|
Cannot modify private(set) property Foo::$bar from scope FooChild
|
66
Zend/tests/asymmetric_visibility/protected.phpt
Normal file
66
Zend/tests/asymmetric_visibility/protected.phpt
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility protected(set)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public protected(set) string $bar = 'bar';
|
||||||
|
|
||||||
|
protected(set) string $baz = 'baz';
|
||||||
|
|
||||||
|
public function setBarPrivate($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBazPrivate($baz) {
|
||||||
|
$this->baz = $baz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FooChild extends Foo {
|
||||||
|
public function setBarProtected($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBazProtected($baz) {
|
||||||
|
$this->baz = $baz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new FooChild();
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foo->bar = 'baz';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo->setBarPrivate('baz');
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
$foo->setBarProtected('qux');
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foo->baz = 'baz';
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo->setbazPrivate('baz2');
|
||||||
|
var_dump($foo->baz);
|
||||||
|
|
||||||
|
$foo->setbazProtected('baz3');
|
||||||
|
var_dump($foo->baz);
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "bar"
|
||||||
|
Cannot modify protected(set) property Foo::$bar from global scope
|
||||||
|
string(3) "baz"
|
||||||
|
string(3) "qux"
|
||||||
|
Cannot modify protected(set) property Foo::$baz from global scope
|
||||||
|
string(4) "baz2"
|
||||||
|
string(4) "baz3"
|
62
Zend/tests/asymmetric_visibility/readonly.phpt
Normal file
62
Zend/tests/asymmetric_visibility/readonly.phpt
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility with readonly
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class P {
|
||||||
|
public readonly int $pDefault;
|
||||||
|
public private(set) readonly int $pPrivate;
|
||||||
|
public protected(set) readonly int $pProtected;
|
||||||
|
public public(set) readonly int $pPublic;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->pDefault = 1;
|
||||||
|
$this->pPrivate = 1;
|
||||||
|
$this->pProtected = 1;
|
||||||
|
$this->pPublic = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class C extends P {
|
||||||
|
public function __construct() {
|
||||||
|
$this->pDefault = 1;
|
||||||
|
try {
|
||||||
|
$this->pPrivate = 1;
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
$this->pProtected = 1;
|
||||||
|
$this->pPublic = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
$p = new ReflectionClass(P::class)->newInstanceWithoutConstructor();
|
||||||
|
try {
|
||||||
|
$p->pDefault = 1;
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$p->pPrivate = 1;
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$p->pProtected = 1;
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
$p->pPublic = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
new P();
|
||||||
|
new C();
|
||||||
|
test();
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot modify private(set) property P::$pPrivate from scope C
|
||||||
|
Cannot modify protected(set) property P::$pDefault from global scope
|
||||||
|
Cannot modify private(set) property P::$pPrivate from global scope
|
||||||
|
Cannot modify protected(set) property P::$pProtected from global scope
|
32
Zend/tests/asymmetric_visibility/reference.phpt
Normal file
32
Zend/tests/asymmetric_visibility/reference.phpt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility reference
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) int $bar = 0;
|
||||||
|
|
||||||
|
public function test() {
|
||||||
|
$bar = &$this->bar;
|
||||||
|
$bar++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new Foo();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$bar = &$foo->bar;
|
||||||
|
$bar++;
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
$foo->test();
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot indirectly modify private(set) property Foo::$bar from global scope
|
||||||
|
int(0)
|
||||||
|
int(1)
|
30
Zend/tests/asymmetric_visibility/reference_2.phpt
Normal file
30
Zend/tests/asymmetric_visibility/reference_2.phpt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility reference in forbidden scope
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class C {
|
||||||
|
public private(set) int $prop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test($c) {
|
||||||
|
$prop = &$c->prop;
|
||||||
|
$prop = 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
test(new C());
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
test(new C());
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||||
|
Cannot indirectly modify private(set) property C::$prop from global scope
|
36
Zend/tests/asymmetric_visibility/scope_rebinding.phpt
Normal file
36
Zend/tests/asymmetric_visibility/scope_rebinding.phpt
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
--TEST--
|
||||||
|
Changing scope with Closure::bindTo() does not confuse asymmetric visibility
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) int $bar = 1;
|
||||||
|
}
|
||||||
|
class Bar {}
|
||||||
|
|
||||||
|
$foo = new Foo();
|
||||||
|
|
||||||
|
$c = function () use ($foo) {
|
||||||
|
$foo->bar++;
|
||||||
|
};
|
||||||
|
|
||||||
|
($c->bindTo(null, Foo::class))();
|
||||||
|
var_dump($foo->bar);
|
||||||
|
try {
|
||||||
|
$c();
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
($c->bindTo(null, Bar::class))();
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
var_dump($foo->bar);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(2)
|
||||||
|
Cannot modify private(set) property Foo::$bar from global scope
|
||||||
|
Cannot modify private(set) property Foo::$bar from scope Bar
|
||||||
|
int(2)
|
67
Zend/tests/asymmetric_visibility/unset.phpt
Normal file
67
Zend/tests/asymmetric_visibility/unset.phpt
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility unset protected(set)
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public protected(set) string $bar;
|
||||||
|
|
||||||
|
public private(set) string $secret;
|
||||||
|
|
||||||
|
public function setBar($bar) {
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSecret($s) {
|
||||||
|
$this->secret = $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unsetBarPrivate() {
|
||||||
|
unset($this->bar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FooChild extends Foo {
|
||||||
|
public function unsetBarProtected() {
|
||||||
|
unset($this->bar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unsetSecret() {
|
||||||
|
unset($this->secret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$foo = new FooChild();
|
||||||
|
|
||||||
|
$foo->setBar('bar');
|
||||||
|
try {
|
||||||
|
unset($foo->bar);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
var_dump($foo->bar ?? 'unset');
|
||||||
|
|
||||||
|
$foo->setBar('bar');
|
||||||
|
$foo->unsetBarPrivate();
|
||||||
|
var_dump($foo->bar ?? 'unset');
|
||||||
|
|
||||||
|
$foo->setBar('bar');
|
||||||
|
$foo->unsetBarProtected();
|
||||||
|
var_dump($foo->bar ?? 'unset');
|
||||||
|
|
||||||
|
$foo->setSecret('beep');
|
||||||
|
try {
|
||||||
|
$foo->unsetSecret();
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
var_dump($foo->secret ?? 'unset');
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Cannot unset protected(set) property Foo::$bar from global scope
|
||||||
|
string(3) "bar"
|
||||||
|
string(5) "unset"
|
||||||
|
string(5) "unset"
|
||||||
|
Cannot unset private(set) property Foo::$secret from scope FooChild
|
||||||
|
string(4) "beep"
|
37
Zend/tests/asymmetric_visibility/unshared_rw_cache_slot.phpt
Normal file
37
Zend/tests/asymmetric_visibility/unshared_rw_cache_slot.phpt
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
--TEST--
|
||||||
|
R/w cache slots should be unshared
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class P {
|
||||||
|
public private(set) string $bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
class C extends P {
|
||||||
|
public function setBar($bar) {
|
||||||
|
var_dump($this->bar);
|
||||||
|
$this->bar = $bar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$c = new C();
|
||||||
|
try {
|
||||||
|
$c->setBar(1);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$c->setBar(1);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage(), "\n";
|
||||||
|
}
|
||||||
|
var_dump($c);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Typed property P::$bar must not be accessed before initialization
|
||||||
|
Typed property P::$bar must not be accessed before initialization
|
||||||
|
object(C)#%d (0) {
|
||||||
|
["bar"]=>
|
||||||
|
uninitialized(string)
|
||||||
|
}
|
134
Zend/tests/asymmetric_visibility/variation.phpt
Normal file
134
Zend/tests/asymmetric_visibility/variation.phpt
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility variations
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
public private(set) int $prop;
|
||||||
|
public private(set) array $array;
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
$this->prop = 1;
|
||||||
|
$this->array = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function r() {
|
||||||
|
echo $this->prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function w() {
|
||||||
|
$this->prop = 1;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rw() {
|
||||||
|
$this->prop += 1;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function im() {
|
||||||
|
$this->array[] = 1;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function is() {
|
||||||
|
echo (int) isset($this->prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function us() {
|
||||||
|
unset($this->prop);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function us_dim() {
|
||||||
|
unset($this->array[0]);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function r($test) {
|
||||||
|
echo $test->prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
function w($test) {
|
||||||
|
$test->prop = 0;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
function rw($test) {
|
||||||
|
$test->prop += 1;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
function im($test) {
|
||||||
|
$test->array[] = 1;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
function is($test) {
|
||||||
|
echo (int) isset($test->prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
function us($test) {
|
||||||
|
unset($test->prop);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
function us_dim($test) {
|
||||||
|
unset($test->array[0]);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ([true, false] as $init) {
|
||||||
|
foreach ([true, false] as $scope) {
|
||||||
|
foreach (['r', 'w', 'rw', 'im', 'is', 'us', 'us_dim'] as $op) {
|
||||||
|
$test = new Test();
|
||||||
|
if ($init) {
|
||||||
|
$test->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo 'Init: ' . ((int) $init) . ', scope: ' . ((int) $scope) . ', op: ' . $op . ": ";
|
||||||
|
try {
|
||||||
|
if ($scope) {
|
||||||
|
$test->{$op}();
|
||||||
|
} else {
|
||||||
|
$op($test);
|
||||||
|
}
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage();
|
||||||
|
}
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Init: 1, scope: 1, op: r: 1
|
||||||
|
Init: 1, scope: 1, op: w: done
|
||||||
|
Init: 1, scope: 1, op: rw: done
|
||||||
|
Init: 1, scope: 1, op: im: done
|
||||||
|
Init: 1, scope: 1, op: is: 1
|
||||||
|
Init: 1, scope: 1, op: us: done
|
||||||
|
Init: 1, scope: 1, op: us_dim: done
|
||||||
|
Init: 1, scope: 0, op: r: 1
|
||||||
|
Init: 1, scope: 0, op: w: Cannot modify private(set) property Test::$prop from global scope
|
||||||
|
Init: 1, scope: 0, op: rw: Cannot modify private(set) property Test::$prop from global scope
|
||||||
|
Init: 1, scope: 0, op: im: Cannot indirectly modify private(set) property Test::$array from global scope
|
||||||
|
Init: 1, scope: 0, op: is: 1
|
||||||
|
Init: 1, scope: 0, op: us: Cannot unset private(set) property Test::$prop from global scope
|
||||||
|
Init: 1, scope: 0, op: us_dim: Cannot indirectly modify private(set) property Test::$array from global scope
|
||||||
|
Init: 0, scope: 1, op: r: Typed property Test::$prop must not be accessed before initialization
|
||||||
|
Init: 0, scope: 1, op: w: done
|
||||||
|
Init: 0, scope: 1, op: rw: Typed property Test::$prop must not be accessed before initialization
|
||||||
|
Init: 0, scope: 1, op: im: done
|
||||||
|
Init: 0, scope: 1, op: is: 0
|
||||||
|
Init: 0, scope: 1, op: us: done
|
||||||
|
Init: 0, scope: 1, op: us_dim: done
|
||||||
|
Init: 0, scope: 0, op: r: Typed property Test::$prop must not be accessed before initialization
|
||||||
|
Init: 0, scope: 0, op: w: Cannot modify private(set) property Test::$prop from global scope
|
||||||
|
Init: 0, scope: 0, op: rw: Typed property Test::$prop must not be accessed before initialization
|
||||||
|
Init: 0, scope: 0, op: im: Cannot indirectly modify private(set) property Test::$array from global scope
|
||||||
|
Init: 0, scope: 0, op: is: 0
|
||||||
|
Init: 0, scope: 0, op: us: Cannot unset private(set) property Test::$prop from global scope
|
||||||
|
Init: 0, scope: 0, op: us_dim: done
|
84
Zend/tests/asymmetric_visibility/variation_nested.phpt
Normal file
84
Zend/tests/asymmetric_visibility/variation_nested.phpt
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
--TEST--
|
||||||
|
Asymmetric visibility nested variations
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Inner {
|
||||||
|
public int $prop = 1;
|
||||||
|
public array $array = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
public private(set) Inner $prop;
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
$this->prop = new Inner();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function r($test) {
|
||||||
|
echo $test->prop->prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
function w($test) {
|
||||||
|
$test->prop->prop = 0;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
function rw($test) {
|
||||||
|
$test->prop->prop += 1;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
function im($test) {
|
||||||
|
$test->prop->array[] = 1;
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
function is($test) {
|
||||||
|
echo (int) isset($test->prop->prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
function us($test) {
|
||||||
|
unset($test->prop->prop);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
function us_dim($test) {
|
||||||
|
unset($test->prop->array[0]);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ([true, false] as $init) {
|
||||||
|
foreach (['r', 'w', 'rw', 'im', 'is', 'us', 'us_dim'] as $op) {
|
||||||
|
$test = new Test();
|
||||||
|
if ($init) {
|
||||||
|
$test->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo 'Init: ' . ((int) $init) . ', op: ' . $op . ": ";
|
||||||
|
try {
|
||||||
|
$op($test);
|
||||||
|
} catch (Error $e) {
|
||||||
|
echo $e->getMessage();
|
||||||
|
}
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Init: 1, op: r: 1
|
||||||
|
Init: 1, op: w: done
|
||||||
|
Init: 1, op: rw: done
|
||||||
|
Init: 1, op: im: done
|
||||||
|
Init: 1, op: is: 1
|
||||||
|
Init: 1, op: us: done
|
||||||
|
Init: 1, op: us_dim: done
|
||||||
|
Init: 0, op: r: Typed property Test::$prop must not be accessed before initialization
|
||||||
|
Init: 0, op: w: Cannot indirectly modify private(set) property Test::$prop from global scope
|
||||||
|
Init: 0, op: rw: Typed property Test::$prop must not be accessed before initialization
|
||||||
|
Init: 0, op: im: Cannot indirectly modify private(set) property Test::$prop from global scope
|
||||||
|
Init: 0, op: is: 0
|
||||||
|
Init: 0, op: us: done
|
||||||
|
Init: 0, op: us_dim: done
|
13
Zend/tests/property_hooks/abstract_get_set_readonly.phpt
Normal file
13
Zend/tests/property_hooks/abstract_get_set_readonly.phpt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
--TEST--
|
||||||
|
readonly property does not satisfy get/set abstract property
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
abstract class P {
|
||||||
|
protected abstract int $prop { get; set; }
|
||||||
|
}
|
||||||
|
class C extends P {
|
||||||
|
public function __construct(protected readonly int $prop) {}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Class C contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (P::$prop::set) in %s on line %d
|
|
@ -1,5 +1,7 @@
|
||||||
--TEST--
|
--TEST--
|
||||||
readonly property does not satisfy get/set interface property
|
readonly property does not satisfy get/set interface property
|
||||||
|
--DESCRIPTION--
|
||||||
|
The error message should be improved, the set access level comes from readonly.
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
interface I {
|
interface I {
|
||||||
|
@ -10,4 +12,4 @@ class C implements I {
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
--EXPECTF--
|
--EXPECTF--
|
||||||
Fatal error: Class C contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (I::$prop::set) in %s on line %d
|
Fatal error: Set access level of C::$prop must be omitted (as in class I) in %s on line %d
|
||||||
|
|
|
@ -22,12 +22,10 @@ try {
|
||||||
} catch (Error $e) {
|
} catch (Error $e) {
|
||||||
echo $e->getMessage(), "\n";
|
echo $e->getMessage(), "\n";
|
||||||
}
|
}
|
||||||
try {
|
$test->initProtected();
|
||||||
$test->initProtected();
|
var_dump($test);
|
||||||
} catch (Error $e) {
|
|
||||||
echo $e->getMessage(), "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$test = new B;
|
||||||
$test->initPrivate();
|
$test->initPrivate();
|
||||||
var_dump($test->prop);
|
var_dump($test->prop);
|
||||||
|
|
||||||
|
@ -56,17 +54,20 @@ class Y extends X {
|
||||||
}
|
}
|
||||||
|
|
||||||
$test = new Y;
|
$test = new Y;
|
||||||
try {
|
$test->initFromParent();
|
||||||
$test->initFromParent();
|
var_dump($test);
|
||||||
} catch (Error $e) {
|
|
||||||
echo $e->getMessage(), "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECT--
|
--EXPECTF--
|
||||||
Cannot initialize readonly property A::$prop from global scope
|
Cannot modify protected(set) property A::$prop from global scope
|
||||||
Cannot initialize readonly property A::$prop from scope B
|
object(B)#%d (1) {
|
||||||
|
["prop"]=>
|
||||||
|
int(2)
|
||||||
|
}
|
||||||
int(3)
|
int(3)
|
||||||
int(1)
|
int(1)
|
||||||
int(3)
|
int(3)
|
||||||
Cannot initialize readonly property Y::$prop from scope X
|
object(Y)#%d (1) {
|
||||||
|
["prop"]=>
|
||||||
|
int(1)
|
||||||
|
}
|
||||||
|
|
|
@ -64,8 +64,8 @@ try {
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
bool(false)
|
bool(false)
|
||||||
Typed property Test::$prop must not be accessed before initialization
|
Typed property Test::$prop must not be accessed before initialization
|
||||||
Cannot initialize readonly property Test::$prop from global scope
|
Cannot modify protected(set) property Test::$prop from global scope
|
||||||
Cannot unset readonly property Test::$prop from global scope
|
Cannot unset protected(set) property Test::$prop from global scope
|
||||||
Test::__isset(prop)
|
Test::__isset(prop)
|
||||||
bool(true)
|
bool(true)
|
||||||
Test::__get(prop)
|
Test::__get(prop)
|
||||||
|
|
13
Zend/tests/readonly_props/public_set_non_readonly.phpt
Normal file
13
Zend/tests/readonly_props/public_set_non_readonly.phpt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
--TEST--
|
||||||
|
public(set) is allowed on non-readonly
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class A {
|
||||||
|
public public(set) int $prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
===DONE===
|
||||||
|
--EXPECT--
|
||||||
|
===DONE===
|
|
@ -61,4 +61,4 @@ Test2::__get
|
||||||
int(1)
|
int(1)
|
||||||
int(1)
|
int(1)
|
||||||
Cannot unset readonly property Test2::$prop
|
Cannot unset readonly property Test2::$prop
|
||||||
Cannot unset readonly property Test3::$prop from global scope
|
Cannot unset protected(set) property Test3::$prop from global scope
|
||||||
|
|
|
@ -5,9 +5,11 @@ Readonly variations
|
||||||
|
|
||||||
class Test {
|
class Test {
|
||||||
public readonly int $prop;
|
public readonly int $prop;
|
||||||
|
public readonly array $array;
|
||||||
|
|
||||||
public function init() {
|
public function init() {
|
||||||
$this->prop = 1;
|
$this->prop = 1;
|
||||||
|
$this->array = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function r() {
|
public function r() {
|
||||||
|
@ -37,6 +39,11 @@ class Test {
|
||||||
unset($this->prop);
|
unset($this->prop);
|
||||||
echo 'done';
|
echo 'done';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function us_dim() {
|
||||||
|
unset($this->array[0]);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function r($test) {
|
function r($test) {
|
||||||
|
@ -67,9 +74,14 @@ function us($test) {
|
||||||
echo 'done';
|
echo 'done';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function us_dim($test) {
|
||||||
|
unset($test->array[0]);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
foreach ([true, false] as $init) {
|
foreach ([true, false] as $init) {
|
||||||
foreach ([true, false] as $scope) {
|
foreach ([true, false] as $scope) {
|
||||||
foreach (['r', 'w', 'rw', 'im', 'is', 'us'] as $op) {
|
foreach (['r', 'w', 'rw', 'im', 'is', 'us', 'us_dim'] as $op) {
|
||||||
$test = new Test();
|
$test = new Test();
|
||||||
if ($init) {
|
if ($init) {
|
||||||
$test->init();
|
$test->init();
|
||||||
|
@ -98,21 +110,25 @@ Init: 1, scope: 1, op: rw: Cannot modify readonly property Test::$prop
|
||||||
Init: 1, scope: 1, op: im: Cannot indirectly modify readonly property Test::$prop
|
Init: 1, scope: 1, op: im: Cannot indirectly modify readonly property Test::$prop
|
||||||
Init: 1, scope: 1, op: is: 1
|
Init: 1, scope: 1, op: is: 1
|
||||||
Init: 1, scope: 1, op: us: Cannot unset readonly property Test::$prop
|
Init: 1, scope: 1, op: us: Cannot unset readonly property Test::$prop
|
||||||
|
Init: 1, scope: 1, op: us_dim: Cannot indirectly modify readonly property Test::$array
|
||||||
Init: 1, scope: 0, op: r: 1
|
Init: 1, scope: 0, op: r: 1
|
||||||
Init: 1, scope: 0, op: w: Cannot modify readonly property Test::$prop
|
Init: 1, scope: 0, op: w: Cannot modify readonly property Test::$prop
|
||||||
Init: 1, scope: 0, op: rw: Cannot modify readonly property Test::$prop
|
Init: 1, scope: 0, op: rw: Cannot modify readonly property Test::$prop
|
||||||
Init: 1, scope: 0, op: im: Cannot indirectly modify readonly property Test::$prop
|
Init: 1, scope: 0, op: im: Cannot indirectly modify readonly property Test::$prop
|
||||||
Init: 1, scope: 0, op: is: 1
|
Init: 1, scope: 0, op: is: 1
|
||||||
Init: 1, scope: 0, op: us: Cannot unset readonly property Test::$prop
|
Init: 1, scope: 0, op: us: Cannot unset readonly property Test::$prop
|
||||||
|
Init: 1, scope: 0, op: us_dim: Cannot indirectly modify readonly property Test::$array
|
||||||
Init: 0, scope: 1, op: r: Typed property Test::$prop must not be accessed before initialization
|
Init: 0, scope: 1, op: r: Typed property Test::$prop must not be accessed before initialization
|
||||||
Init: 0, scope: 1, op: w: done
|
Init: 0, scope: 1, op: w: done
|
||||||
Init: 0, scope: 1, op: rw: Typed property Test::$prop must not be accessed before initialization
|
Init: 0, scope: 1, op: rw: Typed property Test::$prop must not be accessed before initialization
|
||||||
Init: 0, scope: 1, op: im: Cannot indirectly modify readonly property Test::$prop
|
Init: 0, scope: 1, op: im: Cannot indirectly modify readonly property Test::$prop
|
||||||
Init: 0, scope: 1, op: is: 0
|
Init: 0, scope: 1, op: is: 0
|
||||||
Init: 0, scope: 1, op: us: done
|
Init: 0, scope: 1, op: us: done
|
||||||
|
Init: 0, scope: 1, op: us_dim: done
|
||||||
Init: 0, scope: 0, op: r: Typed property Test::$prop must not be accessed before initialization
|
Init: 0, scope: 0, op: r: Typed property Test::$prop must not be accessed before initialization
|
||||||
Init: 0, scope: 0, op: w: Cannot initialize readonly property Test::$prop from global scope
|
Init: 0, scope: 0, op: w: Cannot modify protected(set) property Test::$prop from global scope
|
||||||
Init: 0, scope: 0, op: rw: Typed property Test::$prop must not be accessed before initialization
|
Init: 0, scope: 0, op: rw: Typed property Test::$prop must not be accessed before initialization
|
||||||
Init: 0, scope: 0, op: im: Cannot indirectly modify readonly property Test::$prop
|
Init: 0, scope: 0, op: im: Cannot indirectly modify readonly property Test::$prop
|
||||||
Init: 0, scope: 0, op: is: 0
|
Init: 0, scope: 0, op: is: 0
|
||||||
Init: 0, scope: 0, op: us: Cannot unset readonly property Test::$prop from global scope
|
Init: 0, scope: 0, op: us: Cannot unset protected(set) property Test::$prop from global scope
|
||||||
|
Init: 0, scope: 0, op: us_dim: done
|
||||||
|
|
|
@ -44,8 +44,13 @@ function us($test) {
|
||||||
echo 'done';
|
echo 'done';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function us_dim($test) {
|
||||||
|
unset($test->prop->array[0]);
|
||||||
|
echo 'done';
|
||||||
|
}
|
||||||
|
|
||||||
foreach ([true, false] as $init) {
|
foreach ([true, false] as $init) {
|
||||||
foreach (['r', 'w', 'rw', 'im', 'is', 'us'] as $op) {
|
foreach (['r', 'w', 'rw', 'im', 'is', 'us', 'us_dim'] as $op) {
|
||||||
$test = new Test();
|
$test = new Test();
|
||||||
if ($init) {
|
if ($init) {
|
||||||
$test->init();
|
$test->init();
|
||||||
|
@ -69,9 +74,11 @@ Init: 1, op: rw: done
|
||||||
Init: 1, op: im: done
|
Init: 1, op: im: done
|
||||||
Init: 1, op: is: 1
|
Init: 1, op: is: 1
|
||||||
Init: 1, op: us: done
|
Init: 1, op: us: done
|
||||||
|
Init: 1, op: us_dim: done
|
||||||
Init: 0, op: r: Typed property Test::$prop must not be accessed before initialization
|
Init: 0, op: r: Typed property Test::$prop must not be accessed before initialization
|
||||||
Init: 0, op: w: Cannot indirectly modify readonly property Test::$prop
|
Init: 0, op: w: Cannot indirectly modify readonly property Test::$prop
|
||||||
Init: 0, op: rw: Typed property Test::$prop must not be accessed before initialization
|
Init: 0, op: rw: Typed property Test::$prop must not be accessed before initialization
|
||||||
Init: 0, op: im: Cannot indirectly modify readonly property Test::$prop
|
Init: 0, op: im: Cannot indirectly modify readonly property Test::$prop
|
||||||
Init: 0, op: is: 0
|
Init: 0, op: is: 0
|
||||||
Init: 0, op: us: done
|
Init: 0, op: us: done
|
||||||
|
Init: 0, op: us_dim: done
|
||||||
|
|
|
@ -4523,6 +4523,33 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z
|
||||||
if (!(access_type & ZEND_ACC_PPP_MASK)) {
|
if (!(access_type & ZEND_ACC_PPP_MASK)) {
|
||||||
access_type |= ZEND_ACC_PUBLIC;
|
access_type |= ZEND_ACC_PUBLIC;
|
||||||
}
|
}
|
||||||
|
/* Add the protected(set) bit for public readonly properties with no set visibility. */
|
||||||
|
if ((access_type & (ZEND_ACC_PUBLIC|ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK)) == (ZEND_ACC_PUBLIC|ZEND_ACC_READONLY)) {
|
||||||
|
access_type |= ZEND_ACC_PROTECTED_SET;
|
||||||
|
} else if (UNEXPECTED(access_type & ZEND_ACC_PPP_SET_MASK)) {
|
||||||
|
if (!ZEND_TYPE_IS_SET(type)) {
|
||||||
|
zend_error_noreturn(ce->type == ZEND_INTERNAL_CLASS ? E_CORE_ERROR : E_COMPILE_ERROR,
|
||||||
|
"Property with asymmetric visibility %s::$%s must have type",
|
||||||
|
ZSTR_VAL(ce->name), ZSTR_VAL(name));
|
||||||
|
}
|
||||||
|
uint32_t get_visibility = zend_visibility_to_set_visibility(access_type & ZEND_ACC_PPP_MASK);
|
||||||
|
uint32_t set_visibility = access_type & ZEND_ACC_PPP_SET_MASK;
|
||||||
|
if (get_visibility > set_visibility) {
|
||||||
|
zend_error_noreturn(ce->type == ZEND_INTERNAL_CLASS ? E_CORE_ERROR : E_COMPILE_ERROR,
|
||||||
|
"Visibility of property %s::$%s must not be weaker than set visibility",
|
||||||
|
ZSTR_VAL(ce->name), ZSTR_VAL(name));
|
||||||
|
}
|
||||||
|
/* Remove equivalent set visibility. */
|
||||||
|
if (((access_type & (ZEND_ACC_PUBLIC|ZEND_ACC_PUBLIC_SET)) == (ZEND_ACC_PUBLIC|ZEND_ACC_PUBLIC_SET))
|
||||||
|
|| ((access_type & (ZEND_ACC_PROTECTED|ZEND_ACC_PROTECTED_SET)) == (ZEND_ACC_PROTECTED|ZEND_ACC_PROTECTED_SET))
|
||||||
|
|| ((access_type & (ZEND_ACC_PRIVATE|ZEND_ACC_PRIVATE_SET)) == (ZEND_ACC_PRIVATE|ZEND_ACC_PRIVATE_SET))) {
|
||||||
|
access_type &= ~ZEND_ACC_PPP_SET_MASK;
|
||||||
|
}
|
||||||
|
/* private(set) properties are implicitly final. */
|
||||||
|
if (access_type & ZEND_ACC_PRIVATE_SET) {
|
||||||
|
access_type |= ZEND_ACC_FINAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Virtual properties have no backing storage, the offset should never be used. However, the
|
/* Virtual properties have no backing storage, the offset should never be used. However, the
|
||||||
* virtual flag cannot be definitively determined at compile time. Allow using default values
|
* virtual flag cannot be definitively determined at compile time. Allow using default values
|
||||||
|
|
|
@ -1697,7 +1697,7 @@ static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ZEND_COLD void zend_ast_export_visibility(smart_str *str, uint32_t flags) {
|
static ZEND_COLD void zend_ast_export_visibility(smart_str *str, uint32_t flags, zend_modifier_target target) {
|
||||||
if (flags & ZEND_ACC_PUBLIC) {
|
if (flags & ZEND_ACC_PUBLIC) {
|
||||||
smart_str_appends(str, "public ");
|
smart_str_appends(str, "public ");
|
||||||
} else if (flags & ZEND_ACC_PROTECTED) {
|
} else if (flags & ZEND_ACC_PROTECTED) {
|
||||||
|
@ -1705,6 +1705,16 @@ static ZEND_COLD void zend_ast_export_visibility(smart_str *str, uint32_t flags)
|
||||||
} else if (flags & ZEND_ACC_PRIVATE) {
|
} else if (flags & ZEND_ACC_PRIVATE) {
|
||||||
smart_str_appends(str, "private ");
|
smart_str_appends(str, "private ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_CPP) {
|
||||||
|
if (flags & ZEND_ACC_PRIVATE_SET) {
|
||||||
|
smart_str_appends(str, "private(set) ");
|
||||||
|
} else if (flags & ZEND_ACC_PROTECTED_SET) {
|
||||||
|
smart_str_appends(str, "protected(set) ");
|
||||||
|
} else if (flags & ZEND_ACC_PUBLIC_SET) {
|
||||||
|
smart_str_appends(str, "public(set) ");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int indent) {
|
static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int indent) {
|
||||||
|
@ -1815,7 +1825,7 @@ tail_call:
|
||||||
zend_ast_export_attributes(str, decl->child[4], indent, newlines);
|
zend_ast_export_attributes(str, decl->child[4], indent, newlines);
|
||||||
}
|
}
|
||||||
|
|
||||||
zend_ast_export_visibility(str, decl->flags);
|
zend_ast_export_visibility(str, decl->flags, ZEND_MODIFIER_TARGET_METHOD);
|
||||||
|
|
||||||
if (decl->flags & ZEND_ACC_STATIC) {
|
if (decl->flags & ZEND_ACC_STATIC) {
|
||||||
smart_str_appends(str, "static ");
|
smart_str_appends(str, "static ");
|
||||||
|
@ -1941,7 +1951,7 @@ simple_list:
|
||||||
zend_ast_export_attributes(str, ast->child[2], indent, 1);
|
zend_ast_export_attributes(str, ast->child[2], indent, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
zend_ast_export_visibility(str, ast->attr);
|
zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_PROPERTY);
|
||||||
|
|
||||||
if (ast->attr & ZEND_ACC_STATIC) {
|
if (ast->attr & ZEND_ACC_STATIC) {
|
||||||
smart_str_appends(str, "static ");
|
smart_str_appends(str, "static ");
|
||||||
|
@ -1967,7 +1977,7 @@ simple_list:
|
||||||
zend_ast_export_attributes(str, ast->child[1], indent, 1);
|
zend_ast_export_attributes(str, ast->child[1], indent, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
zend_ast_export_visibility(str, ast->attr);
|
zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_CONSTANT);
|
||||||
smart_str_appends(str, "const ");
|
smart_str_appends(str, "const ");
|
||||||
if (ast->child[2]) {
|
if (ast->child[2]) {
|
||||||
zend_ast_export_type(str, ast->child[2], indent);
|
zend_ast_export_type(str, ast->child[2], indent);
|
||||||
|
@ -2388,7 +2398,7 @@ simple_list:
|
||||||
|
|
||||||
for (uint32_t i = 0; i < hook_list->children; i++) {
|
for (uint32_t i = 0; i < hook_list->children; i++) {
|
||||||
zend_ast_decl *hook = (zend_ast_decl *)hook_list->child[i];
|
zend_ast_decl *hook = (zend_ast_decl *)hook_list->child[i];
|
||||||
zend_ast_export_visibility(str, hook->flags);
|
zend_ast_export_visibility(str, hook->flags, ZEND_MODIFIER_TARGET_PROPERTY);
|
||||||
if (hook->flags & ZEND_ACC_FINAL) {
|
if (hook->flags & ZEND_ACC_FINAL) {
|
||||||
smart_str_appends(str, "final ");
|
smart_str_appends(str, "final ");
|
||||||
}
|
}
|
||||||
|
|
|
@ -860,6 +860,12 @@ static char *zend_modifier_token_to_string(uint32_t token)
|
||||||
return "readonly";
|
return "readonly";
|
||||||
case T_ABSTRACT:
|
case T_ABSTRACT:
|
||||||
return "abstract";
|
return "abstract";
|
||||||
|
case T_PUBLIC_SET:
|
||||||
|
return "public(set)";
|
||||||
|
case T_PROTECTED_SET:
|
||||||
|
return "protected(set)";
|
||||||
|
case T_PRIVATE_SET:
|
||||||
|
return "private(set)";
|
||||||
EMPTY_SWITCH_DEFAULT_CASE()
|
EMPTY_SWITCH_DEFAULT_CASE()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -905,6 +911,21 @@ uint32_t zend_modifier_token_to_flag(zend_modifier_target target, uint32_t token
|
||||||
return ZEND_ACC_STATIC;
|
return ZEND_ACC_STATIC;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case T_PUBLIC_SET:
|
||||||
|
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_CPP) {
|
||||||
|
return ZEND_ACC_PUBLIC_SET;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case T_PROTECTED_SET:
|
||||||
|
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_CPP) {
|
||||||
|
return ZEND_ACC_PROTECTED_SET;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case T_PRIVATE_SET:
|
||||||
|
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_CPP) {
|
||||||
|
return ZEND_ACC_PRIVATE_SET;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *member;
|
char *member;
|
||||||
|
@ -1020,6 +1041,13 @@ uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag, zend_modifi
|
||||||
"Cannot use the final modifier on an abstract method", 0);
|
"Cannot use the final modifier on an abstract method", 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_CPP) {
|
||||||
|
if ((flags & ZEND_ACC_PPP_SET_MASK) && (new_flag & ZEND_ACC_PPP_SET_MASK)) {
|
||||||
|
zend_throw_exception(zend_ce_compile_error,
|
||||||
|
"Multiple access type modifiers are not allowed", 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return new_flags;
|
return new_flags;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
@ -7569,7 +7597,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
|
||||||
zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast));
|
zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast));
|
||||||
bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
|
bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
|
||||||
bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0;
|
bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0;
|
||||||
uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_READONLY);
|
uint32_t property_flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_READONLY);
|
||||||
bool is_promoted = property_flags || hooks_ast;
|
bool is_promoted = property_flags || hooks_ast;
|
||||||
|
|
||||||
znode var_node, default_node;
|
znode var_node, default_node;
|
||||||
|
@ -7793,7 +7821,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
|
||||||
zend_ast *param_ast = list->child[i];
|
zend_ast *param_ast = list->child[i];
|
||||||
zend_ast *hooks_ast = param_ast->child[5];
|
zend_ast *hooks_ast = param_ast->child[5];
|
||||||
bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
|
bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0;
|
||||||
uint32_t flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_READONLY);
|
uint32_t flags = param_ast->attr & (ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_READONLY);
|
||||||
bool is_promoted = flags || hooks_ast;
|
bool is_promoted = flags || hooks_ast;
|
||||||
if (!is_promoted) {
|
if (!is_promoted) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -253,7 +253,7 @@ typedef struct _zend_oparray_context {
|
||||||
/* or IS_CONSTANT_VISITED_MARK | | | */
|
/* or IS_CONSTANT_VISITED_MARK | | | */
|
||||||
#define ZEND_CLASS_CONST_IS_CASE (1 << 6) /* | | | X */
|
#define ZEND_CLASS_CONST_IS_CASE (1 << 6) /* | | | X */
|
||||||
/* | | | */
|
/* | | | */
|
||||||
/* Property Flags (unused: 10...) | | | */
|
/* Property Flags (unused: 13...) | | | */
|
||||||
/* =========== | | | */
|
/* =========== | | | */
|
||||||
/* | | | */
|
/* | | | */
|
||||||
/* Promoted property / parameter | | | */
|
/* Promoted property / parameter | | | */
|
||||||
|
@ -262,6 +262,11 @@ typedef struct _zend_oparray_context {
|
||||||
/* Virtual property without backing storage | | | */
|
/* Virtual property without backing storage | | | */
|
||||||
#define ZEND_ACC_VIRTUAL (1 << 9) /* | | X | */
|
#define ZEND_ACC_VIRTUAL (1 << 9) /* | | X | */
|
||||||
/* | | | */
|
/* | | | */
|
||||||
|
/* Asymmetric visibility | | | */
|
||||||
|
#define ZEND_ACC_PUBLIC_SET (1 << 10) /* | | X | */
|
||||||
|
#define ZEND_ACC_PROTECTED_SET (1 << 11) /* | | X | */
|
||||||
|
#define ZEND_ACC_PRIVATE_SET (1 << 12) /* | | X | */
|
||||||
|
/* | | | */
|
||||||
/* Class Flags (unused: 30,31) | | | */
|
/* Class Flags (unused: 30,31) | | | */
|
||||||
/* =========== | | | */
|
/* =========== | | | */
|
||||||
/* | | | */
|
/* | | | */
|
||||||
|
@ -395,6 +400,20 @@ typedef struct _zend_oparray_context {
|
||||||
|
|
||||||
|
|
||||||
#define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE)
|
#define ZEND_ACC_PPP_MASK (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE)
|
||||||
|
#define ZEND_ACC_PPP_SET_MASK (ZEND_ACC_PUBLIC_SET | ZEND_ACC_PROTECTED_SET | ZEND_ACC_PRIVATE_SET)
|
||||||
|
|
||||||
|
static zend_always_inline uint32_t zend_visibility_to_set_visibility(uint32_t visibility)
|
||||||
|
{
|
||||||
|
switch (visibility) {
|
||||||
|
case ZEND_ACC_PUBLIC:
|
||||||
|
return ZEND_ACC_PUBLIC_SET;
|
||||||
|
case ZEND_ACC_PROTECTED:
|
||||||
|
return ZEND_ACC_PROTECTED_SET;
|
||||||
|
case ZEND_ACC_PRIVATE:
|
||||||
|
return ZEND_ACC_PRIVATE_SET;
|
||||||
|
EMPTY_SWITCH_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* call through internal function handler. e.g. Closure::invoke() */
|
/* call through internal function handler. e.g. Closure::invoke() */
|
||||||
#define ZEND_ACC_CALL_VIA_HANDLER ZEND_ACC_CALL_VIA_TRAMPOLINE
|
#define ZEND_ACC_CALL_VIA_HANDLER ZEND_ACC_CALL_VIA_TRAMPOLINE
|
||||||
|
@ -1001,7 +1020,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
|
||||||
#define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400
|
#define ZEND_FETCH_CLASS_ALLOW_UNLINKED 0x0400
|
||||||
#define ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED 0x0800
|
#define ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED 0x0800
|
||||||
|
|
||||||
/* These should not clash with ZEND_ACC_(PUBLIC|PROTECTED|PRIVATE) */
|
/* These should not clash with ZEND_ACC_PPP_MASK and ZEND_ACC_PPP_SET_MASK */
|
||||||
#define ZEND_PARAM_REF (1<<3)
|
#define ZEND_PARAM_REF (1<<3)
|
||||||
#define ZEND_PARAM_VARIADIC (1<<4)
|
#define ZEND_PARAM_VARIADIC (1<<4)
|
||||||
|
|
||||||
|
|
|
@ -915,6 +915,32 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_object_released_while_assigning_to_pr
|
||||||
ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name));
|
ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_asymmetric_visibility_property_modification_error(
|
||||||
|
const zend_property_info *prop_info, const char *operation
|
||||||
|
) {
|
||||||
|
zend_class_entry *scope;
|
||||||
|
if (EG(fake_scope)) {
|
||||||
|
scope = EG(fake_scope);
|
||||||
|
} else {
|
||||||
|
scope = zend_get_called_scope(EG(current_execute_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *visibility;
|
||||||
|
if (prop_info->flags & ZEND_ACC_PRIVATE_SET) {
|
||||||
|
visibility = "private(set)";
|
||||||
|
} else {
|
||||||
|
ZEND_ASSERT(prop_info->flags & ZEND_ACC_PROTECTED_SET);
|
||||||
|
visibility = "protected(set)";
|
||||||
|
}
|
||||||
|
|
||||||
|
zend_throw_error(NULL, "Cannot %s %s property %s::$%s from %s%s",
|
||||||
|
operation,
|
||||||
|
visibility,
|
||||||
|
ZSTR_VAL(prop_info->ce->name),
|
||||||
|
ZSTR_VAL(prop_info->name),
|
||||||
|
scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : "");
|
||||||
|
}
|
||||||
|
|
||||||
static const zend_class_entry *resolve_single_class_type(zend_string *name, const zend_class_entry *self_ce) {
|
static const zend_class_entry *resolve_single_class_type(zend_string *name, const zend_class_entry *self_ce) {
|
||||||
if (zend_string_equals_literal_ci(name, "self")) {
|
if (zend_string_equals_literal_ci(name, "self")) {
|
||||||
return self_ce;
|
return self_ce;
|
||||||
|
@ -1028,9 +1054,15 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
|
||||||
{
|
{
|
||||||
zval tmp;
|
zval tmp;
|
||||||
|
|
||||||
if (UNEXPECTED((info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE))) {
|
if (UNEXPECTED(info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))) {
|
||||||
zend_readonly_property_modification_error(info);
|
if ((info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE)) {
|
||||||
return &EG(uninitialized_zval);
|
zend_readonly_property_modification_error(info);
|
||||||
|
return &EG(uninitialized_zval);
|
||||||
|
}
|
||||||
|
if (info->flags & ZEND_ACC_PPP_SET_MASK && !zend_asymmetric_property_has_set_access(info)) {
|
||||||
|
zend_asymmetric_visibility_property_modification_error(info, "modify");
|
||||||
|
return &EG(uninitialized_zval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ZVAL_DEREF(value);
|
ZVAL_DEREF(value);
|
||||||
|
@ -3357,7 +3389,8 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
|
||||||
ZVAL_INDIRECT(result, ptr);
|
ZVAL_INDIRECT(result, ptr);
|
||||||
zend_property_info *prop_info = CACHED_PTR_EX(cache_slot + 2);
|
zend_property_info *prop_info = CACHED_PTR_EX(cache_slot + 2);
|
||||||
if (prop_info) {
|
if (prop_info) {
|
||||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
|
if (UNEXPECTED(prop_info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))
|
||||||
|
&& ((prop_info->flags & ZEND_ACC_READONLY) || !zend_asymmetric_property_has_set_access(prop_info))) {
|
||||||
/* For objects, W/RW/UNSET fetch modes might not actually modify object.
|
/* For objects, W/RW/UNSET fetch modes might not actually modify object.
|
||||||
* Similar as with magic __get() allow them, but return the value as a copy
|
* Similar as with magic __get() allow them, but return the value as a copy
|
||||||
* to make sure no actual modification is possible. */
|
* to make sure no actual modification is possible. */
|
||||||
|
@ -3365,7 +3398,11 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
|
||||||
if (Z_TYPE_P(ptr) == IS_OBJECT) {
|
if (Z_TYPE_P(ptr) == IS_OBJECT) {
|
||||||
ZVAL_COPY(result, ptr);
|
ZVAL_COPY(result, ptr);
|
||||||
} else {
|
} else {
|
||||||
zend_readonly_property_indirect_modification_error(prop_info);
|
if (prop_info->flags & ZEND_ACC_READONLY) {
|
||||||
|
zend_readonly_property_indirect_modification_error(prop_info);
|
||||||
|
} else {
|
||||||
|
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||||
|
}
|
||||||
ZVAL_ERROR(result);
|
ZVAL_ERROR(result);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -92,6 +92,9 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_object_released_while_assigning_to_pr
|
||||||
|
|
||||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_add_element(void);
|
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_add_element(void);
|
||||||
|
|
||||||
|
ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info);
|
||||||
|
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_asymmetric_visibility_property_modification_error(const zend_property_info *info, const char *operation);
|
||||||
|
|
||||||
ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg);
|
ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg);
|
||||||
ZEND_API ZEND_COLD void zend_verify_arg_error(
|
ZEND_API ZEND_COLD void zend_verify_arg_error(
|
||||||
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value);
|
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value);
|
||||||
|
|
|
@ -212,6 +212,18 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
static const char *zend_asymmetric_visibility_string(uint32_t fn_flags) /* {{{ */
|
||||||
|
{
|
||||||
|
if (fn_flags & ZEND_ACC_PRIVATE_SET) {
|
||||||
|
return "private(set)";
|
||||||
|
} else if (fn_flags & ZEND_ACC_PROTECTED_SET) {
|
||||||
|
return "protected(set)";
|
||||||
|
} else {
|
||||||
|
ZEND_ASSERT(!(fn_flags & ZEND_ACC_PUBLIC_SET));
|
||||||
|
return "omitted";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *name) {
|
static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *name) {
|
||||||
ZEND_ASSERT(scope);
|
ZEND_ASSERT(scope);
|
||||||
if (zend_string_equals_literal_ci(name, "parent") && scope->parent) {
|
if (zend_string_equals_literal_ci(name, "parent") && scope->parent) {
|
||||||
|
@ -1442,6 +1454,25 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
|
||||||
ZSTR_VAL(ce->name), ZSTR_VAL(key));
|
ZSTR_VAL(ce->name), ZSTR_VAL(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (UNEXPECTED((child_info->flags & ZEND_ACC_PPP_SET_MASK))
|
||||||
|
/* Get-only virtual properties have no set visibility, so any child visibility is fine. */
|
||||||
|
&& !(parent_info->hooks && (parent_info->flags & ZEND_ACC_VIRTUAL) && !parent_info->hooks[ZEND_PROPERTY_HOOK_SET])) {
|
||||||
|
uint32_t parent_set_visibility = parent_info->flags & ZEND_ACC_PPP_SET_MASK;
|
||||||
|
/* Adding set protection is fine if it's the same or weaker than
|
||||||
|
* the parents full property visibility. */
|
||||||
|
if (!parent_set_visibility) {
|
||||||
|
parent_set_visibility = zend_visibility_to_set_visibility(parent_info->flags & ZEND_ACC_PPP_MASK);
|
||||||
|
}
|
||||||
|
uint32_t child_set_visibility = child_info->flags & ZEND_ACC_PPP_SET_MASK;
|
||||||
|
if (child_set_visibility > parent_set_visibility) {
|
||||||
|
zend_error_noreturn(
|
||||||
|
E_COMPILE_ERROR,
|
||||||
|
"Set access level of %s::$%s must be %s (as in class %s)%s",
|
||||||
|
ZSTR_VAL(ce->name), ZSTR_VAL(key),
|
||||||
|
zend_asymmetric_visibility_string(parent_info->flags), ZSTR_VAL(parent_info->ce->name),
|
||||||
|
!(parent_info->flags & ZEND_ACC_PPP_SET_MASK) ? "" : " or weaker");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (UNEXPECTED((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK))) {
|
if (UNEXPECTED((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK))) {
|
||||||
zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ZSTR_VAL(ce->name), ZSTR_VAL(key), zend_visibility_string(parent_info->flags), ZSTR_VAL(parent_info->ce->name), (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
|
zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ZSTR_VAL(ce->name), ZSTR_VAL(key), zend_visibility_string(parent_info->flags), ZSTR_VAL(parent_info->ce->name), (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
|
||||||
|
|
|
@ -154,6 +154,9 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
|
||||||
%token <ident> T_PRIVATE "'private'"
|
%token <ident> T_PRIVATE "'private'"
|
||||||
%token <ident> T_PROTECTED "'protected'"
|
%token <ident> T_PROTECTED "'protected'"
|
||||||
%token <ident> T_PUBLIC "'public'"
|
%token <ident> T_PUBLIC "'public'"
|
||||||
|
%token <ident> T_PRIVATE_SET "'private(set)'"
|
||||||
|
%token <ident> T_PROTECTED_SET "'protected(set)'"
|
||||||
|
%token <ident> T_PUBLIC_SET "'public(set)'"
|
||||||
%token <ident> T_READONLY "'readonly'"
|
%token <ident> T_READONLY "'readonly'"
|
||||||
%token <ident> T_VAR "'var'"
|
%token <ident> T_VAR "'var'"
|
||||||
%token <ident> T_UNSET "'unset'"
|
%token <ident> T_UNSET "'unset'"
|
||||||
|
@ -1066,6 +1069,9 @@ member_modifier:
|
||||||
T_PUBLIC { $$ = T_PUBLIC; }
|
T_PUBLIC { $$ = T_PUBLIC; }
|
||||||
| T_PROTECTED { $$ = T_PROTECTED; }
|
| T_PROTECTED { $$ = T_PROTECTED; }
|
||||||
| T_PRIVATE { $$ = T_PRIVATE; }
|
| T_PRIVATE { $$ = T_PRIVATE; }
|
||||||
|
| T_PUBLIC_SET { $$ = T_PUBLIC_SET; }
|
||||||
|
| T_PROTECTED_SET { $$ = T_PROTECTED_SET; }
|
||||||
|
| T_PRIVATE_SET { $$ = T_PRIVATE_SET; }
|
||||||
| T_STATIC { $$ = T_STATIC; }
|
| T_STATIC { $$ = T_STATIC; }
|
||||||
| T_ABSTRACT { $$ = T_ABSTRACT; }
|
| T_ABSTRACT { $$ = T_ABSTRACT; }
|
||||||
| T_FINAL { $$ = T_FINAL; }
|
| T_FINAL { $$ = T_FINAL; }
|
||||||
|
|
|
@ -1725,6 +1725,18 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_
|
||||||
RETURN_TOKEN_WITH_IDENT(T_PROTECTED);
|
RETURN_TOKEN_WITH_IDENT(T_PROTECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<ST_IN_SCRIPTING>"public(set)" {
|
||||||
|
RETURN_TOKEN_WITH_IDENT(T_PUBLIC_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
<ST_IN_SCRIPTING>"protected(set)" {
|
||||||
|
RETURN_TOKEN_WITH_IDENT(T_PROTECTED_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
<ST_IN_SCRIPTING>"private(set)" {
|
||||||
|
RETURN_TOKEN_WITH_IDENT(T_PRIVATE_SET);
|
||||||
|
}
|
||||||
|
|
||||||
<ST_IN_SCRIPTING>"public" {
|
<ST_IN_SCRIPTING>"public" {
|
||||||
RETURN_TOKEN_WITH_IDENT(T_PUBLIC);
|
RETURN_TOKEN_WITH_IDENT(T_PUBLIC);
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,13 +304,6 @@ static ZEND_COLD zend_never_inline bool zend_deprecated_dynamic_property(
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ZEND_COLD zend_never_inline void zend_readonly_property_modification_scope_error(
|
|
||||||
const zend_class_entry *ce, const zend_string *member, const zend_class_entry *scope, const char *operation) {
|
|
||||||
zend_throw_error(NULL, "Cannot %s readonly property %s::$%s from %s%s",
|
|
||||||
operation, ZSTR_VAL(ce->name), ZSTR_VAL(member),
|
|
||||||
scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
static ZEND_COLD zend_never_inline void zend_readonly_property_unset_error(
|
static ZEND_COLD zend_never_inline void zend_readonly_property_unset_error(
|
||||||
zend_class_entry *ce, zend_string *member) {
|
zend_class_entry *ce, zend_string *member) {
|
||||||
zend_throw_error(NULL, "Cannot unset readonly property %s::$%s",
|
zend_throw_error(NULL, "Cannot unset readonly property %s::$%s",
|
||||||
|
@ -547,6 +540,17 @@ ZEND_API zend_result zend_check_property_access(const zend_object *zobj, zend_st
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info) {
|
||||||
|
ZEND_ASSERT(prop_info->flags & ZEND_ACC_PPP_SET_MASK);
|
||||||
|
ZEND_ASSERT(!(prop_info->flags & ZEND_ACC_PUBLIC_SET));
|
||||||
|
zend_class_entry *scope = get_fake_or_executed_scope();
|
||||||
|
if (prop_info->ce == scope) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return EXPECTED((prop_info->flags & ZEND_ACC_PROTECTED_SET)
|
||||||
|
&& is_protected_compatible_scope(prop_info->ce, scope));
|
||||||
|
}
|
||||||
|
|
||||||
static void zend_property_guard_dtor(zval *el) /* {{{ */ {
|
static void zend_property_guard_dtor(zval *el) /* {{{ */ {
|
||||||
uint32_t *ptr = (uint32_t*)Z_PTR_P(el);
|
uint32_t *ptr = (uint32_t*)Z_PTR_P(el);
|
||||||
if (EXPECTED(!(((uintptr_t)ptr) & 1))) {
|
if (EXPECTED(!(((uintptr_t)ptr) & 1))) {
|
||||||
|
@ -686,32 +690,31 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
|
||||||
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
|
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
|
||||||
try_again:
|
try_again:
|
||||||
retval = OBJ_PROP(zobj, property_offset);
|
retval = OBJ_PROP(zobj, property_offset);
|
||||||
if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
|
|
||||||
if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)
|
if (prop_info && UNEXPECTED(prop_info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))
|
||||||
&& (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) {
|
&& (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)
|
||||||
if (Z_TYPE_P(retval) == IS_OBJECT) {
|
&& ((prop_info->flags & ZEND_ACC_READONLY) || !zend_asymmetric_property_has_set_access(prop_info))) {
|
||||||
/* For objects, W/RW/UNSET fetch modes might not actually modify object.
|
if (Z_TYPE_P(retval) == IS_OBJECT) {
|
||||||
* Similar as with magic __get() allow them, but return the value as a copy
|
/* For objects, W/RW/UNSET fetch modes might not actually modify object.
|
||||||
* to make sure no actual modification is possible. */
|
* Similar as with magic __get() allow them, but return the value as a copy
|
||||||
ZVAL_COPY(rv, retval);
|
* to make sure no actual modification is possible. */
|
||||||
retval = rv;
|
ZVAL_COPY(rv, retval);
|
||||||
} else {
|
retval = rv;
|
||||||
zend_readonly_property_indirect_modification_error(prop_info);
|
goto exit;
|
||||||
retval = &EG(uninitialized_zval);
|
} else if (Z_TYPE_P(retval) == IS_UNDEF && type == BP_VAR_UNSET) {
|
||||||
}
|
retval = &EG(uninitialized_zval);
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
|
if (prop_info->flags & ZEND_ACC_READONLY) {
|
||||||
|
zend_readonly_property_indirect_modification_error(prop_info);
|
||||||
|
} else {
|
||||||
|
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||||
|
}
|
||||||
|
retval = &EG(uninitialized_zval);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
|
||||||
goto exit;
|
goto exit;
|
||||||
} else {
|
|
||||||
if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
|
|
||||||
if (type == BP_VAR_W || type == BP_VAR_RW) {
|
|
||||||
zend_readonly_property_indirect_modification_error(prop_info);
|
|
||||||
retval = &EG(uninitialized_zval);
|
|
||||||
goto exit;
|
|
||||||
} else if (type == BP_VAR_UNSET) {
|
|
||||||
retval = &EG(uninitialized_zval);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (UNEXPECTED(Z_PROP_FLAG_P(retval) & IS_PROP_UNINIT)) {
|
if (UNEXPECTED(Z_PROP_FLAG_P(retval) & IS_PROP_UNINIT)) {
|
||||||
/* Skip __get() for uninitialized typed properties */
|
/* Skip __get() for uninitialized typed properties */
|
||||||
|
@ -912,36 +915,12 @@ static zend_always_inline bool property_uses_strict_types(void) {
|
||||||
&& ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data));
|
&& ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool verify_readonly_initialization_access(
|
|
||||||
const zend_property_info *prop_info, const zend_class_entry *ce,
|
|
||||||
zend_string *name, const char *operation) {
|
|
||||||
zend_class_entry *scope = get_fake_or_executed_scope();
|
|
||||||
if (prop_info->ce == scope) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We may have redeclared a parent property. In that case the parent should still be
|
|
||||||
* allowed to initialize it. */
|
|
||||||
if (scope && is_derived_class(ce, scope)) {
|
|
||||||
const zend_property_info *prop_info = zend_hash_find_ptr(&scope->properties_info, name);
|
|
||||||
if (prop_info) {
|
|
||||||
/* This should be ensured by inheritance. */
|
|
||||||
ZEND_ASSERT(prop_info->flags & ZEND_ACC_READONLY);
|
|
||||||
if (prop_info->ce == scope) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zend_readonly_property_modification_scope_error(prop_info->ce, name, scope, operation);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot) /* {{{ */
|
ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot) /* {{{ */
|
||||||
{
|
{
|
||||||
zval *variable_ptr, tmp;
|
zval *variable_ptr, tmp;
|
||||||
uintptr_t property_offset;
|
uintptr_t property_offset;
|
||||||
const zend_property_info *prop_info = NULL;
|
const zend_property_info *prop_info = NULL;
|
||||||
|
uint32_t *guard = NULL;
|
||||||
ZEND_ASSERT(!Z_ISREF_P(value));
|
ZEND_ASSERT(!Z_ISREF_P(value));
|
||||||
|
|
||||||
property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__set != NULL), cache_slot, &prop_info);
|
property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__set != NULL), cache_slot, &prop_info);
|
||||||
|
@ -949,17 +928,35 @@ ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zva
|
||||||
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
|
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
|
||||||
try_again:
|
try_again:
|
||||||
variable_ptr = OBJ_PROP(zobj, property_offset);
|
variable_ptr = OBJ_PROP(zobj, property_offset);
|
||||||
if (Z_TYPE_P(variable_ptr) != IS_UNDEF) {
|
|
||||||
Z_TRY_ADDREF_P(value);
|
|
||||||
|
|
||||||
if (prop_info) {
|
if (prop_info && UNEXPECTED(prop_info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))) {
|
||||||
if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(variable_ptr) & IS_PROP_REINITABLE))) {
|
bool error;
|
||||||
Z_TRY_DELREF_P(value);
|
if (Z_TYPE_P(variable_ptr) != IS_UNDEF || (Z_PROP_FLAG_P(variable_ptr) & IS_PROP_UNINIT) || !zobj->ce->__set) {
|
||||||
|
error = true;
|
||||||
|
} else {
|
||||||
|
guard = zend_get_property_guard(zobj, name);
|
||||||
|
error = (*guard) & IN_SET;
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
if ((prop_info->flags & ZEND_ACC_READONLY)
|
||||||
|
&& Z_TYPE_P(variable_ptr) != IS_UNDEF
|
||||||
|
&& !(Z_PROP_FLAG_P(variable_ptr) & IS_PROP_REINITABLE)) {
|
||||||
zend_readonly_property_modification_error(prop_info);
|
zend_readonly_property_modification_error(prop_info);
|
||||||
variable_ptr = &EG(error_zval);
|
variable_ptr = &EG(error_zval);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
if ((prop_info->flags & ZEND_ACC_PPP_SET_MASK) && !zend_asymmetric_property_has_set_access(prop_info)) {
|
||||||
|
zend_asymmetric_visibility_property_modification_error(prop_info, "modify");
|
||||||
|
variable_ptr = &EG(error_zval);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Z_TYPE_P(variable_ptr) != IS_UNDEF) {
|
||||||
|
Z_TRY_ADDREF_P(value);
|
||||||
|
|
||||||
|
if (prop_info) {
|
||||||
typed_property:
|
typed_property:
|
||||||
ZVAL_COPY_VALUE(&tmp, value);
|
ZVAL_COPY_VALUE(&tmp, value);
|
||||||
// Increase refcount to prevent object from being released in __toString()
|
// Increase refcount to prevent object from being released in __toString()
|
||||||
|
@ -1073,7 +1070,9 @@ found:;
|
||||||
|
|
||||||
/* magic set */
|
/* magic set */
|
||||||
if (zobj->ce->__set) {
|
if (zobj->ce->__set) {
|
||||||
uint32_t *guard = zend_get_property_guard(zobj, name);
|
if (!guard) {
|
||||||
|
guard = zend_get_property_guard(zobj, name);
|
||||||
|
}
|
||||||
|
|
||||||
if (!((*guard) & IN_SET)) {
|
if (!((*guard) & IN_SET)) {
|
||||||
GC_ADDREF(zobj);
|
GC_ADDREF(zobj);
|
||||||
|
@ -1099,12 +1098,6 @@ write_std_property:
|
||||||
|
|
||||||
Z_TRY_ADDREF_P(value);
|
Z_TRY_ADDREF_P(value);
|
||||||
if (prop_info) {
|
if (prop_info) {
|
||||||
if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY)
|
|
||||||
&& !verify_readonly_initialization_access(prop_info, zobj->ce, name, "initialize"))) {
|
|
||||||
Z_TRY_DELREF_P(value);
|
|
||||||
variable_ptr = &EG(error_zval);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
goto typed_property;
|
goto typed_property;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1270,9 +1263,10 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam
|
||||||
ZVAL_NULL(retval);
|
ZVAL_NULL(retval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
|
} else if (prop_info && UNEXPECTED(prop_info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))) {
|
||||||
/* Readonly property, delegate to read_property + write_property. */
|
if ((prop_info->flags & ZEND_ACC_READONLY) || !zend_asymmetric_property_has_set_access(prop_info)) {
|
||||||
retval = NULL;
|
retval = NULL;
|
||||||
|
}
|
||||||
} else if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
|
} else if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
|
||||||
ZVAL_NULL(retval);
|
ZVAL_NULL(retval);
|
||||||
}
|
}
|
||||||
|
@ -1280,9 +1274,10 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam
|
||||||
/* we do have getter - fail and let it try again with usual get/set */
|
/* we do have getter - fail and let it try again with usual get/set */
|
||||||
retval = NULL;
|
retval = NULL;
|
||||||
}
|
}
|
||||||
} else if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
|
} else if (prop_info && UNEXPECTED(prop_info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))) {
|
||||||
/* Readonly property, delegate to read_property + write_property. */
|
if ((prop_info->flags & ZEND_ACC_READONLY) || !zend_asymmetric_property_has_set_access(prop_info)) {
|
||||||
retval = NULL;
|
retval = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
|
} else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
|
||||||
if (EXPECTED(zobj->properties)) {
|
if (EXPECTED(zobj->properties)) {
|
||||||
|
@ -1327,21 +1322,36 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void
|
||||||
{
|
{
|
||||||
uintptr_t property_offset;
|
uintptr_t property_offset;
|
||||||
const zend_property_info *prop_info = NULL;
|
const zend_property_info *prop_info = NULL;
|
||||||
|
uint32_t *guard = NULL;
|
||||||
|
|
||||||
property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__unset != NULL), cache_slot, &prop_info);
|
property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__unset != NULL), cache_slot, &prop_info);
|
||||||
|
|
||||||
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
|
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
|
||||||
zval *slot = OBJ_PROP(zobj, property_offset);
|
zval *slot = OBJ_PROP(zobj, property_offset);
|
||||||
|
|
||||||
if (Z_TYPE_P(slot) != IS_UNDEF) {
|
if (prop_info && UNEXPECTED(prop_info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))) {
|
||||||
if (UNEXPECTED(prop_info && (prop_info->flags & ZEND_ACC_READONLY))) {
|
bool error;
|
||||||
if (Z_PROP_FLAG_P(slot) & IS_PROP_REINITABLE) {
|
if (Z_TYPE_P(slot) != IS_UNDEF || Z_PROP_FLAG_P(slot) & IS_PROP_UNINIT || !zobj->ce->__unset) {
|
||||||
Z_PROP_FLAG_P(slot) &= ~IS_PROP_REINITABLE;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
|
guard = zend_get_property_guard(zobj, name);
|
||||||
|
error = (*guard) & IN_UNSET;
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
if ((prop_info->flags & ZEND_ACC_READONLY)
|
||||||
|
&& Z_TYPE_P(slot) != IS_UNDEF
|
||||||
|
&& !(Z_PROP_FLAG_P(slot) & IS_PROP_REINITABLE)) {
|
||||||
zend_readonly_property_unset_error(prop_info->ce, name);
|
zend_readonly_property_unset_error(prop_info->ce, name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ((prop_info->flags & ZEND_ACC_PPP_SET_MASK) && !zend_asymmetric_property_has_set_access(prop_info)) {
|
||||||
|
zend_asymmetric_visibility_property_modification_error(prop_info, "unset");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Z_TYPE_P(slot) != IS_UNDEF) {
|
||||||
if (UNEXPECTED(Z_ISREF_P(slot)) &&
|
if (UNEXPECTED(Z_ISREF_P(slot)) &&
|
||||||
(ZEND_DEBUG || ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(slot)))) {
|
(ZEND_DEBUG || ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(slot)))) {
|
||||||
if (prop_info) {
|
if (prop_info) {
|
||||||
|
@ -1358,11 +1368,6 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (UNEXPECTED(Z_PROP_FLAG_P(slot) & IS_PROP_UNINIT)) {
|
if (UNEXPECTED(Z_PROP_FLAG_P(slot) & IS_PROP_UNINIT)) {
|
||||||
if (UNEXPECTED(prop_info && (prop_info->flags & ZEND_ACC_READONLY)
|
|
||||||
&& !verify_readonly_initialization_access(prop_info, zobj->ce, name, "unset"))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset the IS_PROP_UNINIT flag, if it exists and bypass __unset(). */
|
/* Reset the IS_PROP_UNINIT flag, if it exists and bypass __unset(). */
|
||||||
Z_PROP_FLAG_P(slot) = 0;
|
Z_PROP_FLAG_P(slot) = 0;
|
||||||
return;
|
return;
|
||||||
|
@ -1388,7 +1393,9 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void
|
||||||
|
|
||||||
/* magic unset */
|
/* magic unset */
|
||||||
if (zobj->ce->__unset) {
|
if (zobj->ce->__unset) {
|
||||||
uint32_t *guard = zend_get_property_guard(zobj, name);
|
if (!guard) {
|
||||||
|
guard = zend_get_property_guard(zobj, name);
|
||||||
|
}
|
||||||
if (!((*guard) & IN_UNSET)) {
|
if (!((*guard) & IN_UNSET)) {
|
||||||
/* have unsetter - try with it! */
|
/* have unsetter - try with it! */
|
||||||
(*guard) |= IN_UNSET; /* prevent circular unsetting */
|
(*guard) |= IN_UNSET; /* prevent circular unsetting */
|
||||||
|
|
|
@ -310,6 +310,8 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
|
||||||
const zend_property_info *prop_info,
|
const zend_property_info *prop_info,
|
||||||
zend_property_hook_kind kind, zend_string *prop_name);
|
zend_property_hook_kind kind, zend_string *prop_name);
|
||||||
|
|
||||||
|
ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info);
|
||||||
|
|
||||||
#define zend_release_properties(ht) do { \
|
#define zend_release_properties(ht) do { \
|
||||||
if ((ht) && !(GC_FLAGS(ht) & GC_IMMUTABLE) && !GC_DELREF(ht)) { \
|
if ((ht) && !(GC_FLAGS(ht) & GC_IMMUTABLE) && !GC_DELREF(ht)) { \
|
||||||
zend_array_destroy(ht); \
|
zend_array_destroy(ht); \
|
||||||
|
|
|
@ -2648,6 +2648,22 @@ static void ZEND_FASTCALL zend_jit_assign_obj_helper(zend_object *zobj, zend_str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static zend_always_inline bool verify_readonly_and_avis(zval *property_val, zend_property_info *info, bool indirect)
|
||||||
|
{
|
||||||
|
if (UNEXPECTED(info->flags & (ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK))) {
|
||||||
|
if ((info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE)) {
|
||||||
|
zend_readonly_property_modification_error(info);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((info->flags & ZEND_ACC_PPP_SET_MASK) && !zend_asymmetric_property_has_set_access(info)) {
|
||||||
|
const char *operation = indirect ? "indirectly modify" : "modify";
|
||||||
|
zend_asymmetric_visibility_property_modification_error(info, operation);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void ZEND_FASTCALL zend_jit_assign_to_typed_prop(zval *property_val, zend_property_info *info, zval *value, zval *result)
|
static void ZEND_FASTCALL zend_jit_assign_to_typed_prop(zval *property_val, zend_property_info *info, zval *value, zval *result)
|
||||||
{
|
{
|
||||||
zend_execute_data *execute_data = EG(current_execute_data);
|
zend_execute_data *execute_data = EG(current_execute_data);
|
||||||
|
@ -2661,8 +2677,7 @@ static void ZEND_FASTCALL zend_jit_assign_to_typed_prop(zval *property_val, zend
|
||||||
value = &EG(uninitialized_zval);
|
value = &EG(uninitialized_zval);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UNEXPECTED((info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE))) {
|
if (UNEXPECTED(!verify_readonly_and_avis(property_val, info, false))) {
|
||||||
zend_readonly_property_modification_error(info);
|
|
||||||
if (result) {
|
if (result) {
|
||||||
ZVAL_UNDEF(result);
|
ZVAL_UNDEF(result);
|
||||||
}
|
}
|
||||||
|
@ -2723,8 +2738,7 @@ static void ZEND_FASTCALL zend_jit_assign_op_to_typed_prop(zval *zptr, zend_prop
|
||||||
zend_execute_data *execute_data = EG(current_execute_data);
|
zend_execute_data *execute_data = EG(current_execute_data);
|
||||||
zval z_copy;
|
zval z_copy;
|
||||||
|
|
||||||
if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(zptr) & IS_PROP_REINITABLE))) {
|
if (UNEXPECTED(!verify_readonly_and_avis(zptr, prop_info, true))) {
|
||||||
zend_readonly_property_modification_error(prop_info);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2818,8 +2832,7 @@ static void ZEND_FASTCALL zend_jit_inc_typed_prop(zval *var_ptr, zend_property_i
|
||||||
{
|
{
|
||||||
ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
|
ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
|
||||||
|
|
||||||
if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(var_ptr) & IS_PROP_REINITABLE))) {
|
if (UNEXPECTED(!verify_readonly_and_avis(var_ptr, prop_info, true))) {
|
||||||
zend_readonly_property_modification_error(prop_info);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2851,8 +2864,7 @@ static void ZEND_FASTCALL zend_jit_dec_typed_prop(zval *var_ptr, zend_property_i
|
||||||
{
|
{
|
||||||
ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
|
ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
|
||||||
|
|
||||||
if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(var_ptr) & IS_PROP_REINITABLE))) {
|
if (UNEXPECTED(!verify_readonly_and_avis(var_ptr, prop_info, true))) {
|
||||||
zend_readonly_property_modification_error(prop_info);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2898,8 +2910,7 @@ static void ZEND_FASTCALL zend_jit_post_inc_typed_prop(zval *var_ptr, zend_prope
|
||||||
{
|
{
|
||||||
ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
|
ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
|
||||||
|
|
||||||
if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(var_ptr) & IS_PROP_REINITABLE))) {
|
if (UNEXPECTED(!verify_readonly_and_avis(var_ptr, prop_info, true))) {
|
||||||
zend_readonly_property_modification_error(prop_info);
|
|
||||||
if (result) {
|
if (result) {
|
||||||
ZVAL_UNDEF(result);
|
ZVAL_UNDEF(result);
|
||||||
}
|
}
|
||||||
|
@ -2933,8 +2944,7 @@ static void ZEND_FASTCALL zend_jit_post_dec_typed_prop(zval *var_ptr, zend_prope
|
||||||
{
|
{
|
||||||
ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
|
ZEND_ASSERT(Z_TYPE_P(var_ptr) != IS_UNDEF);
|
||||||
|
|
||||||
if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY) && !(Z_PROP_FLAG_P(var_ptr) & IS_PROP_REINITABLE))) {
|
if (UNEXPECTED(!verify_readonly_and_avis(var_ptr, prop_info, true))) {
|
||||||
zend_readonly_property_modification_error(prop_info);
|
|
||||||
if (result) {
|
if (result) {
|
||||||
ZVAL_UNDEF(result);
|
ZVAL_UNDEF(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13933,16 +13933,37 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
|
||||||
&& (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT)))) {
|
&& (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT)))) {
|
||||||
uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
|
uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
|
||||||
|
|
||||||
|
ir_ref allowed_inputs = IR_UNUSED;
|
||||||
|
ir_ref forbidden_inputs = IR_UNUSED;
|
||||||
|
|
||||||
ir_ref prop_info_ref = ir_LOAD_A(
|
ir_ref prop_info_ref = ir_LOAD_A(
|
||||||
ir_ADD_OFFSET(run_time_cache, (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2));
|
ir_ADD_OFFSET(run_time_cache, (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2));
|
||||||
ir_ref if_has_prop_info = ir_IF(prop_info_ref);
|
ir_ref if_has_prop_info = ir_IF(prop_info_ref);
|
||||||
|
|
||||||
ir_IF_TRUE_cold(if_has_prop_info);
|
ir_IF_TRUE_cold(if_has_prop_info);
|
||||||
|
|
||||||
ir_ref if_readonly = ir_IF(
|
ir_ref prop_flags = ir_LOAD_U32(ir_ADD_OFFSET(prop_info_ref, offsetof(zend_property_info, flags)));
|
||||||
ir_AND_U32(ir_LOAD_U32(ir_ADD_OFFSET(prop_info_ref, offsetof(zend_property_info, flags))),
|
ir_ref if_readonly_or_avis = ir_IF(ir_AND_U32(prop_flags, ir_CONST_U32(ZEND_ACC_READONLY|ZEND_ACC_PPP_SET_MASK)));
|
||||||
ir_CONST_U32(ZEND_ACC_READONLY)));
|
|
||||||
|
ir_IF_FALSE(if_readonly_or_avis);
|
||||||
|
ir_END_list(allowed_inputs);
|
||||||
|
|
||||||
|
ir_IF_TRUE_cold(if_readonly_or_avis);
|
||||||
|
|
||||||
|
ir_ref if_readonly = ir_IF(ir_AND_U32(prop_flags, ir_CONST_U32(ZEND_ACC_READONLY)));
|
||||||
ir_IF_TRUE(if_readonly);
|
ir_IF_TRUE(if_readonly);
|
||||||
|
ir_END_list(forbidden_inputs);
|
||||||
|
|
||||||
|
ir_IF_FALSE(if_readonly);
|
||||||
|
ir_ref has_avis_access = ir_CALL_1(IR_BOOL, ir_CONST_FC_FUNC(zend_asymmetric_property_has_set_access), prop_info_ref);
|
||||||
|
ir_ref if_avis_access = ir_IF(has_avis_access);
|
||||||
|
ir_IF_TRUE(if_avis_access);
|
||||||
|
ir_END_list(allowed_inputs);
|
||||||
|
|
||||||
|
ir_IF_FALSE(if_avis_access);
|
||||||
|
ir_END_list(forbidden_inputs);
|
||||||
|
|
||||||
|
ir_MERGE_list(forbidden_inputs);
|
||||||
|
|
||||||
ir_ref if_prop_obj = jit_if_Z_TYPE(jit, prop_addr, IS_OBJECT);
|
ir_ref if_prop_obj = jit_if_Z_TYPE(jit, prop_addr, IS_OBJECT);
|
||||||
ir_IF_TRUE(if_prop_obj);
|
ir_IF_TRUE(if_prop_obj);
|
||||||
|
@ -13955,19 +13976,27 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
|
||||||
ir_IF_FALSE_cold(if_prop_obj);
|
ir_IF_FALSE_cold(if_prop_obj);
|
||||||
|
|
||||||
jit_SET_EX_OPLINE(jit, opline);
|
jit_SET_EX_OPLINE(jit, opline);
|
||||||
|
if_readonly = ir_IF(ir_AND_U32(prop_flags, ir_CONST_U32(ZEND_ACC_READONLY)));
|
||||||
|
ir_IF_TRUE(if_readonly);
|
||||||
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_readonly_property_indirect_modification_error), prop_info_ref);
|
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_readonly_property_indirect_modification_error), prop_info_ref);
|
||||||
jit_set_Z_TYPE_INFO(jit, res_addr, _IS_ERROR);
|
jit_set_Z_TYPE_INFO(jit, res_addr, _IS_ERROR);
|
||||||
ir_END_list(end_inputs);
|
ir_END_list(end_inputs);
|
||||||
|
|
||||||
|
ir_IF_FALSE(if_readonly);
|
||||||
|
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_asymmetric_visibility_property_modification_error),
|
||||||
|
prop_info_ref, ir_CONST_ADDR("indirectly modify"));
|
||||||
|
jit_set_Z_TYPE_INFO(jit, res_addr, _IS_ERROR);
|
||||||
|
ir_END_list(end_inputs);
|
||||||
|
|
||||||
|
ir_MERGE_list(allowed_inputs);
|
||||||
|
|
||||||
if (flags == ZEND_FETCH_DIM_WRITE) {
|
if (flags == ZEND_FETCH_DIM_WRITE) {
|
||||||
ir_IF_FALSE_cold(if_readonly);
|
|
||||||
jit_SET_EX_OPLINE(jit, opline);
|
jit_SET_EX_OPLINE(jit, opline);
|
||||||
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_check_array_promotion),
|
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_check_array_promotion),
|
||||||
prop_ref, prop_info_ref);
|
prop_ref, prop_info_ref);
|
||||||
ir_END_list(end_inputs);
|
ir_END_list(end_inputs);
|
||||||
ir_IF_FALSE(if_has_prop_info);
|
ir_IF_FALSE(if_has_prop_info);
|
||||||
} else if (flags == ZEND_FETCH_REF) {
|
} else if (flags == ZEND_FETCH_REF) {
|
||||||
ir_IF_FALSE_cold(if_readonly);
|
|
||||||
ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_create_typed_ref),
|
ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_create_typed_ref),
|
||||||
prop_ref,
|
prop_ref,
|
||||||
prop_info_ref,
|
prop_info_ref,
|
||||||
|
@ -13976,10 +14005,7 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
|
||||||
ir_IF_FALSE(if_has_prop_info);
|
ir_IF_FALSE(if_has_prop_info);
|
||||||
} else {
|
} else {
|
||||||
ZEND_ASSERT(flags == 0);
|
ZEND_ASSERT(flags == 0);
|
||||||
ir_IF_FALSE(if_has_prop_info);
|
ir_MERGE_WITH_EMPTY_FALSE(if_has_prop_info);
|
||||||
ir_ref no_prop_info_path = ir_END();
|
|
||||||
ir_IF_FALSE(if_readonly);
|
|
||||||
ir_MERGE_WITH(no_prop_info_path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -14005,9 +14031,6 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
|
||||||
ir_IF_TRUE(if_def);
|
ir_IF_TRUE(if_def);
|
||||||
}
|
}
|
||||||
if (opline->opcode == ZEND_FETCH_OBJ_W && (prop_info->flags & ZEND_ACC_READONLY)) {
|
if (opline->opcode == ZEND_FETCH_OBJ_W && (prop_info->flags & ZEND_ACC_READONLY)) {
|
||||||
if (!prop_type_ref) {
|
|
||||||
prop_type_ref = jit_Z_TYPE_INFO(jit, prop_addr);
|
|
||||||
}
|
|
||||||
ir_ref if_prop_obj = jit_if_Z_TYPE(jit, prop_addr, IS_OBJECT);
|
ir_ref if_prop_obj = jit_if_Z_TYPE(jit, prop_addr, IS_OBJECT);
|
||||||
ir_IF_TRUE(if_prop_obj);
|
ir_IF_TRUE(if_prop_obj);
|
||||||
ir_ref ref = jit_Z_PTR(jit, prop_addr);
|
ir_ref ref = jit_Z_PTR(jit, prop_addr);
|
||||||
|
@ -14023,6 +14046,28 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
|
||||||
ir_END_list(end_inputs);
|
ir_END_list(end_inputs);
|
||||||
|
|
||||||
goto result_fetched;
|
goto result_fetched;
|
||||||
|
} else if (opline->opcode == ZEND_FETCH_OBJ_W && (prop_info->flags & ZEND_ACC_PPP_SET_MASK)) {
|
||||||
|
/* Readonly properties which are also asymmetric are never mutable indirectly, which is
|
||||||
|
* handled by the previous branch. */
|
||||||
|
ir_ref has_access = ir_CALL_1(IR_BOOL, ir_CONST_FC_FUNC(zend_asymmetric_property_has_set_access), ir_CONST_ADDR(prop_info));
|
||||||
|
|
||||||
|
ir_ref if_access = ir_IF(has_access);
|
||||||
|
ir_IF_FALSE_cold(if_access);
|
||||||
|
|
||||||
|
ir_ref if_prop_obj = jit_if_Z_TYPE(jit, prop_addr, IS_OBJECT);
|
||||||
|
ir_IF_TRUE(if_prop_obj);
|
||||||
|
ir_ref ref = jit_Z_PTR(jit, prop_addr);
|
||||||
|
jit_GC_ADDREF(jit, ref);
|
||||||
|
jit_set_Z_PTR(jit, res_addr, ref);
|
||||||
|
jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX);
|
||||||
|
ir_END_list(end_inputs);
|
||||||
|
|
||||||
|
ir_IF_FALSE_cold(if_prop_obj);
|
||||||
|
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_asymmetric_visibility_property_modification_error),
|
||||||
|
ir_CONST_ADDR(prop_info), ir_CONST_ADDR("indirectly modify"));
|
||||||
|
ir_END_list(end_inputs);
|
||||||
|
|
||||||
|
ir_IF_TRUE(if_access);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opline->opcode == ZEND_FETCH_OBJ_W
|
if (opline->opcode == ZEND_FETCH_OBJ_W
|
||||||
|
|
|
@ -940,6 +940,17 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha
|
||||||
smart_str_appends(str, "protected ");
|
smart_str_appends(str, "protected ");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
switch (prop->flags & ZEND_ACC_PPP_SET_MASK) {
|
||||||
|
case ZEND_ACC_PRIVATE_SET:
|
||||||
|
smart_str_appends(str, "private(set) ");
|
||||||
|
break;
|
||||||
|
case ZEND_ACC_PROTECTED_SET:
|
||||||
|
smart_str_appends(str, "protected(set) ");
|
||||||
|
break;
|
||||||
|
case ZEND_ACC_PUBLIC_SET:
|
||||||
|
ZEND_UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (prop->flags & ZEND_ACC_STATIC) {
|
if (prop->flags & ZEND_ACC_STATIC) {
|
||||||
smart_str_appends(str, "static ");
|
smart_str_appends(str, "static ");
|
||||||
}
|
}
|
||||||
|
@ -5679,6 +5690,16 @@ ZEND_METHOD(ReflectionProperty, isProtected)
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
ZEND_METHOD(ReflectionProperty, isPrivateSet)
|
||||||
|
{
|
||||||
|
_property_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PRIVATE_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZEND_METHOD(ReflectionProperty, isProtectedSet)
|
||||||
|
{
|
||||||
|
_property_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_PROTECTED_SET);
|
||||||
|
}
|
||||||
|
|
||||||
/* {{{ Returns whether this property is static */
|
/* {{{ Returns whether this property is static */
|
||||||
ZEND_METHOD(ReflectionProperty, isStatic)
|
ZEND_METHOD(ReflectionProperty, isStatic)
|
||||||
{
|
{
|
||||||
|
@ -5727,7 +5748,7 @@ ZEND_METHOD(ReflectionProperty, getModifiers)
|
||||||
{
|
{
|
||||||
reflection_object *intern;
|
reflection_object *intern;
|
||||||
property_reference *ref;
|
property_reference *ref;
|
||||||
uint32_t keep_flags = ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC | ZEND_ACC_READONLY | ZEND_ACC_ABSTRACT | ZEND_ACC_VIRTUAL;
|
uint32_t keep_flags = ZEND_ACC_PPP_MASK | ZEND_ACC_PPP_SET_MASK | ZEND_ACC_STATIC | ZEND_ACC_READONLY | ZEND_ACC_ABSTRACT | ZEND_ACC_VIRTUAL;
|
||||||
|
|
||||||
if (zend_parse_parameters_none() == FAILURE) {
|
if (zend_parse_parameters_none() == FAILURE) {
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
|
|
|
@ -444,6 +444,10 @@ class ReflectionProperty implements Reflector
|
||||||
public const int IS_PRIVATE = UNKNOWN;
|
public const int IS_PRIVATE = UNKNOWN;
|
||||||
/** @cvalue ZEND_ACC_ABSTRACT */
|
/** @cvalue ZEND_ACC_ABSTRACT */
|
||||||
public const int IS_ABSTRACT = UNKNOWN;
|
public const int IS_ABSTRACT = UNKNOWN;
|
||||||
|
/** @cvalue ZEND_ACC_PROTECTED_SET */
|
||||||
|
public const int IS_PROTECTED_SET = UNKNOWN;
|
||||||
|
/** @cvalue ZEND_ACC_PRIVATE_SET */
|
||||||
|
public const int IS_PRIVATE_SET = UNKNOWN;
|
||||||
|
|
||||||
public string $name;
|
public string $name;
|
||||||
public string $class;
|
public string $class;
|
||||||
|
@ -480,6 +484,10 @@ class ReflectionProperty implements Reflector
|
||||||
/** @tentative-return-type */
|
/** @tentative-return-type */
|
||||||
public function isProtected(): bool {}
|
public function isProtected(): bool {}
|
||||||
|
|
||||||
|
public function isPrivateSet(): bool {}
|
||||||
|
|
||||||
|
public function isProtectedSet(): bool {}
|
||||||
|
|
||||||
/** @tentative-return-type */
|
/** @tentative-return-type */
|
||||||
public function isStatic(): bool {}
|
public function isStatic(): bool {}
|
||||||
|
|
||||||
|
|
22
ext/reflection/php_reflection_arginfo.h
generated
22
ext/reflection/php_reflection_arginfo.h
generated
|
@ -1,5 +1,5 @@
|
||||||
/* This is a generated file, edit the .stub.php file instead.
|
/* This is a generated file, edit the .stub.php file instead.
|
||||||
* Stub hash: d2a365f6f398bbcf3f2520c28d508ca63f08b5ff */
|
* Stub hash: 28fde6ed0e247201ee25d608d27a4c5b0bb8f2f7 */
|
||||||
|
|
||||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0)
|
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0)
|
||||||
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
|
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
|
||||||
|
@ -371,6 +371,10 @@ ZEND_END_ARG_INFO()
|
||||||
|
|
||||||
#define arginfo_class_ReflectionProperty_isProtected arginfo_class_ReflectionFunctionAbstract_inNamespace
|
#define arginfo_class_ReflectionProperty_isProtected arginfo_class_ReflectionFunctionAbstract_inNamespace
|
||||||
|
|
||||||
|
#define arginfo_class_ReflectionProperty_isPrivateSet arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
|
||||||
|
|
||||||
|
#define arginfo_class_ReflectionProperty_isProtectedSet arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
|
||||||
|
|
||||||
#define arginfo_class_ReflectionProperty_isStatic arginfo_class_ReflectionFunctionAbstract_inNamespace
|
#define arginfo_class_ReflectionProperty_isStatic arginfo_class_ReflectionFunctionAbstract_inNamespace
|
||||||
|
|
||||||
#define arginfo_class_ReflectionProperty_isReadOnly arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
|
#define arginfo_class_ReflectionProperty_isReadOnly arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
|
||||||
|
@ -785,6 +789,8 @@ ZEND_METHOD(ReflectionProperty, isInitialized);
|
||||||
ZEND_METHOD(ReflectionProperty, isPublic);
|
ZEND_METHOD(ReflectionProperty, isPublic);
|
||||||
ZEND_METHOD(ReflectionProperty, isPrivate);
|
ZEND_METHOD(ReflectionProperty, isPrivate);
|
||||||
ZEND_METHOD(ReflectionProperty, isProtected);
|
ZEND_METHOD(ReflectionProperty, isProtected);
|
||||||
|
ZEND_METHOD(ReflectionProperty, isPrivateSet);
|
||||||
|
ZEND_METHOD(ReflectionProperty, isProtectedSet);
|
||||||
ZEND_METHOD(ReflectionProperty, isStatic);
|
ZEND_METHOD(ReflectionProperty, isStatic);
|
||||||
ZEND_METHOD(ReflectionProperty, isReadOnly);
|
ZEND_METHOD(ReflectionProperty, isReadOnly);
|
||||||
ZEND_METHOD(ReflectionProperty, isDefault);
|
ZEND_METHOD(ReflectionProperty, isDefault);
|
||||||
|
@ -1078,6 +1084,8 @@ static const zend_function_entry class_ReflectionProperty_methods[] = {
|
||||||
ZEND_ME(ReflectionProperty, isPublic, arginfo_class_ReflectionProperty_isPublic, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionProperty, isPublic, arginfo_class_ReflectionProperty_isPublic, ZEND_ACC_PUBLIC)
|
||||||
ZEND_ME(ReflectionProperty, isPrivate, arginfo_class_ReflectionProperty_isPrivate, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionProperty, isPrivate, arginfo_class_ReflectionProperty_isPrivate, ZEND_ACC_PUBLIC)
|
||||||
ZEND_ME(ReflectionProperty, isProtected, arginfo_class_ReflectionProperty_isProtected, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionProperty, isProtected, arginfo_class_ReflectionProperty_isProtected, ZEND_ACC_PUBLIC)
|
||||||
|
ZEND_ME(ReflectionProperty, isPrivateSet, arginfo_class_ReflectionProperty_isPrivateSet, ZEND_ACC_PUBLIC)
|
||||||
|
ZEND_ME(ReflectionProperty, isProtectedSet, arginfo_class_ReflectionProperty_isProtectedSet, ZEND_ACC_PUBLIC)
|
||||||
ZEND_ME(ReflectionProperty, isStatic, arginfo_class_ReflectionProperty_isStatic, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionProperty, isStatic, arginfo_class_ReflectionProperty_isStatic, ZEND_ACC_PUBLIC)
|
||||||
ZEND_ME(ReflectionProperty, isReadOnly, arginfo_class_ReflectionProperty_isReadOnly, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionProperty, isReadOnly, arginfo_class_ReflectionProperty_isReadOnly, ZEND_ACC_PUBLIC)
|
||||||
ZEND_ME(ReflectionProperty, isDefault, arginfo_class_ReflectionProperty_isDefault, ZEND_ACC_PUBLIC)
|
ZEND_ME(ReflectionProperty, isDefault, arginfo_class_ReflectionProperty_isDefault, ZEND_ACC_PUBLIC)
|
||||||
|
@ -1515,6 +1523,18 @@ static zend_class_entry *register_class_ReflectionProperty(zend_class_entry *cla
|
||||||
zend_declare_typed_class_constant(class_entry, const_IS_ABSTRACT_name, &const_IS_ABSTRACT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
zend_declare_typed_class_constant(class_entry, const_IS_ABSTRACT_name, &const_IS_ABSTRACT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||||
zend_string_release(const_IS_ABSTRACT_name);
|
zend_string_release(const_IS_ABSTRACT_name);
|
||||||
|
|
||||||
|
zval const_IS_PROTECTED_SET_value;
|
||||||
|
ZVAL_LONG(&const_IS_PROTECTED_SET_value, ZEND_ACC_PROTECTED_SET);
|
||||||
|
zend_string *const_IS_PROTECTED_SET_name = zend_string_init_interned("IS_PROTECTED_SET", sizeof("IS_PROTECTED_SET") - 1, 1);
|
||||||
|
zend_declare_typed_class_constant(class_entry, const_IS_PROTECTED_SET_name, &const_IS_PROTECTED_SET_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||||
|
zend_string_release(const_IS_PROTECTED_SET_name);
|
||||||
|
|
||||||
|
zval const_IS_PRIVATE_SET_value;
|
||||||
|
ZVAL_LONG(&const_IS_PRIVATE_SET_value, ZEND_ACC_PRIVATE_SET);
|
||||||
|
zend_string *const_IS_PRIVATE_SET_name = zend_string_init_interned("IS_PRIVATE_SET", sizeof("IS_PRIVATE_SET") - 1, 1);
|
||||||
|
zend_declare_typed_class_constant(class_entry, const_IS_PRIVATE_SET_name, &const_IS_PRIVATE_SET_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||||
|
zend_string_release(const_IS_PRIVATE_SET_name);
|
||||||
|
|
||||||
zval property_name_default_value;
|
zval property_name_default_value;
|
||||||
ZVAL_UNDEF(&property_name_default_value);
|
ZVAL_UNDEF(&property_name_default_value);
|
||||||
zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
|
zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
|
||||||
|
|
|
@ -25,8 +25,8 @@ Class [ <user> readonly class Foo ] {
|
||||||
}
|
}
|
||||||
|
|
||||||
- Properties [2] {
|
- Properties [2] {
|
||||||
Property [ public readonly int $bar ]
|
Property [ public protected(set) readonly int $bar ]
|
||||||
Property [ public readonly int $baz ]
|
Property [ public protected(set) readonly int $baz ]
|
||||||
}
|
}
|
||||||
|
|
||||||
- Methods [0] {
|
- Methods [0] {
|
||||||
|
|
|
@ -31,7 +31,7 @@ Class [ <user> final class Foo implements UnitEnum ] {
|
||||||
}
|
}
|
||||||
|
|
||||||
- Properties [1] {
|
- Properties [1] {
|
||||||
Property [ public readonly string $name ]
|
Property [ public protected(set) readonly string $name ]
|
||||||
}
|
}
|
||||||
|
|
||||||
- Methods [0] {
|
- Methods [0] {
|
||||||
|
|
|
@ -31,7 +31,7 @@ Class [ <user> final class Foo implements UnitEnum ] {
|
||||||
}
|
}
|
||||||
|
|
||||||
- Properties [1] {
|
- Properties [1] {
|
||||||
Property [ public readonly string $name ]
|
Property [ public protected(set) readonly string $name ]
|
||||||
}
|
}
|
||||||
|
|
||||||
- Methods [0] {
|
- Methods [0] {
|
||||||
|
|
34
ext/reflection/tests/asymmetric_visibility_flags.phpt
Normal file
34
ext/reflection/tests/asymmetric_visibility_flags.phpt
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
--TEST--
|
||||||
|
ReflectionProperty::is{Private,Protected}Set
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) int $bar;
|
||||||
|
public protected(set) int $baz;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test($property) {
|
||||||
|
$reflectionProperty = new ReflectionProperty(Foo::class, $property);
|
||||||
|
var_dump($reflectionProperty->isPrivateSet());
|
||||||
|
var_dump($reflectionProperty->isProtectedSet());
|
||||||
|
var_dump(($reflectionProperty->getModifiers() & ReflectionProperty::IS_PRIVATE_SET) !== 0);
|
||||||
|
var_dump(($reflectionProperty->getModifiers() & ReflectionProperty::IS_PROTECTED_SET) !== 0);
|
||||||
|
echo $reflectionProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('bar');
|
||||||
|
test('baz');
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
|
bool(false)
|
||||||
|
bool(true)
|
||||||
|
bool(false)
|
||||||
|
Property [ public private(set) int $bar ]
|
||||||
|
bool(false)
|
||||||
|
bool(true)
|
||||||
|
bool(false)
|
||||||
|
bool(true)
|
||||||
|
Property [ public protected(set) int $baz ]
|
25
ext/reflection/tests/asymmetric_visibility_write.phpt
Normal file
25
ext/reflection/tests/asymmetric_visibility_write.phpt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
--TEST--
|
||||||
|
ReflectionProperty::is{Private,Protected}Set
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Foo {
|
||||||
|
public private(set) int $bar = 0;
|
||||||
|
public protected(set) int $baz = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test($property) {
|
||||||
|
static $i = 0;
|
||||||
|
$foo = new Foo();
|
||||||
|
$reflectionProperty = new ReflectionProperty(Foo::class, $property);
|
||||||
|
$reflectionProperty->setValue($foo, $i++);
|
||||||
|
var_dump($reflectionProperty->getValue($foo));
|
||||||
|
}
|
||||||
|
|
||||||
|
test('bar');
|
||||||
|
test('baz');
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
int(0)
|
||||||
|
int(1)
|
|
@ -25,4 +25,4 @@ bool(false)
|
||||||
bool(false)
|
bool(false)
|
||||||
bool(true)
|
bool(true)
|
||||||
bool(true)
|
bool(true)
|
||||||
Property [ public readonly int $ro ]
|
Property [ public protected(set) readonly int $ro ]
|
||||||
|
|
|
@ -36,8 +36,8 @@ Class [ <internal%s> class Directory ] {
|
||||||
}
|
}
|
||||||
|
|
||||||
- Properties [2] {
|
- Properties [2] {
|
||||||
Property [ public readonly string $path ]
|
Property [ public protected(set) readonly string $path ]
|
||||||
Property [ public readonly mixed $handle ]
|
Property [ public protected(set) readonly mixed $handle ]
|
||||||
}
|
}
|
||||||
|
|
||||||
- Methods [3] {
|
- Methods [3] {
|
||||||
|
|
3
ext/tokenizer/tokenizer_data.c
generated
3
ext/tokenizer/tokenizer_data.c
generated
|
@ -92,6 +92,9 @@ char *get_token_type_name(int token_type)
|
||||||
case T_PRIVATE: return "T_PRIVATE";
|
case T_PRIVATE: return "T_PRIVATE";
|
||||||
case T_PROTECTED: return "T_PROTECTED";
|
case T_PROTECTED: return "T_PROTECTED";
|
||||||
case T_PUBLIC: return "T_PUBLIC";
|
case T_PUBLIC: return "T_PUBLIC";
|
||||||
|
case T_PRIVATE_SET: return "T_PRIVATE_SET";
|
||||||
|
case T_PROTECTED_SET: return "T_PROTECTED_SET";
|
||||||
|
case T_PUBLIC_SET: return "T_PUBLIC_SET";
|
||||||
case T_READONLY: return "T_READONLY";
|
case T_READONLY: return "T_READONLY";
|
||||||
case T_VAR: return "T_VAR";
|
case T_VAR: return "T_VAR";
|
||||||
case T_UNSET: return "T_UNSET";
|
case T_UNSET: return "T_UNSET";
|
||||||
|
|
|
@ -337,6 +337,21 @@ const T_PROTECTED = UNKNOWN;
|
||||||
* @cvalue T_PUBLIC
|
* @cvalue T_PUBLIC
|
||||||
*/
|
*/
|
||||||
const T_PUBLIC = UNKNOWN;
|
const T_PUBLIC = UNKNOWN;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* @cvalue T_PRIVATE_SET
|
||||||
|
*/
|
||||||
|
const T_PRIVATE_SET = UNKNOWN;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* @cvalue T_PROTECTED_SET
|
||||||
|
*/
|
||||||
|
const T_PROTECTED_SET = UNKNOWN;
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* @cvalue T_PUBLIC_SET
|
||||||
|
*/
|
||||||
|
const T_PUBLIC_SET = UNKNOWN;
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
* @cvalue T_READONLY
|
* @cvalue T_READONLY
|
||||||
|
|
5
ext/tokenizer/tokenizer_data_arginfo.h
generated
5
ext/tokenizer/tokenizer_data_arginfo.h
generated
|
@ -1,5 +1,5 @@
|
||||||
/* This is a generated file, edit the .stub.php file instead.
|
/* This is a generated file, edit the .stub.php file instead.
|
||||||
* Stub hash: b7a13b3b242bd535f62a7d819eb0df751efb54ed */
|
* Stub hash: d917cab61a2b436a16d2227cdb438add45e42d69 */
|
||||||
|
|
||||||
static void register_tokenizer_data_symbols(int module_number)
|
static void register_tokenizer_data_symbols(int module_number)
|
||||||
{
|
{
|
||||||
|
@ -70,6 +70,9 @@ static void register_tokenizer_data_symbols(int module_number)
|
||||||
REGISTER_LONG_CONSTANT("T_PRIVATE", T_PRIVATE, CONST_PERSISTENT);
|
REGISTER_LONG_CONSTANT("T_PRIVATE", T_PRIVATE, CONST_PERSISTENT);
|
||||||
REGISTER_LONG_CONSTANT("T_PROTECTED", T_PROTECTED, CONST_PERSISTENT);
|
REGISTER_LONG_CONSTANT("T_PROTECTED", T_PROTECTED, CONST_PERSISTENT);
|
||||||
REGISTER_LONG_CONSTANT("T_PUBLIC", T_PUBLIC, CONST_PERSISTENT);
|
REGISTER_LONG_CONSTANT("T_PUBLIC", T_PUBLIC, CONST_PERSISTENT);
|
||||||
|
REGISTER_LONG_CONSTANT("T_PRIVATE_SET", T_PRIVATE_SET, CONST_PERSISTENT);
|
||||||
|
REGISTER_LONG_CONSTANT("T_PROTECTED_SET", T_PROTECTED_SET, CONST_PERSISTENT);
|
||||||
|
REGISTER_LONG_CONSTANT("T_PUBLIC_SET", T_PUBLIC_SET, CONST_PERSISTENT);
|
||||||
REGISTER_LONG_CONSTANT("T_READONLY", T_READONLY, CONST_PERSISTENT);
|
REGISTER_LONG_CONSTANT("T_READONLY", T_READONLY, CONST_PERSISTENT);
|
||||||
REGISTER_LONG_CONSTANT("T_VAR", T_VAR, CONST_PERSISTENT);
|
REGISTER_LONG_CONSTANT("T_VAR", T_VAR, CONST_PERSISTENT);
|
||||||
REGISTER_LONG_CONSTANT("T_UNSET", T_UNSET, CONST_PERSISTENT);
|
REGISTER_LONG_CONSTANT("T_UNSET", T_UNSET, CONST_PERSISTENT);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue