mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Implement asymmetric visibility for static properties
https://wiki.php.net/rfc/static-aviz Optimally, this would be moved to zend_fetch_static_property_address(). However, this isn't currently effective for opcache, because R and RW/W/UNSET cache slots are merged. This will circumvent the visibility check if the cache is primed by a R instruction. Closes GH-16486
This commit is contained in:
parent
cb3bca249a
commit
1f6fdde646
5 changed files with 224 additions and 7 deletions
|
@ -122,6 +122,8 @@ PHP 8.5 UPGRADE NOTES
|
|||
it can be used to suppress warnings emitted by #[\NoDiscard] and possibly
|
||||
also diagnostics emitted by external IDEs or static analysis tools.
|
||||
RFC: https://wiki.php.net/rfc/marking_return_value_as_important
|
||||
. Added asymmetric visibility support for static properties.
|
||||
RFC: https://wiki.php.net/rfc/static-aviz
|
||||
|
||||
- Curl:
|
||||
. Added support for share handles that are persisted across multiple PHP
|
||||
|
|
|
@ -5,8 +5,145 @@ Asymmetric visibility on static props
|
|||
|
||||
class C {
|
||||
public private(set) static int $prop;
|
||||
public private(set) static array $prop2;
|
||||
public private(set) static stdClass $prop3;
|
||||
public private(set) static object $unset;
|
||||
|
||||
public static function reset() {
|
||||
self::$prop = 1;
|
||||
self::$prop2 = [];
|
||||
self::$prop3 = new stdClass();
|
||||
}
|
||||
|
||||
public static function setProp($prop) {
|
||||
self::$prop = $prop;
|
||||
}
|
||||
|
||||
public static function addProp2($prop2) {
|
||||
self::$prop2[] = $prop2;
|
||||
}
|
||||
}
|
||||
|
||||
function test() {
|
||||
C::reset();
|
||||
|
||||
try {
|
||||
C::$prop = 2;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump(C::$prop);
|
||||
|
||||
C::setProp(3);
|
||||
var_dump(C::$prop);
|
||||
|
||||
try {
|
||||
++C::$prop;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump(C::$prop);
|
||||
|
||||
try {
|
||||
C::$prop++;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump(C::$prop);
|
||||
|
||||
try {
|
||||
C::$prop += str_repeat('a', 10);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump(C::$prop);
|
||||
|
||||
try {
|
||||
$ref = &C::$prop;
|
||||
$ref++;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump(C::$prop);
|
||||
|
||||
try {
|
||||
$ref = 4;
|
||||
C::$prop = &$ref;
|
||||
$ref++;
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump(C::$prop);
|
||||
|
||||
try {
|
||||
C::$prop2[] = 'foo';
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
var_dump(C::$prop2);
|
||||
|
||||
C::addProp2('bar');
|
||||
var_dump(C::$prop2);
|
||||
|
||||
C::$prop3->foo = 'foo';
|
||||
var_dump(C::$prop3);
|
||||
|
||||
unset(C::$unset->foo);
|
||||
}
|
||||
|
||||
test();
|
||||
echo "\nRepeat:\n";
|
||||
test();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Static property may not have asymmetric visibility in %s on line %d
|
||||
Cannot modify private(set) property C::$prop from global scope
|
||||
int(1)
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop2 from global scope
|
||||
array(0) {
|
||||
}
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
object(stdClass)#%d (1) {
|
||||
["foo"]=>
|
||||
string(3) "foo"
|
||||
}
|
||||
|
||||
Repeat:
|
||||
Cannot modify private(set) property C::$prop from global scope
|
||||
int(1)
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop from global scope
|
||||
int(3)
|
||||
Cannot indirectly modify private(set) property C::$prop2 from global scope
|
||||
array(0) {
|
||||
}
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(3) "bar"
|
||||
}
|
||||
object(stdClass)#%d (1) {
|
||||
["foo"]=>
|
||||
string(3) "foo"
|
||||
}
|
||||
|
|
|
@ -8694,10 +8694,6 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
|
|||
zend_error_noreturn(E_COMPILE_ERROR, "Property cannot be both final and private");
|
||||
}
|
||||
|
||||
if ((flags & ZEND_ACC_STATIC) && (flags & ZEND_ACC_PPP_SET_MASK)) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Static property may not have asymmetric visibility");
|
||||
}
|
||||
|
||||
if (ce->ce_flags & ZEND_ACC_INTERFACE) {
|
||||
if (flags & ZEND_ACC_FINAL) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Property in interface cannot be final");
|
||||
|
|
|
@ -1116,6 +1116,14 @@ ZEND_VM_HANDLER(29, ZEND_ASSIGN_STATIC_PROP_OP, ANY, ANY, OP)
|
|||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
UNDEF_RESULT();
|
||||
FREE_OP_DATA();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
do {
|
||||
|
@ -1430,6 +1438,13 @@ ZEND_VM_HANDLER(38, ZEND_PRE_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT)
|
|||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
UNDEF_RESULT();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
zend_pre_incdec_property_zval(prop,
|
||||
ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC);
|
||||
|
||||
|
@ -1457,6 +1472,13 @@ ZEND_VM_HANDLER(40, ZEND_POST_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT)
|
|||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
UNDEF_RESULT();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
zend_post_incdec_property_zval(prop,
|
||||
ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC);
|
||||
|
||||
|
@ -1836,18 +1858,29 @@ ZEND_VM_INLINE_HELPER(zend_fetch_static_prop_helper, ANY, ANY, int type)
|
|||
{
|
||||
USE_OPLINE
|
||||
zval *prop;
|
||||
zend_property_info *prop_info;
|
||||
|
||||
SAVE_OPLINE();
|
||||
|
||||
prop = zend_fetch_static_property_address(
|
||||
NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type,
|
||||
&prop_info, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type,
|
||||
type == BP_VAR_W ? opline->extended_value : 0 OPLINE_CC EXECUTE_DATA_CC);
|
||||
if (UNEXPECTED(!prop)) {
|
||||
ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS));
|
||||
prop = &EG(uninitialized_zval);
|
||||
} else if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
if (Z_TYPE_P(prop) == IS_OBJECT) {
|
||||
ZEND_VM_C_GOTO(copy_deref);
|
||||
} else if (type != BP_VAR_UNSET || Z_TYPE_P(prop) != IS_UNDEF) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
}
|
||||
prop = &EG(uninitialized_zval);
|
||||
}
|
||||
|
||||
if (type == BP_VAR_R || type == BP_VAR_IS) {
|
||||
ZEND_VM_C_LABEL(copy_deref):
|
||||
ZVAL_COPY_DEREF(EX_VAR(opline->result.var), prop);
|
||||
} else {
|
||||
ZVAL_INDIRECT(EX_VAR(opline->result.var), prop);
|
||||
|
@ -2893,6 +2926,14 @@ ZEND_VM_HANDLER(33, ZEND_ASSIGN_STATIC_PROP_REF, ANY, ANY, CACHE_SLOT|SRC)
|
|||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
FREE_OP_DATA();
|
||||
UNDEF_RESULT();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
value_ptr = GET_OP_DATA_ZVAL_PTR_PTR(BP_VAR_W);
|
||||
|
||||
if (OP_DATA_TYPE == IS_VAR && (opline->extended_value & ZEND_RETURNS_FUNCTION) && UNEXPECTED(!Z_ISREF_P(value_ptr))) {
|
||||
|
|
43
Zend/zend_vm_execute.h
generated
43
Zend/zend_vm_execute.h
generated
|
@ -783,6 +783,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_OP_SPEC_HAN
|
|||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
UNDEF_RESULT();
|
||||
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1);
|
||||
|
||||
do {
|
||||
|
@ -826,6 +834,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_STATIC_PROP_SPEC_HANDL
|
|||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
UNDEF_RESULT();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
zend_pre_incdec_property_zval(prop,
|
||||
ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC);
|
||||
|
||||
|
@ -847,6 +862,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_STATIC_PROP_SPEC_HAND
|
|||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
UNDEF_RESULT();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
zend_post_incdec_property_zval(prop,
|
||||
ZEND_TYPE_IS_SET(prop_info->type) ? prop_info : NULL OPLINE_CC EXECUTE_DATA_CC);
|
||||
|
||||
|
@ -858,18 +880,29 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET zend_fetch_static_prop_helper_
|
|||
{
|
||||
USE_OPLINE
|
||||
zval *prop;
|
||||
zend_property_info *prop_info;
|
||||
|
||||
SAVE_OPLINE();
|
||||
|
||||
prop = zend_fetch_static_property_address(
|
||||
NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type,
|
||||
&prop_info, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type,
|
||||
type == BP_VAR_W ? opline->extended_value : 0 OPLINE_CC EXECUTE_DATA_CC);
|
||||
if (UNEXPECTED(!prop)) {
|
||||
ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS));
|
||||
prop = &EG(uninitialized_zval);
|
||||
} else if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
if (Z_TYPE_P(prop) == IS_OBJECT) {
|
||||
goto copy_deref;
|
||||
} else if (type != BP_VAR_UNSET || Z_TYPE_P(prop) != IS_UNDEF) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
}
|
||||
prop = &EG(uninitialized_zval);
|
||||
}
|
||||
|
||||
if (type == BP_VAR_R || type == BP_VAR_IS) {
|
||||
copy_deref:
|
||||
ZVAL_COPY_DEREF(EX_VAR(opline->result.var), prop);
|
||||
} else {
|
||||
ZVAL_INDIRECT(EX_VAR(opline->result.var), prop);
|
||||
|
@ -1105,6 +1138,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_REF_SPEC_HA
|
|||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(prop_info->flags & ZEND_ACC_PPP_SET_MASK)
|
||||
&& UNEXPECTED(!zend_asymmetric_property_has_set_access(prop_info))) {
|
||||
zend_asymmetric_visibility_property_modification_error(prop_info, "indirectly modify");
|
||||
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
|
||||
UNDEF_RESULT();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
value_ptr = get_zval_ptr_ptr((opline+1)->op1_type, (opline+1)->op1, BP_VAR_W);
|
||||
|
||||
if ((opline+1)->op1_type == IS_VAR && (opline->extended_value & ZEND_RETURNS_FUNCTION) && UNEXPECTED(!Z_ISREF_P(value_ptr))) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue