Skip to content

Commit 336eb48

Browse files
nikicnicolas-grekas
authored andcommitted
Automatically implement Stringable interface
1 parent 9e775db commit 336eb48

File tree

5 files changed

+80
-5
lines changed

5 files changed

+80
-5
lines changed

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,8 @@ PHP 8.0 UPGRADE NOTES
410410
. Some consistency fixes to variable syntax have been applied, for example
411411
writing `Foo::BAR::$baz` is now allowed.
412412
RFC: https://wiki.php.net/rfc/variable_syntax_tweaks
413+
. Added Stringable.
414+
RFC: https://wiki.php.net/rfc/stringable
413415

414416
- Date:
415417
. Added DateTime::createFromInterface() and
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
Stringable is automatically implemented
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public function __toString() {
8+
return "foo";
9+
}
10+
}
11+
12+
var_dump(new Test instanceof Stringable);
13+
var_dump((new ReflectionClass(Test::class))->getInterfaceNames());
14+
15+
class Test2 extends Test {
16+
public function __toString() {
17+
return "bar";
18+
}
19+
}
20+
21+
var_dump(new Test2 instanceof Stringable);
22+
var_dump((new ReflectionClass(Test2::class))->getInterfaceNames());
23+
24+
?>
25+
--EXPECT--
26+
bool(true)
27+
array(1) {
28+
[0]=>
29+
string(10) "Stringable"
30+
}
31+
bool(true)
32+
array(1) {
33+
[0]=>
34+
string(10) "Stringable"
35+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Automatic Stringable implementation participates in variance
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function test(): Stringable {}
8+
}
9+
class Bar extends Foo {
10+
public function test(): Bar {}
11+
public function __toString() {}
12+
}
13+
14+
?>
15+
===DONE===
16+
--EXPECT--
17+
===DONE===

Zend/zend_compile.c

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6066,6 +6066,24 @@ static void zend_check_magic_method_attr(uint32_t attr, const char* method, zend
60666066
}
60676067
/* }}} */
60686068

6069+
static void add_stringable_interface(zend_class_entry *ce) {
6070+
for (uint32_t i = 0; i < ce->num_interfaces; i++) {
6071+
if (zend_string_equals_literal(ce->interface_names[i].lc_name, "stringable")) {
6072+
/* Interface already explicitly implemented */
6073+
return;
6074+
}
6075+
}
6076+
6077+
ce->num_interfaces++;
6078+
ce->interface_names =
6079+
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
6080+
// TODO: Add known interned strings instead?
6081+
ce->interface_names[ce->num_interfaces - 1].name =
6082+
zend_string_init("Stringable", sizeof("Stringable") - 1, 0);
6083+
ce->interface_names[ce->num_interfaces - 1].lc_name =
6084+
zend_string_init("stringable", sizeof("stringable") - 1, 0);
6085+
}
6086+
60696087
void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body) /* {{{ */
60706088
{
60716089
zend_class_entry *ce = CG(active_class_entry);
@@ -6147,6 +6165,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
61476165
} else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
61486166
zend_check_magic_method_attr(fn_flags, "__toString", 0);
61496167
ce->__tostring = (zend_function *) op_array;
6168+
add_stringable_interface(ce);
61506169
} else if (zend_string_equals_literal(lcname, ZEND_INVOKE_FUNC_NAME)) {
61516170
zend_check_magic_method_attr(fn_flags, "__invoke", 0);
61526171
} else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
@@ -6680,6 +6699,10 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
66806699

66816700
CG(active_class_entry) = ce;
66826701

6702+
if (implements_ast) {
6703+
zend_compile_implements(implements_ast);
6704+
}
6705+
66836706
zend_compile_stmt(stmt_ast);
66846707

66856708
/* Reset lineno for final opcodes and errors */
@@ -6719,10 +6742,6 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
67196742
}
67206743
}
67216744

6722-
if (implements_ast) {
6723-
zend_compile_implements(implements_ast);
6724-
}
6725-
67266745
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
67276746
zend_verify_abstract_class(ce);
67286747
}

ext/spl/tests/class_implements_variation1.phpt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ Error: 2 - class_implements(): Class does not exist and could not be loaded, %s
184184
bool(false)
185185

186186
--instance of classWithToString--
187-
array(0) {
187+
array(1) {
188+
["Stringable"]=>
189+
string(10) "Stringable"
188190
}
189191

190192
--instance of classWithoutToString--

0 commit comments

Comments
 (0)