Fix method calls for PHP objects wrapped in variant

As is, methods of PHP can never be called, because we're first trying
to read the property with the name of the method.

We fix this by first checking for `DISPATCH_METHOD` and treat that as
method call, if the method would be callable.  Only otherwise we try to
access the respective property.

It needs to be noted that this breaks code which accesses a property of
an object, which defines a method of the same name.  However, instances
of such classes should never be wrapped in variants, because this can't
be distinguished by COM anyway.

Closes GH-16945.
This commit is contained in:
Christoph M. Becker 2024-11-26 16:43:25 +01:00
parent fdd3839d80
commit 8b68274319
No known key found for this signature in database
GPG key ID: D66C9593118BCCB6
3 changed files with 51 additions and 7 deletions

3
NEWS
View file

@ -3,7 +3,8 @@ PHP NEWS
?? ??? ????, PHP 8.5.0alpha1
- COM:
. Fix property access of PHP objects wrapped in variant. (cmb)
. Fixed property access of PHP objects wrapped in variant. (cmb)
. Fixed method calls for PHP objects wrapped in variant. (cmb)
- Core:
. Fixed bug GH-16665 (\array and \callable should not be usable in

View file

@ -257,13 +257,10 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex(
/* TODO: if PHP raises an exception here, we should catch it
* and expose it as a COM exception */
if (wFlags & DISPATCH_PROPERTYGET) {
retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), 1, &rv);
ret = S_OK;
} else if (wFlags & DISPATCH_PROPERTYPUT) {
if (wFlags & DISPATCH_PROPERTYPUT) {
zend_update_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), &params[0]);
ret = S_OK;
} else if (wFlags & DISPATCH_METHOD) {
} else if (wFlags & DISPATCH_METHOD && zend_is_callable_ex(name, Z_OBJ(disp->object), 0, NULL, NULL, NULL)) {
zend_try {
retval = &rv;
if (SUCCESS == call_user_function(NULL, &disp->object, name,
@ -289,6 +286,9 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex(
trace("something blew up\n");
ret = DISP_E_EXCEPTION;
} zend_end_try();
} else if (wFlags & DISPATCH_PROPERTYGET) {
retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), 1, &rv);
ret = S_OK;
} else {
trace("Don't know how to handle this invocation %08x\n", wFlags);
}
@ -307,7 +307,9 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex(
VariantInit(pvarRes);
php_com_variant_from_zval(pvarRes, retval, COMG(code_page));
}
// zval_ptr_dtor(retval); // TODO needed for function calls?
if (retval == &rv) {
zval_ptr_dtor(retval);
}
} else if (pvarRes) {
VariantInit(pvarRes);
}

View file

@ -0,0 +1,41 @@
--TEST--
Testing reading properties and calling functions
--EXTENSIONS--
com_dotnet
--FILE--
<?php
class MyClass {
public $foo = "property";
public $bar = "bar";
public function foo() {
return "method";
}
public function stdClass() {
return new stdclass();
}
}
$o = new MyClass();
$v = new variant($o);
var_dump($v->foo);
var_dump($v->foo());
var_dump($v->bar);
var_dump($v->bar());
var_dump($v->stdclass);
var_dump($v->stdclass());
try {
var_dump($v->qux);
} catch (com_exception $ex) {
echo $ex->getMessage(), "\n";
}
?>
--EXPECTF--
string(6) "method"
string(6) "method"
string(3) "bar"
string(3) "bar"
object(variant)#%d (0) {
}
object(variant)#%d (0) {
}
Unable to lookup `qux': %s