Automatically implement Stringable interface

This commit is contained in:
Nikita Popov 2020-02-06 10:27:30 +01:00 committed by Nicolas Grekas
parent 9e775db025
commit 336eb48c36
5 changed files with 80 additions and 5 deletions

View File

@ -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

View File

@ -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"
}

View File

@ -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===

View File

@ -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);
@ -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)) {
@ -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 */
@ -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);
}

View File

@ -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--