static void com_write_dimension(zval *object, zval *offset, zval *value) { php_com_dotnet_object *obj; zval args[2]; VARIANT v; HRESULT res; obj = CDNO_FETCH(object); if (V_VT(&obj->v) == VT_DISPATCH) { ZVAL_COPY_VALUE(&args[0], offset); ZVAL_COPY_VALUE(&args[1], value); VariantInit(&v); if (SUCCESS == php_com_do_invoke_by_id(obj, DISPID_VALUE, DISPATCH_METHOD|DISPATCH_PROPERTYPUT, &v, 2, args, 0, 0)) { VariantClear(&v); } } else if (V_ISARRAY(&obj->v)) { LONG indices = 0; VARTYPE vt; if (SafeArrayGetDim(V_ARRAY(&obj->v)) == 1) { if (FAILED(SafeArrayGetVartype(V_ARRAY(&obj->v), &vt)) || vt == VT_EMPTY) { vt = V_VT(&obj->v) & ~VT_ARRAY; } convert_to_long(offset); indices = (LONG)Z_LVAL_P(offset); VariantInit(&v); php_com_variant_from_zval(&v, value, obj->code_page); if (V_VT(&v) != vt) { VariantChangeType(&v, &v, 0, vt); } if (vt == VT_VARIANT) { res = SafeArrayPutElement(V_ARRAY(&obj->v), &indices, &v); } else { res = SafeArrayPutElement(V_ARRAY(&obj->v), &indices, &v.lVal); } VariantClear(&v); if (FAILED(res)) { php_com_throw_exception(res, NULL); } } else { php_com_throw_exception(DISP_E_BADINDEX, "this variant has multiple dimensions; you can't set a new value without specifying *all* dimensions"); } } else { php_com_throw_exception(E_INVALIDARG, "this variant is not an array type"); } }
static zval *com_property_read(zval *object, zval *member, int type TSRMLS_DC) { zval *return_value; php_com_dotnet_object *obj; VARIANT v; HRESULT res; MAKE_STD_ZVAL(return_value); ZVAL_NULL(return_value); Z_SET_REFCOUNT_P(return_value, 0); Z_UNSET_ISREF_P(return_value); obj = CDNO_FETCH(object); if (V_VT(&obj->v) == VT_DISPATCH) { VariantInit(&v); convert_to_string_ex(&member); res = php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL, 1 TSRMLS_CC); if (res == SUCCESS) { php_com_zval_from_variant(return_value, &v, obj->code_page TSRMLS_CC); VariantClear(&v); } else if (res == DISP_E_BADPARAMCOUNT) { php_com_saproxy_create(object, return_value, member TSRMLS_CC); } } else { php_com_throw_exception(E_INVALIDARG, "this variant has no properties" TSRMLS_CC); } return return_value; }
static zval *com_read_dimension(zval *object, zval *offset, int type, zval *rv) { php_com_dotnet_object *obj; VARIANT v; ZVAL_NULL(rv); obj = CDNO_FETCH(object); if (V_VT(&obj->v) == VT_DISPATCH) { VariantInit(&v); if (SUCCESS == php_com_do_invoke_by_id(obj, DISPID_VALUE, DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 1, offset, 0, 0)) { php_com_zval_from_variant(rv, &v, obj->code_page); VariantClear(&v); } } else if (V_ISARRAY(&obj->v)) { convert_to_long(offset); if (SafeArrayGetDim(V_ARRAY(&obj->v)) == 1) { if (php_com_safearray_get_elem(&obj->v, &v, (LONG)Z_LVAL_P(offset))) { php_com_wrap_variant(rv, &v, obj->code_page); VariantClear(&v); } } else { php_com_saproxy_create(object, rv, offset); } } else { php_com_throw_exception(E_INVALIDARG, "this variant is not an array type"); } return rv; }
static zval *com_property_read(zval *object, zval *member, int type, void **cahce_slot, zval *rv) { php_com_dotnet_object *obj; VARIANT v; HRESULT res; ZVAL_NULL(rv); obj = CDNO_FETCH(object); if (V_VT(&obj->v) == VT_DISPATCH) { VariantInit(&v); convert_to_string_ex(member); res = php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, 0, NULL, 1); if (res == SUCCESS) { php_com_zval_from_variant(rv, &v, obj->code_page); VariantClear(&v); } else if (res == DISP_E_BADPARAMCOUNT) { php_com_saproxy_create(object, rv, member); } } else { php_com_throw_exception(E_INVALIDARG, "this variant has no properties"); } return rv; }
static zval *saproxy_property_read(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv) { ZVAL_NULL(rv); php_com_throw_exception(E_INVALIDARG, "safearray has no properties"); return rv; }
static zval *saproxy_property_read(zval *object, zval *member, int type TSRMLS_DC) { zval *return_value; MAKE_STD_ZVAL(return_value); ZVAL_NULL(return_value); php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC); return return_value; }
static void variant_unary_operation(enum variant_unary_opcode op, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ { VARIANT vres; VARIANT left_val; VARIANT *vleft = NULL; zval *zleft = NULL; php_com_dotnet_object *obj; HRESULT result; int codepage = CP_ACP; VariantInit(&left_val); VariantInit(&vres); if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &zleft, php_com_variant_class_entry)) { obj = CDNO_FETCH(zleft); vleft = &obj->v; } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z!", &zleft)) { vleft = &left_val; php_com_variant_from_zval(vleft, zleft, codepage); } else { return; } switch (op) { case VOP_ABS: result = VarAbs(vleft, &vres); break; case VOP_FIX: result = VarFix(vleft, &vres); break; case VOP_INT: result = VarInt(vleft, &vres); break; case VOP_NEG: result = VarNeg(vleft, &vres); break; case VOP_NOT: result = VarNot(vleft, &vres); break; default: result = E_INVALIDARG; } if (SUCCEEDED(result)) { php_com_wrap_variant(return_value, &vres, codepage); } else { php_com_throw_exception(result, NULL); } VariantClear(&vres); VariantClear(&left_val); }
/* this is a convenience function for fetching a particular * element from a (possibly multi-dimensional) safe array */ PHP_COM_DOTNET_API int php_com_safearray_get_elem(VARIANT *array, VARIANT *dest, LONG dim1) { UINT dims; LONG lbound, ubound; LONG indices[1]; VARTYPE vt; if (!V_ISARRAY(array)) { return 0; } dims = SafeArrayGetDim(V_ARRAY(array)); if (dims != 1) { php_error_docref(NULL, E_WARNING, "Can only handle single dimension variant arrays (this array has %d)", dims); return 0; } if (FAILED(SafeArrayGetVartype(V_ARRAY(array), &vt)) || vt == VT_EMPTY) { vt = V_VT(array) & ~VT_ARRAY; } /* determine the bounds */ SafeArrayGetLBound(V_ARRAY(array), 1, &lbound); SafeArrayGetUBound(V_ARRAY(array), 1, &ubound); /* check bounds */ if (dim1 < lbound || dim1 > ubound) { php_com_throw_exception(DISP_E_BADINDEX, "index out of bounds"); return 0; } /* now fetch that element */ VariantInit(dest); indices[0] = dim1; if (vt == VT_VARIANT) { SafeArrayGetElement(V_ARRAY(array), indices, dest); } else { V_VT(dest) = vt; /* store the value into "lVal" member of the variant. * This works because it is a union; since we know the variant * type, we end up with a working variant */ SafeArrayGetElement(V_ARRAY(array), indices, &dest->lVal); } return 1; }
static void com_property_write(zval *object, zval *member, zval *value, void **cache_slot) { php_com_dotnet_object *obj; VARIANT v; obj = CDNO_FETCH(object); if (V_VT(&obj->v) == VT_DISPATCH) { VariantInit(&v); convert_to_string_ex(member); if (SUCCESS == php_com_do_invoke(obj, Z_STRVAL_P(member), Z_STRLEN_P(member), DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF, &v, 1, value, 0)) { VariantClear(&v); } } else { php_com_throw_exception(E_INVALIDARG, "this variant has no properties"); } }
int php_com_do_invoke(php_com_dotnet_object *obj, char *name, size_t namelen, WORD flags, VARIANT *v, int nargs, zval *args, int allow_noarg) { DISPID dispid; HRESULT hr; char *winerr = NULL; char *msg = NULL; hr = php_com_get_id_of_name(obj, name, namelen, &dispid); if (FAILED(hr)) { winerr = php_win32_error_to_msg(hr); spprintf(&msg, 0, "Unable to lookup `%s': %s", name, winerr); LocalFree(winerr); php_com_throw_exception(hr, msg); efree(msg); return FAILURE; } return php_com_do_invoke_by_id(obj, dispid, flags, v, nargs, args, 0, allow_noarg); }
/* Performs an Invoke on the given com object. * returns a failure code and creates an exception if there was an error */ HRESULT php_com_invoke_helper(php_com_dotnet_object *obj, DISPID id_member, WORD flags, DISPPARAMS *disp_params, VARIANT *v, int silent, int allow_noarg TSRMLS_DC) { HRESULT hr; unsigned int arg_err; EXCEPINFO e = {0}; hr = IDispatch_Invoke(V_DISPATCH(&obj->v), id_member, &IID_NULL, LOCALE_SYSTEM_DEFAULT, flags, disp_params, v, &e, &arg_err); if (silent == 0 && FAILED(hr)) { char *source = NULL, *desc = NULL, *msg = NULL; int source_len, desc_len; switch (hr) { case DISP_E_EXCEPTION: if (e.bstrSource) { source = php_com_olestring_to_string(e.bstrSource, &source_len, obj->code_page TSRMLS_CC); SysFreeString(e.bstrSource); } if (e.bstrDescription) { desc = php_com_olestring_to_string(e.bstrDescription, &desc_len, obj->code_page TSRMLS_CC); SysFreeString(e.bstrDescription); } if (PG(html_errors)) { spprintf(&msg, 0, "<b>Source:</b> %s<br/><b>Description:</b> %s", source ? source : "Unknown", desc ? desc : "Unknown"); } else { spprintf(&msg, 0, "Source: %s\nDescription: %s", source ? source : "Unknown", desc ? desc : "Unknown"); } if (desc) { efree(desc); } if (source) { efree(source); } if (e.bstrHelpFile) { SysFreeString(e.bstrHelpFile); } break; case DISP_E_PARAMNOTFOUND: case DISP_E_TYPEMISMATCH: desc = php_win_err(hr); spprintf(&msg, 0, "Parameter %d: %s", arg_err, desc); LocalFree(desc); break; case DISP_E_BADPARAMCOUNT: if ((disp_params->cArgs + disp_params->cNamedArgs == 0) && (allow_noarg == 1)) { /* if getting a property and they are missing all parameters, * we want to create a proxy object for them; so lets not create an * exception here */ msg = NULL; break; } /* else fall through */ default: desc = php_win_err(hr); spprintf(&msg, 0, "Error [0x%08x] %s", hr, desc); LocalFree(desc); break; } if (msg) { php_com_throw_exception(hr, msg TSRMLS_CC); efree(msg); } } return hr; }
/* the core of COM */ int php_com_do_invoke_byref(php_com_dotnet_object *obj, zend_internal_function *f, WORD flags, VARIANT *v, int nargs, zval *args) { DISPID dispid, altdispid; DISPPARAMS disp_params; HRESULT hr; VARIANT *vargs = NULL, *byref_vals = NULL; int i, byref_count = 0, j; /* assumption: that the active function (f) is the function we generated for the engine */ if (!f) { return FAILURE; } hr = php_com_get_id_of_name(obj, f->function_name->val, f->function_name->len, &dispid); if (FAILED(hr)) { char *winerr = NULL; char *msg = NULL; winerr = php_win32_error_to_msg(hr); spprintf(&msg, 0, "Unable to lookup `%s': %s", f->function_name->val, winerr); LocalFree(winerr); php_com_throw_exception(hr, msg); efree(msg); return FAILURE; } if (nargs) { vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0); } if (f->arg_info) { for (i = 0; i < nargs; i++) { if (f->arg_info[nargs - i - 1].pass_by_reference) { byref_count++; } } } if (byref_count) { byref_vals = (VARIANT*)safe_emalloc(sizeof(VARIANT), byref_count, 0); for (j = 0, i = 0; i < nargs; i++) { if (f->arg_info[nargs - i - 1].pass_by_reference) { /* put the value into byref_vals instead */ php_com_variant_from_zval(&byref_vals[j], &args[nargs - i - 1], obj->code_page); /* if it is already byref, "move" it into the vargs array, otherwise * make vargs a reference to this value */ if (V_VT(&byref_vals[j]) & VT_BYREF) { memcpy(&vargs[i], &byref_vals[j], sizeof(vargs[i])); VariantInit(&byref_vals[j]); /* leave the variant slot empty to simplify cleanup */ } else { VariantInit(&vargs[i]); V_VT(&vargs[i]) = V_VT(&byref_vals[j]) | VT_BYREF; /* union magic ensures that this works out */ vargs[i].byref = &V_UINT(&byref_vals[j]); } j++; } else { php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page); } } } else { /* Invoke'd args are in reverse order */ for (i = 0; i < nargs; i++) { php_com_variant_from_zval(&vargs[i], &args[nargs - i - 1], obj->code_page); } } disp_params.cArgs = nargs; disp_params.cNamedArgs = 0; disp_params.rgvarg = vargs; disp_params.rgdispidNamedArgs = NULL; if (flags & DISPATCH_PROPERTYPUT) { altdispid = DISPID_PROPERTYPUT; disp_params.rgdispidNamedArgs = &altdispid; disp_params.cNamedArgs = 1; } /* this will create an exception if needed */ hr = php_com_invoke_helper(obj, dispid, flags, &disp_params, v, 0, 0); /* release variants */ if (vargs) { if (f && f->arg_info) { for (i = 0, j = 0; i < nargs; i++) { /* if this was byref, update the zval */ if (f->arg_info[nargs - i - 1].pass_by_reference) { SEPARATE_ZVAL_IF_NOT_REF(&args[nargs - i - 1]); /* if the variant is pointing at the byref_vals, we need to map * the pointee value as a zval; otherwise, the value is pointing * into an existing PHP variant record */ if (V_VT(&vargs[i]) & VT_BYREF) { if (vargs[i].byref == &V_UINT(&byref_vals[j])) { /* copy that value */ php_com_zval_from_variant(&args[nargs - i - 1], &byref_vals[j], obj->code_page); } } else { /* not sure if this can ever happen; the variant we marked as BYREF * is no longer BYREF - copy its value */ php_com_zval_from_variant(&args[nargs - i - 1], &vargs[i], obj->code_page); } VariantClear(&byref_vals[j]); j++; } VariantClear(&vargs[i]); } } else { for (i = 0, j = 0; i < nargs; i++) { VariantClear(&vargs[i]); } } efree(vargs); } return SUCCEEDED(hr) ? SUCCESS : FAILURE; }
static zval *saproxy_read_dimension(zend_object *object, zval *offset, int type, zval *rv) { php_com_saproxy *proxy = (php_com_saproxy*) object; UINT dims, i; SAFEARRAY *sa; LONG ubound, lbound; HRESULT res; ZVAL_NULL(rv); if (V_VT(&proxy->obj->v) == VT_DISPATCH) { VARIANT v; zval *args; /* prop-get using first dimension as the property name, * all subsequent dimensions and the offset as parameters */ args = safe_emalloc(proxy->dimensions + 1, sizeof(zval), 0); for (i = 1; i < (UINT) proxy->dimensions; i++) { args[i-1] = proxy->indices[i]; } ZVAL_COPY_VALUE(&args[i-1], offset); convert_to_string(&proxy->indices[0]); VariantInit(&v); res = php_com_do_invoke(proxy->obj, Z_STRVAL(proxy->indices[0]), Z_STRLEN(proxy->indices[0]), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, proxy->dimensions, args, 0); if (res == SUCCESS) { php_com_zval_from_variant(rv, &v, proxy->obj->code_page); VariantClear(&v); } else if (res == DISP_E_BADPARAMCOUNT) { /* return another proxy */ php_com_saproxy_create(object, rv, offset); } return rv; } else if (!V_ISARRAY(&proxy->obj->v)) { php_com_throw_exception(E_INVALIDARG, "invalid read from com proxy object"); return rv; } /* the SafeArray case */ /* offset/index must be an integer */ convert_to_long(offset); sa = V_ARRAY(&proxy->obj->v); dims = SafeArrayGetDim(sa); if ((UINT) proxy->dimensions >= dims) { /* too many dimensions */ php_com_throw_exception(E_INVALIDARG, "too many dimensions!"); return rv; } /* bounds check */ SafeArrayGetLBound(sa, proxy->dimensions, &lbound); SafeArrayGetUBound(sa, proxy->dimensions, &ubound); if (Z_LVAL_P(offset) < lbound || Z_LVAL_P(offset) > ubound) { php_com_throw_exception(DISP_E_BADINDEX, "index out of bounds"); return rv; } if (dims - 1 == proxy->dimensions) { LONG *indices; VARTYPE vt; VARIANT v; VariantInit(&v); /* we can return a real value */ indices = safe_emalloc(dims, sizeof(LONG), 0); /* copy indices from proxy */ for (i = 0; i < dims; i++) { convert_to_long(&proxy->indices[i]); indices[i] = (LONG)Z_LVAL(proxy->indices[i]); } /* add user-supplied index */ indices[dims-1] = (LONG)Z_LVAL_P(offset); /* now fetch the value */ if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) { vt = V_VT(&proxy->obj->v) & ~VT_ARRAY; } if (vt == VT_VARIANT) { res = SafeArrayGetElement(sa, indices, &v); } else { V_VT(&v) = vt; res = SafeArrayGetElement(sa, indices, &v.lVal); } efree(indices); if (SUCCEEDED(res)) { php_com_wrap_variant(rv, &v, proxy->obj->code_page); } else { php_com_throw_exception(res, NULL); } VariantClear(&v); } else { /* return another proxy */ php_com_saproxy_create(object, rv, offset); } return rv; }
static zval *saproxy_property_write(zend_object *object, zend_string *member, zval *value, void **cache_slot) { php_com_throw_exception(E_INVALIDARG, "safearray has no properties"); return value; }
static void saproxy_write_dimension(zend_object *object, zval *offset, zval *value) { php_com_saproxy *proxy = (php_com_saproxy*) object; UINT dims, i; HRESULT res; VARIANT v; if (V_VT(&proxy->obj->v) == VT_DISPATCH) { /* We do a prop-set using the first dimension as the property name, * all subsequent dimensions and offset as parameters, with value as * the final value */ zval *args = safe_emalloc(proxy->dimensions + 2, sizeof(zval), 0); for (i = 1; i < (UINT) proxy->dimensions; i++) { ZVAL_COPY_VALUE(&args[i-1], &proxy->indices[i]); } ZVAL_COPY_VALUE(&args[i-1], offset); ZVAL_COPY_VALUE(&args[i], value); convert_to_string(&proxy->indices[0]); VariantInit(&v); if (SUCCESS == php_com_do_invoke(proxy->obj, Z_STRVAL(proxy->indices[0]), Z_STRLEN(proxy->indices[0]), DISPATCH_PROPERTYPUT, &v, proxy->dimensions + 1, args, 0)) { VariantClear(&v); } efree(args); } else if (V_ISARRAY(&proxy->obj->v)) { LONG *indices; VARTYPE vt; dims = SafeArrayGetDim(V_ARRAY(&proxy->obj->v)); indices = safe_emalloc(dims, sizeof(LONG), 0); /* copy indices from proxy */ for (i = 0; i < dims; i++) { convert_to_long(&proxy->indices[i]); indices[i] = (LONG)Z_LVAL(proxy->indices[i]); } /* add user-supplied index */ convert_to_long(offset); indices[dims-1] = (LONG)Z_LVAL_P(offset); if (FAILED(SafeArrayGetVartype(V_ARRAY(&proxy->obj->v), &vt)) || vt == VT_EMPTY) { vt = V_VT(&proxy->obj->v) & ~VT_ARRAY; } VariantInit(&v); php_com_variant_from_zval(&v, value, proxy->obj->code_page); if (V_VT(&v) != vt) { VariantChangeType(&v, &v, 0, vt); } if (vt == VT_VARIANT) { res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v); } else { res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v.lVal); } efree(indices); VariantClear(&v); if (FAILED(res)) { php_com_throw_exception(res, NULL); } } else { php_com_throw_exception(E_NOTIMPL, "invalid write to com proxy object"); } }
static void variant_binary_operation(enum variant_binary_opcode op, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ { VARIANT vres; VARIANT left_val, right_val; VARIANT *vleft = NULL, *vright = NULL; zval *zleft = NULL, *zright = NULL; php_com_dotnet_object *obj; HRESULT result; int codepage = CP_ACP; VariantInit(&left_val); VariantInit(&right_val); VariantInit(&vres); if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OO", &zleft, php_com_variant_class_entry, &zright, php_com_variant_class_entry)) { obj = CDNO_FETCH(zleft); vleft = &obj->v; obj = CDNO_FETCH(zright); vright = &obj->v; } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "Oz!", &zleft, php_com_variant_class_entry, &zright)) { obj = CDNO_FETCH(zleft); vleft = &obj->v; vright = &right_val; php_com_variant_from_zval(vright, zright, codepage); } else if (SUCCESS == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "z!O", &zleft, &zright, php_com_variant_class_entry)) { obj = CDNO_FETCH(zright); vright = &obj->v; vleft = &left_val; php_com_variant_from_zval(vleft, zleft, codepage); } else if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z!z!", &zleft, &zright)) { vleft = &left_val; php_com_variant_from_zval(vleft, zleft, codepage); vright = &right_val; php_com_variant_from_zval(vright, zright, codepage); } else { return; } switch (op) { case VOP_ADD: result = VarAdd(vleft, vright, &vres); break; case VOP_CAT: result = VarCat(vleft, vright, &vres); break; case VOP_SUB: result = VarSub(vleft, vright, &vres); break; case VOP_MUL: result = VarMul(vleft, vright, &vres); break; case VOP_AND: result = VarAnd(vleft, vright, &vres); break; case VOP_DIV: result = VarDiv(vleft, vright, &vres); break; case VOP_EQV: result = VarEqv(vleft, vright, &vres); break; case VOP_IDIV: result = VarIdiv(vleft, vright, &vres); break; case VOP_IMP: result = VarImp(vleft, vright, &vres); break; case VOP_MOD: result = VarMod(vleft, vright, &vres); break; case VOP_OR: result = VarOr(vleft, vright, &vres); break; case VOP_POW: result = VarPow(vleft, vright, &vres); break; case VOP_XOR: result = VarXor(vleft, vright, &vres); break; /*Let say it fails as no valid op has been given */ default: result = E_INVALIDARG; } if (SUCCEEDED(result)) { php_com_wrap_variant(return_value, &vres, codepage); } else { php_com_throw_exception(result, NULL); } VariantClear(&vres); VariantClear(&left_val); VariantClear(&right_val); }