Skip to content

Commit e219ec1

Browse files
nikicbwoebikrakjoedstogov
committed
Implement typed properties
RFC: https://wiki.php.net/rfc/typed_properties_v2 This is a squash of PR #3734, which is a squash of PR #3313. Co-authored-by: Bob Weinand <[email protected]> Co-authored-by: Joe Watkins <[email protected]> Co-authored-by: Dmitry Stogov <[email protected]>
1 parent fe8fdfa commit e219ec1

File tree

210 files changed

+23045
-5961
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

210 files changed

+23045
-5961
lines changed

UPGRADING

+13-1
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,20 @@ PHP 7.4 UPGRADE NOTES
7878
2. New Features
7979
========================================
8080

81+
- Core:
82+
. Added support for typed properties. For example:
83+
84+
class User {
85+
public int $id;
86+
public string $name;
87+
}
88+
89+
This will enforce that $user->id can only be assigned integer and
90+
$user->name can only be assigned strings. For more information see the
91+
RFC: https://wiki.php.net/rfc/typed_properties_v2
92+
8193
- PDO_OCI:
82-
. PDOStatement::getColumnMeta is now available
94+
. PDOStatement::getColumnMeta() is now available
8395

8496
- PDO_SQLite:
8597
. PDOStatement::getAttribute(PDO::SQLITE_ATTR_READONLY_STATEMENT) allows to

UPGRADING.INTERNALS

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ PHP 7.4 INTERNALS UPGRADE NOTES
1313
j. Removed add_get_assoc_*() and add_get_index_*()
1414
k. Class declaration opcodes
1515
l. HASH_FLAG_INITIALIZED
16+
m. write_property return value
17+
n. Assignments to references
1618

1719
2. Build system changes
1820
a. Abstract
@@ -158,6 +160,17 @@ PHP 7.4 INTERNALS UPGRADE NOTES
158160
Special HT_IS_INITIALIZED() and HT_INVALIDATE() macro were introduced
159161
to hide implementation details.
160162

