Skip to content

Commit cc039c9

Browse files
committed
Add basic support for extending interfaces with associated type
1 parent e9c28b1 commit cc039c9

4 files changed

+95
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Associated type behaviour in extended interface
3+
--FILE--
4+
<?php
5+
6+
interface I {
7+
type T : int|string;
8+
public function foo(T $param): T;
9+
}
10+
11+
interface I2 extends I {
12+
type T;
13+
public function bar(int $o, T $param): T;
14+
}
15+
16+
class C implements I2 {
17+
public function foo(string $param): string {}
18+
public function bar(int $o, float $param): float {}
19+
}
20+
21+
?>
22+
--EXPECTF--
23+
Fatal error: Cannot redeclare associated type T in interface I2 inherited from interface I in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Associated type behaviour in extended interface
3+
--FILE--
4+
<?php
5+
6+
interface I {
7+
type T : int|string|(Traversable&Countable);
8+
public function foo(T $param): T;
9+
}
10+
11+
interface I2 extends I {
12+
public function bar(int $o, T $param): T;
13+
}
14+
15+
class C implements I2 {
16+
public function foo(string $param): string {}
17+
public function bar(int $o, float $param): float {}
18+
}
19+
20+
?>
21+
--EXPECTF--
22+
Fatal error: Declaration of C::bar(int $o, float $param): float must be compatible with I2::bar(int $o, T $param): T in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Associated type behaviour in extended interface
3+
--FILE--
4+
<?php
5+
6+
interface I {
7+
type T : int|string|(Traversable&Countable);
8+
public function foo(T $param): T;
9+
}
10+
11+
interface I2 extends I {
12+
type T2 : float|bool|stdClass;
13+
public function bar(T2 $o, T $param): T2;
14+
}
15+
16+
class C implements I2 {
17+
public function foo(string $param): string {}
18+
public function bar(float $o, string $param): float {}
19+
}
20+
21+
// TODO: Ideally error message would be:
22+
//Fatal error: Declaration of C::bar(float $o, string $param): float must be compatible with I2::bar(T2<stdClass|float|bool> $o, T<(Traversable&Countable)|int|string> $param): T2<stdClass|float|bool> in %s on line %d
23+
//Improve zend_append_type_hint()?
24+
?>
25+
--EXPECTF--
26+
Fatal error: Declaration of C::bar(float $o, string $param): float must be compatible with I2::bar(T2<stdClass|float|bool> $o, T $param): T2<stdClass|float|bool> in %s on line %d

Diff for: Zend/zend_inheritance.c

+24-1
Original file line numberDiff line numberDiff line change
@@ -2232,8 +2232,31 @@ static void do_interface_implementation(zend_class_entry *ce, zend_class_entry *
22322232
}
22332233

22342234
if (iface->associated_types) {
2235+
const uint32_t num_associated_types = zend_hash_num_elements(iface->associated_types);
2236+
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
2237+
const bool persistent = ce->type == ZEND_INTERNAL_CLASS;
2238+
if (ce->associated_types) {
2239+
zend_string *associated_type_name;
2240+
zend_type *associated_type_ptr;
2241+
ZEND_HASH_FOREACH_STR_KEY_PTR(iface->associated_types, associated_type_name, associated_type_ptr) {
2242+
if (zend_hash_exists(ce->associated_types, associated_type_name)) {
2243+
zend_error_noreturn(E_ERROR,
2244+
"Cannot redeclare associated type %s in interface %s inherited from interface %s",
2245+
ZSTR_VAL(associated_type_name), ZSTR_VAL(ce->name), ZSTR_VAL(iface->name));
2246+
}
2247+
/* Deep copy the type information */
2248+
zend_type_copy_ctor(associated_type_ptr, /* use_arena */ !persistent, /* persistent */ persistent);
2249+
zend_hash_add_new_mem(ce->associated_types, associated_type_name, associated_type_ptr, sizeof(*associated_type_ptr));
2250+
} ZEND_HASH_FOREACH_END();
2251+
} else {
2252+
ce->associated_types = pemalloc(sizeof(HashTable), persistent);
2253+
zend_hash_init(ce->associated_types, num_associated_types, NULL, NULL, false);
2254+
zend_hash_copy(ce->associated_types, iface->associated_types, NULL);
2255+
}
2256+
return;
2257+
}
22352258
HashTable *ht = emalloc(sizeof(HashTable));
2236-
zend_hash_init(ht, zend_hash_num_elements(iface->associated_types), NULL, NULL, false);
2259+
zend_hash_init(ht, num_associated_types, NULL, NULL, false);
22372260
CG(bound_associated_types) = ht;
22382261
}
22392262
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&iface->constants_table, key, c) {

0 commit comments

Comments
 (0)