Skip to content

Commit d95c60a

Browse files
nikicnicolas-grekas
authored andcommitted
Automatically implement Stringable interface
1 parent c1a0a2f commit d95c60a

File tree

4 files changed

+78
-5
lines changed

4 files changed

+78
-5
lines changed
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
@@ -6037,6 +6037,24 @@ static void zend_check_magic_method_attr(uint32_t attr, const char* method, zend
60376037
}
60386038
/* }}} */
60396039

6040+
static void add_stringable_interface(zend_class_entry *ce) {
6041+
for (uint32_t i = 0; i < ce->num_interfaces; i++) {
6042+
if (zend_string_equals_literal(ce->interface_names[i].lc_name, "stringable")) {
6043+
/* Interface already explicitly implemented */
6044+
return;
6045+
}
6046+
}
6047+
6048+
ce->num_interfaces++;
6049+
ce->interface_names =
6050+
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
6051+
// TODO: Add known interned strings instead?
6052+
ce->interface_names[ce->num_interfaces - 1].name =
6053+
zend_string_init("Stringable", sizeof("Stringable") - 1, 0);
6054+
ce->interface_names[ce->num_interfaces - 1].lc_name =
6055+
zend_string_init("stringable", sizeof("stringable") - 1, 0);
6056+
}
6057+
60406058
void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body) /* {{{ */
60416059
{
60426060
zend_class_entry *ce = CG(active_class_entry);
@@ -6118,6 +6136,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
61186136
} else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
61196137
zend_check_magic_method_attr(fn_flags, "__toString", 0);
61206138
ce->__tostring = (zend_function *) op_array;
6139+
add_stringable_interface(ce);
61216140
} else if (zend_string_equals_literal(lcname, ZEND_INVOKE_FUNC_NAME)) {
61226141
zend_check_magic_method_attr(fn_flags, "__invoke", 0);
61236142
} else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
@@ -6640,6 +6659,10 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
66406659

66416660
CG(active_class_entry) = ce;
66426661

6662+
if (implements_ast) {
6663+
zend_compile_implements(implements_ast);
6664+
}
6665+
66436666
zend_compile_stmt(stmt_ast);
66446667

66456668
/* Reset lineno for final opcodes and errors */
@@ -6679,10 +6702,6 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
66796702
}
66806703
}
66816704

6682-
if (implements_ast) {
6683-
zend_compile_implements(implements_ast);
6684-
}
6685-
66866705
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) {
66876706
zend_verify_abstract_class(ce);
66886707
}

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)