From a80360dbed1232e2a6e871fb892670464aab8c56 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 9 Jul 2021 12:13:12 +0200 Subject: [PATCH] Deprecate direct access to static trait members Static trait members may only be accessed through a class in which the trait is used, not directly on the trait. A complication here is that we should not store static methods/properties for which a deprecation is triggered in a cache slot. As the check for this is simple and cheap, I'm handling this in the cache slot population code in the VM. The alternative would be to pass the cache slot down into the fetching code. Part of https://wiki.php.net/rfc/deprecations_php_8_1. --- UPGRADING | 4 ++ .../traits/direct_static_member_access.phpt | 64 +++++++++++++++++++ .../typed_properties_043.phpt | 11 +++- Zend/zend_execute.c | 3 +- Zend/zend_object_handlers.c | 52 +++++++++------ Zend/zend_vm_def.h | 3 +- Zend/zend_vm_execute.h | 36 +++++++---- 7 files changed, 138 insertions(+), 35 deletions(-) create mode 100644 Zend/tests/traits/direct_static_member_access.phpt diff --git a/UPGRADING b/UPGRADING index f86eb17d0a9..b5317605d17 100644 --- a/UPGRADING +++ b/UPGRADING @@ -330,6 +330,10 @@ PHP 8.1 UPGRADE NOTES e.g. a truncation from 1.9 to 1, is deprecated. This affects array keys, int parameter and return types, and operators working on integers. RFC: https://wiki.php.net/rfc/implicit-float-int-deprecate + . Calling a static method or accessing a static property directly on a trait + is deprecated. Static methods and properties should only be accessed on a + class using the trait. + RFC: https://wiki.php.net/rfc/deprecations_php_8_1 . Returning a non-array from __sleep will raise a warning . Returning by reference from a void function is deprecated. RFC: https://wiki.php.net/rfc/deprecations_php_8_1 diff --git a/Zend/tests/traits/direct_static_member_access.phpt b/Zend/tests/traits/direct_static_member_access.phpt new file mode 100644 index 00000000000..3ba866175fc --- /dev/null +++ b/Zend/tests/traits/direct_static_member_access.phpt @@ -0,0 +1,64 @@ +--TEST-- +Direct access to static trait members is deprecated +--FILE-- + +--EXPECTF-- +Deprecated: Accessing static trait property T::$foo is deprecated, it should only be accessed on a class using the trait in %s on line %d + +Deprecated: Accessing static trait property T::$foo is deprecated, it should only be accessed on a class using the trait in %s on line %d +int(42) + +Deprecated: Calling static trait method T::foo is deprecated, it should only be called on a class using the trait in %s on line %d +Foo + +Deprecated: Calling static trait method T::bar is deprecated, it should only be called on a class using the trait in %s on line %d +CallStatic(bar) + + +Deprecated: Accessing static trait property T::$foo is deprecated, it should only be accessed on a class using the trait in %s on line %d + +Deprecated: Accessing static trait property T::$foo is deprecated, it should only be accessed on a class using the trait in %s on line %d +int(42) + +Deprecated: Calling static trait method T::foo is deprecated, it should only be called on a class using the trait in %s on line %d +Foo + +Deprecated: Calling static trait method T::bar is deprecated, it should only be called on a class using the trait in %s on line %d +CallStatic(bar) + +int(42) +Foo +CallStatic(bar) diff --git a/Zend/tests/type_declarations/typed_properties_043.phpt b/Zend/tests/type_declarations/typed_properties_043.phpt index 79f01545e15..ed4951f39c3 100644 --- a/Zend/tests/type_declarations/typed_properties_043.phpt +++ b/Zend/tests/type_declarations/typed_properties_043.phpt @@ -40,10 +40,19 @@ Bar::$parentProp = new Foo; var_dump(Bar::$selfProp, Bar::$selfNullProp, Bar::$parentProp); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Accessing static trait property Test::$selfProp is deprecated, it should only be accessed on a class using the trait in %s on line %d Cannot assign stdClass to property Test::$selfProp of type self + +Deprecated: Accessing static trait property Test::$selfNullProp is deprecated, it should only be accessed on a class using the trait in %s on line %d Cannot assign stdClass to property Test::$selfNullProp of type ?self + +Deprecated: Accessing static trait property Test::$parentProp is deprecated, it should only be accessed on a class using the trait in %s on line %d Cannot assign stdClass to property Test::$parentProp of type parent + +Deprecated: Accessing static trait property Test::$selfNullProp is deprecated, it should only be accessed on a class using the trait in %s on line %d + +Deprecated: Accessing static trait property Test::$selfNullProp is deprecated, it should only be accessed on a class using the trait in %s on line %d NULL object(Bar)#3 (0) { } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e689d479db6..c670c46ae09 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -3024,7 +3024,8 @@ static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval *prop_info = property_info; - if (EXPECTED(op1_type == IS_CONST)) { + if (EXPECTED(op1_type == IS_CONST) + && EXPECTED(!(property_info->ce->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(cache_slot, ce, *retval); CACHE_PTR(cache_slot + sizeof(void *) * 2, property_info); } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 0e4ed78c1df..c348152d6a9 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1307,32 +1307,37 @@ ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_st lc_function_name = zend_string_tolower(function_name); } + zend_function *fbc; zval *func = zend_hash_find(&ce->function_table, lc_function_name); - if (UNEXPECTED(!func)) { - if (UNEXPECTED(!key)) { - zend_string_release_ex(lc_function_name, 0); - } - return get_static_method_fallback(ce, function_name); - } - - zend_function *fbc = Z_FUNC_P(func); - if (!(fbc->op_array.fn_flags & ZEND_ACC_PUBLIC)) { - zend_class_entry *scope = zend_get_executed_scope(); - if (UNEXPECTED(fbc->common.scope != scope)) { - if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) - || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) { - zend_function *fallback_fbc = get_static_method_fallback(ce, function_name); - if (!fallback_fbc) { - zend_bad_method_call(fbc, function_name, scope); + if (EXPECTED(func)) { + fbc = Z_FUNC_P(func); + if (!(fbc->op_array.fn_flags & ZEND_ACC_PUBLIC)) { + zend_class_entry *scope = zend_get_executed_scope(); + if (UNEXPECTED(fbc->common.scope != scope)) { + if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) + || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) { + zend_function *fallback_fbc = get_static_method_fallback(ce, function_name); + if (!fallback_fbc) { + zend_bad_method_call(fbc, function_name, scope); + } + fbc = fallback_fbc; } - fbc = fallback_fbc; } } + } else { + fbc = get_static_method_fallback(ce, function_name); } - if (fbc && UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_ABSTRACT)) { - zend_abstract_method_call(fbc); - fbc = NULL; + if (EXPECTED(fbc)) { + if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_ABSTRACT)) { + zend_abstract_method_call(fbc); + fbc = NULL; + } else if (UNEXPECTED(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT)) { + zend_error(E_DEPRECATED, + "Calling static trait method %s::%s is deprecated, " + "it should only be called on a class using the trait", + ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + } } if (UNEXPECTED(!key)) { @@ -1428,6 +1433,13 @@ undeclared_property: return NULL; } + if (UNEXPECTED(ce->ce_flags & ZEND_ACC_TRAIT)) { + zend_error(E_DEPRECATED, + "Accessing static trait property %s::$%s is deprecated, " + "it should only be accessed on a class using the trait", + ZSTR_VAL(property_info->ce->name), ZSTR_VAL(property_name)); + } + return ret; } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7b65efe5791..43ccf271ea4 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3615,7 +3615,8 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, HANDLE_EXCEPTION(); } if (OP2_TYPE == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 6847f58f3c5..4e04a022eb5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -6812,7 +6812,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C HANDLE_EXCEPTION(); } if (IS_CONST == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -9139,7 +9140,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C HANDLE_EXCEPTION(); } if ((IS_TMP_VAR|IS_VAR) == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -9882,7 +9884,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C HANDLE_EXCEPTION(); } if (IS_UNUSED == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -11489,7 +11492,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C HANDLE_EXCEPTION(); } if (IS_CV == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -23954,7 +23958,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V HANDLE_EXCEPTION(); } if (IS_CONST == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -26494,7 +26499,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V HANDLE_EXCEPTION(); } if ((IS_TMP_VAR|IS_VAR) == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -27788,7 +27794,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V HANDLE_EXCEPTION(); } if (IS_UNUSED == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -30494,7 +30501,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V HANDLE_EXCEPTION(); } if (IS_CV == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -32626,7 +32634,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U HANDLE_EXCEPTION(); } if (IS_CONST == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -34523,7 +34532,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U HANDLE_EXCEPTION(); } if ((IS_TMP_VAR|IS_VAR) == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -34937,7 +34947,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U HANDLE_EXCEPTION(); } if (IS_UNUSED == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { @@ -37016,7 +37027,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U HANDLE_EXCEPTION(); } if (IS_CV == IS_CONST && - EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { + EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && + EXPECTED(!(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) {