Skip to content

Basic accessors implementation (WIP) #6873

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 44 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
fd951b9
Basic accessors implementation
nikic Mar 22, 2021
6a7269b
Support (generated) accessors in property promotion
nikic Apr 21, 2021
6f6f4a2
Suppress compile warning
nikic Apr 21, 2021
bce7dda
Add basic reflection support
nikic Apr 21, 2021
484dfa9
Represent accessors using an array
nikic Apr 21, 2021
672b615
Generate proper implementations for auto accessors
nikic Apr 21, 2021
a051fc1
Don't use mangled propery name during compilation
nikic Apr 21, 2021
19bb004
Explicit forbid accessors on static props
nikic Apr 21, 2021
aa1abc9
Improve opcache compatibility
nikic Apr 21, 2021
e3145b4
Fix JIT (by bailing out)
nikic Apr 21, 2021
11aafaa
Fix clang build (va promotion UB)
nikic Apr 21, 2021
d2c177d
Try to fix another maybe-uninitialized warning
nikic Apr 22, 2021
36dff63
Optimize reads from auto accessors
nikic Apr 22, 2021
ad8916d
Property handle by-ref distinction for auto accessors
nikic Apr 22, 2021
4c46fb1
Handle overloaded object semantics more precisely
nikic Apr 22, 2021
a94d2cb
Optimize isset() with proper accessor
nikic Apr 22, 2021
f982549
Optimize assignment to trivial accessor
nikic Apr 22, 2021
afcd945
Add dump test
nikic Apr 22, 2021
a467ae1
Fix trivial read/write cache
nikic Apr 22, 2021
f518ce0
Prohibit redundant final modifier
nikic Apr 27, 2021
f4f07fd
Prohibit final private property
nikic Apr 27, 2021
a8b8d08
Fix rebase fallout
nikic Apr 30, 2021
7af517f
Support accessors without backing property
nikic Apr 27, 2021
5a49974
Blanket forbid recursive accessor read/writes
nikic Apr 30, 2021
afddb9d
Don't allow default value on virtual accessors
nikic Apr 30, 2021
d5e6198
Don't allow mixing implicit and explicit accessors
nikic Apr 30, 2021
af100cc
Forbid implicit set without get
nikic Apr 30, 2021
b79cbdb
Support init-only properties
nikic Apr 30, 2021
afa6e7e
Never give accessors an implicit null default value
nikic Apr 30, 2021
17defb4
Forbig isolated implicit &get
nikic May 3, 2021
8f5ad8f
Add test for different types of indirect modification
nikic May 3, 2021
373f453
Forbid isolated &get even if not implicit
nikic May 3, 2021
4514702
Extend dump test
nikic May 3, 2021
976f2db
Allow final property with private accessor
nikic May 3, 2021
04dad4a
Expose isFinal() and isAbstract() in reflection
nikic May 3, 2021
1857bb9
Use proper return types for newly added methods
nikic May 3, 2021
5069793
Test one more private + final combination
nikic May 3, 2021
2a2880b
Add ref source for by-ref auto-prop
nikic May 4, 2021
00323ab
Fix constant updating with virtual properties
nikic May 4, 2021
91ca074
Fix unset of uninit flag if initialization fails
nikic May 4, 2021
eb70521
Forbid final in interface
nikic May 5, 2021
e97adc6
Fix class name in final property error
nikic May 5, 2021
a151ec5
List supported accessors in error message
nikic May 5, 2021
43d2b5c
Don't allow unserializing to virtual property
nikic May 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Zend/tests/accessors/abstract.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Abstract accessors
--FILE--
<?php

class Test {
public $prop {
abstract get;
set { echo __METHOD__, "()\n"; }
}
}

?>
--EXPECTF--
Fatal error: Class Test contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Test::$prop::get) in %s on line %d
17 changes: 17 additions & 0 deletions Zend/tests/accessors/abstract_prop.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Whole property can be marked abstract, but must use accessors
--FILE--
<?php

abstract class A {
abstract public $prop { get; set; }
}

class B extends A {
public $prop { get; set; }
}

?>
===DONE===
--EXPECT--
===DONE===
12 changes: 12 additions & 0 deletions Zend/tests/accessors/abstract_prop_abstract_accessor.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Abstract property with abstract accessor
--FILE--
<?php

class C {
abstract public $prop { abstract get; }
}

?>
--EXPECTF--
Fatal error: Accessor on abstract property cannot be explicitly abstract in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/accessors/abstract_prop_not_implemented.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Abstract property not implemented
--FILE--
<?php

class A {
abstract public $prop { get; set; }
}

class B extends A {
}

?>
--EXPECTF--
Fatal error: Class A contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (A::$prop::get, A::$prop::set) in %s on line %d
34 changes: 34 additions & 0 deletions Zend/tests/accessors/auto.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
Basic auto-generated accessors
--FILE--
<?php

class Test {
public $prop { get; private set; }

public function __construct() {
$this->prop = 42;
$this->prop = 24;
}
}

