Skip to content

Commit f4891e2

Browse files
committed
Automatically implement Stringable interface
1 parent e460c07 commit f4891e2

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
@@ -6010,6 +6010,24 @@ static void zend_check_magic_method_attr(uint32_t attr, const char* method, zend
60106010
}
60116011
/* }}} */
60126012

6013+
static void add_stringable_interface(zend_class_entry *ce) {
6014+
for (uint32_t i = 0; i < ce->num_interfaces; i++) {
6015+
if (zend_string_equals_literal(ce->interface_names[i].lc_name, "stringable")) {
6016+
/* Interface already explicitly implemented */
6017+
return;
6018+
}
6019+
}
6020+
6021+
ce->num_interfaces++;
6022+
ce->interface_names =
6023+
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
6024+
// TODO: Add known interned strings instead?
6025+
ce->interface_names[ce->num_interfaces - 1].name =
6026+
zend_string_init("Stringable", sizeof("Stringable") - 1, 0);
6027+
ce->interface_names[ce->num_interfaces - 1].lc_name =
6028+
zend_string_init("stringable", sizeof("stringable") - 1, 0);
6029+
}
6030+
60136031
void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body) /* {{{ */
60146032
{
60156033
zend_class_entry *ce = CG(active_class_entry);
@@ -6091,6 +6109,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
60916109
} else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
60926110
zend_check_magic_method_attr(fn_flags, "__toString", 0);
60936111
ce->__tostring = (zend_function *) op_array;
6112+
add_stringable_interface(ce);
60946113
} else if (zend_string_equals_literal(lcname, ZEND_INVOKE_FUNC_NAME)) {
60956114
zend_check_magic_method_attr(fn_flags, "__invoke", 0);
60966115
} else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
@@ -6613,6 +6632,10 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
66136632

66146633
CG(active_class_entry) = ce;
66156634

6635+
if (implements_ast) {
6636+
zend_compile_implements(implements_ast);
6637+
}
6638+
66166639
zend_compile_stmt(stmt_ast);
66176640

66186641
/* Reset lineno for final opcodes and errors */
@@ -6652,10 +6675,6 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
66526675
}
66536676
}
66546677

6655-
if (implements_ast) {
6656-
zend_compile_implements(implements_ast);
6657-
}
6658-
66596678
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) {
66606679
zend_verify_abstract_class(ce);
66616680
}

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)