Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Automatically implement Stringable interface
  • Loading branch information
nikic authored and nicolas-grekas committed Mar 2, 2020
commit 336eb48c36f3c1c115349307c18e6cf16ab003df
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ PHP 8.0 UPGRADE NOTES
. Some consistency fixes to variable syntax have been applied, for example
writing `Foo::BAR::$baz` is now allowed.
RFC: https://wiki.php.net/rfc/variable_syntax_tweaks
. Added Stringable.
RFC: https://wiki.php.net/rfc/stringable

- Date:
. Added DateTime::createFromInterface() and
Expand Down
35 changes: 35 additions & 0 deletions Zend/tests/stringable_automatic_implementation.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
Stringable is automatically implemented
--FILE--
<?php

class Test {
public function __toString() {
return "foo";
}
}

var_dump(new Test instanceof Stringable);
var_dump((new ReflectionClass(Test::class))->getInterfaceNames());

class Test2 extends Test {
public function __toString() {
return "bar";
}
}

var_dump(new Test2 instanceof Stringable);
var_dump((new ReflectionClass(Test2::class))->getInterfaceNames());

?>
--EXPECT--
bool(true)
array(1) {
[0]=>
string(10) "Stringable"
}
bool(true)
array(1) {
[0]=>
string(10) "Stringable"
}
17 changes: 17 additions & 0 deletions Zend/tests/type_declarations/variance/stringable.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Automatic Stringable implementation participates in variance
--FILE--
<?php

class Foo {
public function test(): Stringable {}
}
class Bar extends Foo {
public function test(): Bar {}
public function __toString() {}
}

?>
===DONE===
--EXPECT--
===DONE===
27 changes: 23 additions & 4 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -6066,6 +6066,24 @@ static void zend_check_magic_method_attr(uint32_t attr, const char* method, zend
}
/* }}} */

static void add_stringable_interface(zend_class_entry *ce) {
for (uint32_t i = 0; i < ce->num_interfaces; i++) {
if (zend_string_equals_literal(ce->interface_names[i].lc_name, "stringable")) {
/* Interface already explicitly implemented */
return;
}
}

ce->num_interfaces++;
ce->interface_names =
erealloc(ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces);
// TODO: Add known interned strings instead?
ce->interface_names[ce->num_interfaces - 1].name =
zend_string_init("Stringable", sizeof("Stringable") - 1, 0);
ce->interface_names[ce->num_interfaces - 1].lc_name =
zend_string_init("stringable", sizeof("stringable") - 1, 0);
}

void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_bool has_body) /* {{{ */
{
zend_class_entry *ce = CG(active_class_entry);
Expand Down Expand Up @@ -6147,6 +6165,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo
} else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
zend_check_magic_method_attr(fn_flags, "__toString", 0);
ce->__tostring = (zend_function *) op_array;
add_stringable_interface(ce);
} else if (zend_string_equals_literal(lcname, ZEND_INVOKE_FUNC_NAME)) {
zend_check_magic_method_attr(fn_flags, "__invoke", 0);
} else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
Expand Down Expand Up @@ -6680,6 +6699,10 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */

CG(active_class_entry) = ce;

if (implements_ast) {
zend_compile_implements(implements_ast);
}

zend_compile_stmt(stmt_ast);

/* Reset lineno for final opcodes and errors */
Expand Down Expand Up @@ -6719,10 +6742,6 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
}
}

if (implements_ast) {
zend_compile_implements(implements_ast);
}

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) {
zend_verify_abstract_class(ce);
}
Expand Down
4 changes: 3 additions & 1 deletion ext/spl/tests/class_implements_variation1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ Error: 2 - class_implements(): Class does not exist and could not be loaded, %s
bool(false)

--instance of classWithToString--
array(0) {
array(1) {
["Stringable"]=>
string(10) "Stringable"
}

--instance of classWithoutToString--
Expand Down