$test = new Test;
try {
$test->prop = 12;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($test->prop);
var_dump(isset($test->prop));
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Call to private accessor Test::$prop::set() from global scope
int(24)
bool(true)
Cannot unset accessor property Test::$prop
17 changes: 17 additions & 0 deletions Zend/tests/accessors/basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Basic accessor syntax
--FILE--
<?php

class Test {
public $prop1 { get; set; }
public $prop2 {
get { }
private set { }
}
public $prop3 = 1 { get; private set; }
}

?>
--EXPECT--

37 changes: 37 additions & 0 deletions Zend/tests/accessors/by_ref_get_typed_prop.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
By-ref getter with a typed property
--FILE--
<?php

class Test {
public array $prop = [] { &get; set; }
}

$test = new Test;
$test->prop[] = 42;
var_dump($test->prop);
$ref =& $test->prop;
$ref = [1, 2, 3];
var_dump($test->prop);

try {
$ref = 42;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
array(1) {
[0]=>
int(42)
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
Cannot assign int to reference held by property Test::$prop of type array
55 changes: 55 additions & 0 deletions Zend/tests/accessors/cache.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
--TEST--
Test caching of accessor property kind
--FILE--
<?php

class Test {
private $_prop;
public $prop {
&get { echo __METHOD__, "\n"; return $this->_prop; }
set { echo __METHOD__, "\n"; $this->_prop = $value; }
}
}

function doTest(Test $test) {
$test->prop;
$test->prop = 1;
$test->prop += 1;
$test->prop = [];
$test->prop[] = 1;
isset($test->prop);
isset($test->prop[0]);
try {
unset($test->prop);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
}

$test = new Test;
$test->dyn = 1;
doTest($test);
echo "\n";
doTest($test);

?>
--EXPECT--
Test::$prop::get
Test::$prop::set
Test::$prop::get
Test::$prop::set
Test::$prop::set
Test::$prop::get
Test::$prop::get
Test::$prop::get
Cannot unset accessor property Test::$prop

Test::$prop::get
Test::$prop::set
Test::$prop::get
Test::$prop::set
Test::$prop::set
Test::$prop::get
Test::$prop::get
Test::$prop::get
Cannot unset accessor property Test::$prop
16 changes: 16 additions & 0 deletions Zend/tests/accessors/default_on_virtual.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Purely virtual accessors cannot have default value
--FILE--
<?php

class Test {
private $_prop;
public $prop = 0 {
get { return $this->_prop; }
set { $this->_prop = $value; }
}
Comment on lines +8 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public $prop = 0 {
get { return $this->_prop; }
set { $this->_prop = $value; }
}
public $prop {
get { return $this->_prop; }
set { $this->_prop = $value; }
} = 0;

is allowed by C#, why such syntax is not supported? Supporting default values would be very helpful.

}

?>
--EXPECTF--
Fatal error: Cannot specify default value for property with explicit accessors in %s on line %d
46 changes: 46 additions & 0 deletions Zend/tests/accessors/dump.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
--TEST--
Dumping object with accessors
--FILE--
<?php

// The dump always only shows the contents of backing properties,
// not any computed properties.
class Test {
public $prop = 1;
public $prop2 = 2 { get; set; }
public $prop3 {
get { return 42; }
}
}

$test = new Test;
var_dump($test);
var_dump(get_object_vars($test));
var_dump((array) $test);

foreach ($test as $prop => $value) {
echo "$prop => $value\n";
}

?>
--EXPECT--
object(Test)#1 (2) {
["prop"]=>
int(1)
["prop2"]=>
int(2)
}
array(2) {
["prop"]=>
int(1)
["prop2"]=>
int(2)
}
array(2) {
["prop"]=>
int(1)
["prop2"]=>
int(2)
}
prop => 1
prop2 => 2
15 changes: 15 additions & 0 deletions Zend/tests/accessors/duplicate_accessor.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Cannot declare same accessor twice
--FILE--
<?php

class Test {
public $prop {
get {}
get {}
}
}

?>
--EXPECTF--
Fatal error: Cannot redeclare accessor "get" in %s on line %d
22 changes: 22 additions & 0 deletions Zend/tests/accessors/failed_prop_initialization.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Test the case where the initializing property assignment fails
--FILE--
<?php
class Test {
public int $prop { get; }
}
$b = new Test;
try {
$b->prop = "foo";
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try {
$b->prop;
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Cannot assign string to property Test::$prop of type int
Property Test::$prop must not be accessed before initialization
20 changes: 20 additions & 0 deletions Zend/tests/accessors/final.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Final accessors
--FILE--
<?php

class A {
public $prop {
final get { return 42; }
}
}

class B extends A {
public $prop {
get { return 24; }
}
}

?>
--EXPECTF--
Fatal error: Cannot override final accessor A::$prop::get() in %s on line %d
12 changes: 12 additions & 0 deletions Zend/tests/accessors/final_private_prop.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Property cannot be both final and private
--FILE--
<?php

class Test {
final private $prop;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still reading this rfc - final protected $prop { private get; private set; } is allowed and would work similarly to a private final property in terms of forbidding redefining it in subclasses? The only major difference would be serialize() output and ReflectionProperty behavior.

I guess that's consistent with private final methods recently being forbidden.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I was against that change at the time, but now we have to follow suit...

}

?>
--EXPECTF--
Fatal error: Property cannot be both final and private in %s on line %d
16 changes: 16 additions & 0 deletions Zend/tests/accessors/final_prop.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Property itself may be marked final (accessor)
--FILE--
<?php

class A {
public final $prop { get; set; }
}

class B extends A {
public $prop { get; set; }
}

?>
--EXPECTF--
Fatal error: Cannot override final property A::$prop in %s on line %d
16 changes: 16 additions & 0 deletions Zend/tests/accessors/final_prop_2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Property itself may be marked final (normal)
--FILE--
<?php

class A {
public final $prop;
}

class B extends A {
public $prop { get; set; }
}

?>
--EXPECTF--
Fatal error: Cannot override final property A::$prop in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/accessors/final_prop_final_accessor.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Cannot make accessor explicitly final in final property
--FILE--
<?php

class Test {
final public $prop {
final get;
final set;
}
}

?>
--EXPECTF--
Fatal error: Accessor on final property cannot be explicitly final in %s on line %d
Loading