Inconsistency between FETCH_DIM_IS and FETCH_OBJ_IS

php.internals

Sara Golemon

20 years ago
When executing ZEND_FETCH_OBJ_IS on a non-object, the handler dispatches to zend_fetch_property_address_read_helper with type==BP_VAR_IS and ends up generating an error (isset() is supposed to be quiet) here: if (Z_TYPE_P(container) != IS_OBJECT || !Z_OBJ_HT_P(container)->read_property) { zend_error(E_NOTICE, "Trying to get property of non-object"); *retval = EG(uninitialized_zval_ptr); SELECTIVE_PZVAL_LOCK(*retval, &opline->result); AI_USE_PTR(EX_T(opline->result.u.var).var); It's cousin ZEND_FETCH_DIM_IS winds up at zend_fetch_dimension_address() where the non-arrayness of op1 is handled via these case statements: case IS_NULL: { /* for read-mode only */ if (result) { result->var.ptr_ptr = &EG(uninitialized_zval_ptr); PZVAL_LOCK(*result->var.ptr_ptr); } if (type==BP_VAR_W || type==BP_VAR_RW) { zend_error(E_WARNING, "Cannot use a NULL value as an array"); } break; } default: { switch (type) { case BP_VAR_UNSET: zend_error(E_WARNING, "Cannot unset offset in a non-array variable"); /* break missing intentionally */ case BP_VAR_R: case BP_VAR_IS: retval = &EG(uninitialized_zval_ptr); break; default: retval = &EG(error_zval_ptr); break; } if (result) { result->var.ptr_ptr = retval; PZVAL_LOCK(*result->var.ptr_ptr); } if (type==BP_VAR_W || type==BP_VAR_RW) { zend_error(E_WARNING, "Cannot use a scalar value as an array"); } } Which indicate the expected behavior of silent failure. It's probably gone undetected till now as it requires fetching a property or dimension offset from a property in a third (non-object) variable within an isset() statement: $foo = NULL; isset($foo->bar->baz); Unless someone can suggest why Objects should be noisy inside isset(), I'd like to suggest the following fix: Index: Zend/zend_vm_def.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_def.h,v retrieving revision 1.120 diff -u -r1.120 zend_vm_def.h --- Zend/zend_vm_def.h 13 Jun 2006 12:56:20 -0000 1.120 +++ Zend/zend_vm_def.h 5 Jul 2006 20:52:41 -0000 @@ -1176,7 +1176,9 @@ if (Z_TYPE_P(container) != IS_OBJECT || !Z_OBJ_HT_P(container)->read_property) { - zend_error(E_NOTICE, "Trying to get property of non-object"); + if (type != BP_VAR_IS) { + zend_error(E_NOTICE, "Trying to get property of non-object"); + } *retval = EG(uninitialized_zval_ptr); SELECTIVE_PZVAL_LOCK(*retval, &opline->result); AI_USE_PTR(EX_T(opline->result.u.var).var); I suggest only excluding BP_VAR_IS since the matching behavior for unset()/DIM is to throw a notice. I could potentially see excluding BP_VAR_R in order to match read/DIM, but personally I never liked that behavior :) -Sara

Marcus Börger

20 years ago
Hello Sara, nice catch, fix looks pretty correct to me best regards marcus Wednesday, July 5, 2006, 10:59:35 PM, you wrote:
> When executing ZEND_FETCH_OBJ_IS on a non-object, the handler dispatches > to zend_fetch_property_address_read_helper with type==BP_VAR_IS and ends > up generating an error (isset() is supposed to be quiet) here:
> if (Z_TYPE_P(container) != IS_OBJECT || > !Z_OBJ_HT_P(container)->read_property) { > zend_error(E_NOTICE, "Trying to get property of non-object"); > *retval = EG(uninitialized_zval_ptr); > SELECTIVE_PZVAL_LOCK(*retval, &opline->result); > AI_USE_PTR(EX_T(opline->result.u.var).var);
> It's cousin ZEND_FETCH_DIM_IS winds up at zend_fetch_dimension_address() > where the non-arrayness of op1 is handled via these case statements: > case IS_NULL: { > /* for read-mode only */ > if (result) { > result->var.ptr_ptr = &EG(uninitialized_zval_ptr); > PZVAL_LOCK(*result->var.ptr_ptr); > } > if (type==BP_VAR_W || type==BP_VAR_RW) { > zend_error(E_WARNING, "Cannot use a NULL value as an array"); > } > break; > } > default: { > switch (type) { > case BP_VAR_UNSET: > zend_error(E_WARNING, "Cannot unset offset in a non-array variable"); > /* break missing intentionally */ > case BP_VAR_R: > case BP_VAR_IS: > retval = &EG(uninitialized_zval_ptr); > break; > default: > retval = &EG(error_zval_ptr); > break; > } > if (result) { > result->var.ptr_ptr = retval; > PZVAL_LOCK(*result->var.ptr_ptr); > } > if (type==BP_VAR_W || type==BP_VAR_RW) { > zend_error(E_WARNING, "Cannot use a scalar value as an array"); > } > }
> Which indicate the expected behavior of silent failure. It's probably > gone undetected till now as it requires fetching a property or dimension > offset from a property in a third (non-object) variable within an isset() statement:
> $foo = NULL; > isset($foo->bar->baz);
> Unless someone can suggest why Objects should be noisy inside isset(), > I'd like to suggest the following fix:
> Index: Zend/zend_vm_def.h > =================================================================== > RCS file: /repository/ZendEngine2/zend_vm_def.h,v > retrieving revision 1.120 > diff -u -r1.120 zend_vm_def.h > --- Zend/zend_vm_def.h 13 Jun 2006 12:56:20 -0000 1.120 > +++ Zend/zend_vm_def.h 5 Jul 2006 20:52:41 -0000 > @@ -1176,7 +1176,9 @@
> if (Z_TYPE_P(container) != IS_OBJECT || > !Z_OBJ_HT_P(container)->read_property) { > - zend_error(E_NOTICE, "Trying to get property of non-object"); > + if (type != BP_VAR_IS) { > + zend_error(E_NOTICE, "Trying to get property of non-object"); > + } > *retval = EG(uninitialized_zval_ptr); > SELECTIVE_PZVAL_LOCK(*retval, &opline->result); > AI_USE_PTR(EX_T(opline->result.u.var).var);
> I suggest only excluding BP_VAR_IS since the matching behavior for > unset()/DIM is to throw a notice. I could potentially see excluding > BP_VAR_R in order to match read/DIM, but personally I never liked that behavior :)
> -Sara
Best regards, Marcus

Andi Gutmans

20 years ago
Looks good to me. Thanks!