Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
2 changes: 1 addition & 1 deletion Zend/tests/magic_methods/magic_methods_021.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Foo2 {
}

class Foo3 {
public static function __set_state(array $data): Foo3|self {}
public static function __set_state(array $data): Foo3|Foo2 {}
}

?>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
parent type can take part in an intersection type is resolvable at compile time
--FILE--
<?php

class A {}

class B extends A {
public function foo(): parent&Iterator {}
}

?>
DONE
--EXPECT--
DONE
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
--TEST--
parent type cannot take part in an intersection type
parent type cannot take part in an intersection type if not resolvable at compile time
--FILE--
<?php

class A {}

class B extends A {
trait T {
public function foo(): parent&Iterator {}
}

?>
DONE
--EXPECTF--
Fatal error: Type parent cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
self type can take part in an intersection type is resolvable at compile time
--FILE--
<?php

class A {
public function foo(): self&Iterator {}
}

?>
DONE
--EXPECT--
DONE
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
--TEST--
self type cannot take part in an intersection type
self type cannot take part in an intersection type if not resolvable at compile time
--FILE--
<?php

class A {
trait T {
public function foo(): self&Iterator {}
}

?>
DONE
--EXPECTF--
Fatal error: Type self cannot be part of an intersection type in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Duplicate parent type
--FILE--
<?php

class Foo {
public function method(array $data) {}
}
class Bar extends Foo {
public function method(array $data): parent|parent {}
}

?>
--EXPECTF--
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Duplicate self type
--FILE--
<?php

class Foo {
public function method(array $data): self|self {}
}

?>
--EXPECTF--
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Duplicate static type
--FILE--
<?php

class Foo {
public function method(array $data): static|static {}
}

?>
--EXPECTF--
Fatal error: Duplicate type static is redundant in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Relative class type self resolving to an existing entry (after variation)
--FILE--
<?php

class Foo {
public function method(array $data): Foo|self {}
}

?>
DONE
--EXPECTF--
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Relative class type self resolving to an existing entry (before variation)
--FILE--
<?php

class Foo {
public function method(array $data): self|Foo {}
}

?>
DONE
--EXPECTF--
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Relative class type parent resolving to an existing entry (after variation)
--FILE--
<?php

class Foo {
public function method(array $data) {}
}
class Bar extends Foo {
public function method(array $data): Foo|parent {}
}

?>
DONE
--EXPECTF--
Fatal error: Duplicate type Foo is redundant in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Relative class type parent resolving to an existing entry (before variation)
--FILE--
<?php

class Foo {
public function method(array $data) {}
}
class Bar extends Foo {
public function method(array $data): parent|Foo {}
}

?>
DONE
--EXPECTF--
Fatal error: Duplicate type Foo is redundant in %s on line %d
51 changes: 36 additions & 15 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1409,10 +1409,8 @@ static zend_string *add_intersection_type(zend_string *str,
ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) {
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type));
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*single_type));
zend_string *name = ZEND_TYPE_NAME(*single_type);
zend_string *resolved = resolve_class_name(name, scope);
intersection_str = add_type_string(intersection_str, resolved, /* is_intersection */ true);
zend_string_release(resolved);

intersection_str = add_type_string(intersection_str, ZEND_TYPE_NAME(*single_type), /* is_intersection */ true);
} ZEND_TYPE_LIST_FOREACH_END();

ZEND_ASSERT(intersection_str);
Expand Down Expand Up @@ -1444,6 +1442,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop
}
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*list_type));

zend_string *name = ZEND_TYPE_NAME(*list_type);
zend_string *resolved = resolve_class_name(name, scope);
str = add_type_string(str, resolved, /* is_intersection */ false);
Expand Down Expand Up @@ -6957,14 +6956,14 @@ static zend_type zend_compile_single_typename(zend_ast *ast)

return (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0);
} else {
zend_string *class_name = zend_ast_get_str(ast);
uint8_t type_code = zend_lookup_builtin_type_by_name(class_name);
zend_string *type_name = zend_ast_get_str(ast);
uint8_t type_code = zend_lookup_builtin_type_by_name(type_name);

if (type_code != 0) {
if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) {
zend_error_noreturn(E_COMPILE_ERROR,
"Type declaration '%s' must be unqualified",
ZSTR_VAL(zend_string_tolower(class_name)));
ZSTR_VAL(zend_string_tolower(type_name)));
}

/* Transform iterable into a type union alias */
Expand All @@ -6978,38 +6977,55 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
return (zend_type) ZEND_TYPE_INIT_CODE(type_code, 0, 0);
} else {
const char *correct_name;
zend_string *orig_name = zend_ast_get_str(ast);
uint32_t fetch_type = zend_get_class_fetch_type_ast(ast);
zend_string *class_name = type_name;

if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
class_name = zend_resolve_class_name_ast(ast);
zend_assert_valid_class_name(class_name, "a type name");
} else {
ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_SELF || fetch_type == ZEND_FETCH_CLASS_PARENT);

zend_ensure_valid_class_fetch_type(fetch_type);
if (fetch_type == ZEND_FETCH_CLASS_SELF) {
/* Scope might be unknown for unbound closures and traits */
if (zend_is_scope_known()) {
class_name = CG(active_class_entry)->name;
ZEND_ASSERT(class_name && "must know class name when resolving self type at compile time");
}
} else {
ZEND_ASSERT(fetch_type == ZEND_FETCH_CLASS_PARENT);
/* Scope might be unknown for unbound closures and traits */
if (zend_is_scope_known()) {
class_name = CG(active_class_entry)->parent_name;
ZEND_ASSERT(class_name && "must know class name when resolving parent type at compile time");
}
}
zend_string_addref(class_name);
}

