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.
This commit is contained in:
Nikita Popov 2021-07-09 12:13:12 +02:00
parent cdbe39b16a
commit a80360dbed
7 changed files with 138 additions and 35 deletions

View File

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

View File

@ -0,0 +1,64 @@
--TEST--
Direct access to static trait members is deprecated
--FILE--
<?php
trait T {
public static $foo;
public static function foo() {
echo "Foo\n";
}
public static function __callStatic($name, $args) {
echo "CallStatic($name)\n";
}
}
class C {
use T;
}
function test() {
T::$foo = 42;
var_dump(T::$foo);
T::foo();
T::bar();
echo "\n";
}
// Call twice to test cache slot behavior.
test();
test();
C::$foo = 42;
var_dump(C::$foo);
C::foo();
C::bar();
?>
--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)

View File

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

View File

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

View File

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

View File

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

View File

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