163+
m. The write_property() object handler now returns the assigned value (after
164+
possible type coercions) rather than void. For extensions, it should
165+
usually be sufficient to return whatever was passed as the argument.
166+
167+
n. Assignments to references now need to ensure that they respect property
168+
types that affect the reference. This means that references should no
169+
longer be directly assigned to, and instead a set of specialized macros
170+
of the form ZEND_TRY_ASSIGN* needs to be used. You can find detailed
171+
porting instructions as well as a compatibility shim in the wiki:
172+
https://wiki.php.net/rfc/typed_properties_v2#assignments_to_references
173+
161174
========================
162175
2. Build system changes
163176
========================
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
Test typed properties basic operation
3+
--FILE--
4+
<?php
5+
var_dump(new class(1, 2.2, true, ["four"], new stdClass) {
6+
public int $int;
7+
public float $float;
8+
public bool $bool;
9+
public array $array;
10+
public stdClass $std;
11+
public iterable $it;
12+
13+
public function __construct(int $int, float $float, bool $bool, array $array, stdClass $std) {
14+
$this->int = $int;
15+
$this->float = $float;
16+
$this->bool = $bool;
17+
$this->array = $array;
18+
$this->std = $std;
19+
$this->it = $array;
20+
}
21+
});
22+
?>
23+
--EXPECTF--
24+
object(class@anonymous)#%d (6) {
25+
["int"]=>
26+
int(1)
27+
["float"]=>
28+
float(2.2)
29+
["bool"]=>
30+
bool(true)
31+
["array"]=>
32+
array(1) {
33+
[0]=>
34+
string(4) "four"
35+
}
36+
["std"]=>
37+
object(stdClass)#%d (0) {
38+
}
39+
["it"]=>
40+
array(1) {
41+
[0]=>
42+
string(4) "four"
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Test typed properties error condition (read uninitialized)
3+
--FILE--
4+
<?php
5+
$thing = new class() {
6+
public int $int;
7+
};
8+
9+
var_dump($thing->int);
10+
?>
11+
--EXPECTF--
12+
Fatal error: Uncaught Error: Typed property class@anonymous::$int must not be accessed before initialization in %s:6
13+
Stack trace:
14+
#0 {main}
15+
thrown in %s on line 6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Test typed properties error condition (fetch uninitialized by reference)
3+
--FILE--
4+
<?php
5+
$thing = new class() {
6+
public int $int;
7+
};
8+
9+
$var = &$thing->int;
10+
?>
11+
--EXPECTF--
12+
Fatal error: Uncaught Error: Cannot access uninitialized non-nullable property class@anonymous::$int by reference in %s:%d
13+
Stack trace:
14+
#0 {main}
15+
thrown in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Test typed properties error condition (type mismatch)
3+
--FILE--
4+
<?php
5+
new class("PHP 7 is better than you, and it knows it ...") {
6+
public int $int;
7+
8+
public function __construct(string $string) {
9+
$this->int = $string;
10+
}
11+
};
12+
?>
13+
--EXPECTF--
14+
Fatal error: Uncaught TypeError: Typed property class@anonymous::$int must be int, string used in %s:6
15+
Stack trace:
16+
#0 %s(2): class@anonymous->__construct('PHP 7 is better...')
17+
#1 {main}
18+
thrown in %s on line 6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Test typed properties error condition (type mismatch object)
3+
--FILE--
4+
<?php
5+
class Dummy {}
6+
7+
new class(new Dummy) {
8+
public stdClass $std;
9+
10+
public function __construct(Dummy $dummy) {
11+
$this->std = $dummy;
12+
}
13+
};
14+
?>
15+
--EXPECTF--
16+
Fatal error: Uncaught TypeError: Typed property class@anonymous::$std must be an instance of stdClass, Dummy used in %s:8
17+
Stack trace:
18+
#0 %s(4): class@anonymous->__construct(Object(Dummy))
19+
#1 {main}
20+
thrown in %s on line 8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Test typed properties inheritance (scalar)
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $qux;
7+
}
8+
9+
class Bar extends Foo {
10+
public string $qux;
11+
}
12+
?>
13+
--EXPECTF--
14+
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Test typed properties inheritance
3+
--FILE--
4+
<?php
5+
class Whatever {}
6+
class Thing extends Whatever {}
7+
8+
class Foo {
9+
public Whatever $qux;
10+
}
11+
12+
class Bar extends Foo {
13+
public Thing $qux;
14+
}
15+
?>
16+
--EXPECTF--
17+
Fatal error: Type of Bar::$qux must be Whatever (as in class Foo) in %s on line 11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Test typed properties inheritance (missing info)
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $qux;
7+
}
8+
9+
class Bar extends Foo {
10+
public $qux;
11+
}
12+
?>
13+
--EXPECTF--
14+
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Test typed properties unset leaves properties in an uninitialized state
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $bar;
7+
8+
public function __get($name) {
9+
var_dump($name);
10+
/* return value has to be compatible with int */
11+
return 0;
12+
}
13+
}
14+
15+
$foo = new Foo();
16+
17+
unset($foo->bar);
18+
19+
var_dump($foo->bar);
20+
?>
21+
--EXPECT--
22+
string(3) "bar"
23+
int(0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Test typed properties allow fetch reference
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $bar = 1;
7+
}
8+
9+
$cb = function(int &$bar) {
10+
var_dump($bar);
11+
};
12+
13+
$foo = new Foo();
14+
$cb($foo->bar);
15+
?>
16+
--EXPECT--
17+
int(1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Test typed properties allow fetch reference for init array
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $bar = 1;
7+
}
8+
9+
$foo = new Foo();
10+
11+
$array = [&$foo->bar];
12+
var_dump($array);
13+
?>
14+
--EXPECT--
15+
array(1) {
16+
[0]=>
17+
&int(1)
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Test typed properties allow fetch reference for foreach
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $bar = 1;
7+
}
8+
9+
$foo = new Foo();
10+
foreach ($foo as &$prop) {
11+
$prop++;
12+
}
13+
var_dump($foo);
14+
?>
15+
--EXPECT--
16+
object(Foo)#1 (1) {
17+
["bar"]=>
18+
&int(2)
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Test typed properties disallow incorrect type initial value (scalar)
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $bar = "string";
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Default value for property of type int can only be int in %s on line 3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Test typed properties disallow incorrect type initial value (array)
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public array $bar = 32;
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Default value for property of type array can only be an array in %s on line 3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Test typed properties disallow incorrect type initial value (object)
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public stdClass $bar = null;
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Default value for property of type stdClass may not be null. Use the nullable type ?stdClass to allow null default value in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Test typed properties initial values
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $int = 1;
7+
public float $flt = 2.2;
8+
public float $flt2 = 2;
9+
public array $arr = [];
10+
public bool $bool = false;
11+
public iterable $iter = [];
12+
}
13+
echo "ok\n";
14+
?>
15+
--EXPECT--
16+
ok
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Test typed properties disallow void
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public void $int;
7+
}
8+
9+
$foo = new Foo();
10+
?>
11+
--EXPECTF--
12+
Fatal error: Property Foo::$int cannot have type void in %s on line 3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Test typed properties type applies to all props in group
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $bar,
7+
$qux;
8+
}
9+
10+
$reflector = new ReflectionClass(Foo::class);
11+
12+
$prop = $reflector->getProperty("qux");
13+
14+
var_dump((string) $prop->getType());
15+
?>
16+
--EXPECT--
17+
string(3) "int"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Test typed properties int must not be allowed to overflow
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public int $bar = PHP_INT_MAX;
7+
8+
public function inc() {
9+
return ++$this->bar;
10+
}
11+
}
12+
13+
$foo = new Foo();
14+
15+
try {
16+
$foo->inc();
17+
} catch (TypeError $e) {
18+
echo $e->getMessage(), "\n";
19+
}
20+
?>
21+
--EXPECT--
22+
Cannot increment property Foo::$bar of type int past its maximal value

0 commit comments

Comments
 (0)