if (ast->attr == ZEND_NAME_NOT_FQ
&& zend_is_confusable_type(orig_name, &correct_name)
&& zend_is_not_imported(orig_name)) {
&& zend_is_confusable_type(type_name, &correct_name)
&& zend_is_not_imported(type_name)) {
const char *extra =
FC(current_namespace) ? " or import the class with \"use\"" : "";
if (correct_name) {
zend_error(E_COMPILE_WARNING,
"\"%s\" will be interpreted as a class name. Did you mean \"%s\"? "
"Write \"\\%s\"%s to suppress this warning",
ZSTR_VAL(orig_name), correct_name, ZSTR_VAL(class_name), extra);
ZSTR_VAL(type_name), correct_name, ZSTR_VAL(class_name), extra);
} else {
zend_error(E_COMPILE_WARNING,
"\"%s\" is not a supported builtin type "
"and will be interpreted as a class name. "
"Write \"\\%s\"%s to suppress this warning",
ZSTR_VAL(orig_name), ZSTR_VAL(class_name), extra);
ZSTR_VAL(type_name), ZSTR_VAL(class_name), extra);
}
}

class_name = zend_new_interned_string(class_name);
zend_alloc_ce_cache(class_name);
return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 0, 0);
return (zend_type) ZEND_TYPE_INIT_CLASS(class_name, /* allow null */ false, 0);
}
}
}
Expand Down Expand Up @@ -7132,6 +7148,7 @@ static zend_type zend_compile_typename_ex(
/* Switch from single name to name list. */
type_list->num_types = 1;
type_list->types[0] = type;
/* Clear MAY_BE_* type flags */
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
}
/* Mark type as list type */
Expand Down Expand Up @@ -7178,6 +7195,7 @@ static zend_type zend_compile_typename_ex(
"Type contains both true and false, bool must be used instead");
}
ZEND_TYPE_FULL_MASK(type) |= ZEND_TYPE_PURE_MASK(single_type);
/* Clear MAY_BE_* type flags */
ZEND_TYPE_FULL_MASK(single_type) &= ~_ZEND_TYPE_MAY_BE_MASK;

if (ZEND_TYPE_IS_COMPLEX(single_type)) {
Expand All @@ -7190,6 +7208,7 @@ static zend_type zend_compile_typename_ex(
/* Switch from single name to name list. */
type_list->num_types = 1;
type_list->types[0] = type;
/* Clear MAY_BE_* type flags */
ZEND_TYPE_FULL_MASK(type_list->types[0]) &= ~_ZEND_TYPE_MAY_BE_MASK;
ZEND_TYPE_SET_LIST(type, type_list);
}
Expand Down Expand Up @@ -7253,8 +7272,10 @@ static zend_type zend_compile_typename_ex(
zend_string_release_ex(standard_type_str, false);
}
/* Check for "self" and "parent" too */
if (zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "self")
|| zend_string_equals_literal_ci(ZEND_TYPE_NAME(single_type), "parent")) {
if (
zend_string_equals(ZEND_TYPE_NAME(single_type), ZSTR_KNOWN(ZEND_STR_SELF))
|| zend_string_equals(ZEND_TYPE_NAME(single_type), ZSTR_KNOWN(ZEND_STR_PARENT))
) {
zend_error_noreturn(E_COMPILE_ERROR,
"Type %s cannot be part of an intersection type", ZSTR_VAL(ZEND_TYPE_NAME(single_type)));
}
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,8 @@ EMPTY_SWITCH_DEFAULT_CASE()
_(ZEND_STR_NULL_LOWERCASE, "null") \
_(ZEND_STR_MIXED, "mixed") \
_(ZEND_STR_TRAVERSABLE, "Traversable") \
_(ZEND_STR_SELF, "self") \
_(ZEND_STR_PARENT, "parent") \
_(ZEND_STR_SLEEP, "__sleep") \
_(ZEND_STR_WAKEUP, "__wakeup") \
_(ZEND_STR_CASES, "cases") \
Expand Down
8 changes: 4 additions & 4 deletions ext/reflection/tests/types/ReflectionType_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ string(10) "SplSubject"
bool(true)
bool(false)
bool(false)
string(4) "self"
string(1) "c"
** Method 2 - parameter 0
bool(true)
bool(false)
bool(false)
string(6) "parent"
string(8) "stdClass"
** Method 3 - parameter 0
bool(true)
bool(false)
Expand All @@ -195,12 +195,12 @@ string(3) "int"
bool(true)
bool(false)
bool(false)
string(4) "self"
string(1) "c"
** Function/method return type 4
bool(true)
bool(false)
bool(false)
string(6) "parent"
string(8) "stdClass"
** Function/method return type 5
bool(true)
bool(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ foreach ((new ReflectionClass(C::class))->getMethods() as $method) {
--EXPECT--
C::a()
$method->getReturnType() returns ReflectionNamedType
$method->getReturnType()->__toString() returns self
$method->getReturnType()->__toString() returns C

C::b()
$method->getReturnType() returns ReflectionUnionType
$method->getReturnType()->__toString() returns stdClass|self
$method->getReturnType()->__toString() returns stdClass|C
$method->getReturnType()->getTypes() returns an array with 2 element(s)
type(s) in union: ReflectionNamedType(stdClass), ReflectionNamedType(self)
type(s) in union: ReflectionNamedType(stdClass), ReflectionNamedType(C)

C::c()
$method->getReturnType() returns ReflectionNamedType
Expand Down
Loading