[RFC] Asymmetric visibility v2 (GH-15063)

Co-authored-by: Larry Garfield <larry@garfieldtech.com>
This commit is contained in:
Ilija Tovilo 2024-08-27 02:04:48 +02:00 committed by GitHub
parent fef55bc8e4
commit 8df557ac42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 1768 additions and 155 deletions

View file

@ -212,6 +212,18 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */
}
/* }}} */
static const char *zend_asymmetric_visibility_string(uint32_t fn_flags) /* {{{ */
{
if (fn_flags & ZEND_ACC_PRIVATE_SET) {
return "private(set)";
} else if (fn_flags & ZEND_ACC_PROTECTED_SET) {
return "protected(set)";
} else {
ZEND_ASSERT(!(fn_flags & ZEND_ACC_PUBLIC_SET));
return "omitted";
}
}
static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *name) {
ZEND_ASSERT(scope);
if (zend_string_equals_literal_ci(name, "parent") && scope->parent) {
@ -1442,6 +1454,25 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
ZSTR_VAL(ce->name), ZSTR_VAL(key));
}
}
if (UNEXPECTED((child_info->flags & ZEND_ACC_PPP_SET_MASK))
/* Get-only virtual properties have no set visibility, so any child visibility is fine. */
&& !(parent_info->hooks && (parent_info->flags & ZEND_ACC_VIRTUAL) && !parent_info->hooks[ZEND_PROPERTY_HOOK_SET])) {
uint32_t parent_set_visibility = parent_info->flags & ZEND_ACC_PPP_SET_MASK;
/* Adding set protection is fine if it's the same or weaker than
* the parents full property visibility. */
if (!parent_set_visibility) {
parent_set_visibility = zend_visibility_to_set_visibility(parent_info->flags & ZEND_ACC_PPP_MASK);
}
uint32_t child_set_visibility = child_info->flags & ZEND_ACC_PPP_SET_MASK;
if (child_set_visibility > parent_set_visibility) {
zend_error_noreturn(
E_COMPILE_ERROR,
"Set access level of %s::$%s must be %s (as in class %s)%s",
ZSTR_VAL(ce->name), ZSTR_VAL(key),
zend_asymmetric_visibility_string(parent_info->flags), ZSTR_VAL(parent_info->ce->name),
!(parent_info->flags & ZEND_ACC_PPP_SET_MASK) ? "" : " or weaker");
}
}
if (UNEXPECTED((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK))) {
zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ZSTR_VAL(ce->name), ZSTR_VAL(key), zend_visibility_string(parent_info->flags), ZSTR_VAL(parent_info->ce->name), (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");