Skip to content

Commit 1ad19f2

Browse files
committed
Automatically implement Stringable interface
1 parent 628e1ff commit 1ad19f2

File tree

3 files changed

+76
-4
lines changed

3 files changed

+76
-4
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: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6010,6 +6010,25 @@ 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->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES;
6022+
ce->num_interfaces++;
6023+
ce->interface_names =
6024+
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
6025+
// TODO: Add known interned strings instead?
6026+
ce->interface_names[ce->num_interfaces - 1].name =
6027+
zend_string_init("Stringable", sizeof("Stringable") - 1, 0);
6028+
ce->interface_names[ce->num_interfaces - 1].lc_name =
6029+
zend_string_init("stringable", sizeof("stringable") - 1, 0);
6030+
}
6031+
60136032
void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body) /* {{{ */
60146033
{
60156034
zend_class_entry *ce = CG(active_class_entry);
@@ -6091,6 +6110,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
60916110
} else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
60926111
zend_check_magic_method_attr(fn_flags, "__toString", 0);
60936112
ce->__tostring = (zend_function *) op_array;
6113+
add_stringable_interface(ce);
60946114
} else if (zend_string_equals_literal(lcname, ZEND_INVOKE_FUNC_NAME)) {
60956115
zend_check_magic_method_attr(fn_flags, "__invoke", 0);
60966116
} else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
@@ -6616,6 +6636,10 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
66166636

66176637
CG(active_class_entry) = ce;
66186638

6639+
if (implements_ast) {
6640+
zend_compile_implements(implements_ast);
6641+
}
6642+
66196643
zend_compile_stmt(stmt_ast);
66206644

66216645
/* Reset lineno for final opcodes and errors */
@@ -6655,10 +6679,6 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
66556679
}
66566680
}
66576681

6658-
if (implements_ast) {
6659-
zend_compile_implements(implements_ast);
6660-
}
6661-
66626682
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) {
66636683
zend_verify_abstract_class(ce);
66646684
}

0 commit comments

Comments
 (0)