Skip to content

Commit

Permalink
Deprecate direct access to static trait members
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikic committed Jul 9, 2021
1 parent cdbe39b commit a80360d
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 35 deletions.
4 changes: 4 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 64 additions & 0 deletions Zend/tests/traits/direct_static_member_access.phpt
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 10 additions & 1 deletion Zend/tests/type_declarations/typed_properties_043.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
Expand Down
3 changes: 2 additions & 1 deletion Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
52 changes: 32 additions & 20 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -1428,6 +1433,13 @@ ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend
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;
}
/* }}} */
Expand Down
3 changes: 2 additions & 1 deletion Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down
36 changes: 24 additions & 12 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down Expand Up @@ -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))) {
Expand Down

0 comments on commit a80360d

Please sign in to comment.