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 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); }
int php_com_do_invoke_by_id(php_com_dotnet_object *obj, DISPID dispid, WORD flags, VARIANT *v, int nargs, zval *args, int silent, int allow_noarg) { DISPID altdispid; DISPPARAMS disp_params; HRESULT hr; VARIANT *vargs = NULL; int i; if (nargs) { vargs = (VARIANT*)safe_emalloc(sizeof(VARIANT), nargs, 0); } /* 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, silent, allow_noarg); /* release variants */ if (vargs) { for (i = 0; i < nargs; i++) { VariantClear(&vargs[i]); } efree(vargs); } /* a bit of a hack this, but it's needed for COM array access. */ if (hr == DISP_E_BADPARAMCOUNT) return hr; return SUCCEEDED(hr) ? SUCCESS : FAILURE; }
/* create an automation SafeArray from a PHP array. * Only creates a single-dimensional array of variants. * The keys of the PHP hash MUST be numeric. If the array * is sparse, then the gaps will be filled with NULL variants */ static void safe_array_from_zval(VARIANT *v, zval *z, int codepage TSRMLS_DC) { SAFEARRAY *sa = NULL; SAFEARRAYBOUND bound; HashPosition pos; int keytype; char *strindex; int strindexlen; long intindex = -1; long max_index = 0; VARIANT *va; zval **item; /* find the largest array index, and assert that all keys are integers */ zend_hash_internal_pointer_reset_ex(HASH_OF(z), &pos); for (;; zend_hash_move_forward_ex(HASH_OF(z), &pos)) { keytype = zend_hash_get_current_key_ex(HASH_OF(z), &strindex, &strindexlen, &intindex, 0, &pos); if (HASH_KEY_IS_STRING == keytype) { goto bogus; } else if (HASH_KEY_NON_EXISTANT == keytype) { break; } if (intindex > max_index) { max_index = intindex; } } /* allocate the structure */ bound.lLbound = 0; bound.cElements = intindex + 1; sa = SafeArrayCreate(VT_VARIANT, 1, &bound); /* get a lock on the array itself */ SafeArrayAccessData(sa, &va); va = (VARIANT*)sa->pvData; /* now fill it in */ zend_hash_internal_pointer_reset_ex(HASH_OF(z), &pos); for (;; zend_hash_move_forward_ex(HASH_OF(z), &pos)) { if (FAILURE == zend_hash_get_current_data_ex(HASH_OF(z), (void**)&item, &pos)) { break; } zend_hash_get_current_key_ex(HASH_OF(z), &strindex, &strindexlen, &intindex, 0, &pos); php_com_variant_from_zval(&va[intindex], *item, codepage TSRMLS_CC); } /* Unlock it and stuff it into our variant */ SafeArrayUnaccessData(sa); V_VT(v) = VT_ARRAY|VT_VARIANT; V_ARRAY(v) = sa; return; bogus: php_error_docref(NULL TSRMLS_CC, E_WARNING, "COM: converting from PHP array to VARIANT array; only arrays with numeric keys are allowed"); V_VT(v) = VT_NULL; if (sa) { SafeArrayUnlock(sa); SafeArrayDestroy(sa); } }
/* 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 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); }
/* create an automation SafeArray from a PHP array. * Only creates a single-dimensional array of variants. * The keys of the PHP hash MUST be numeric. If the array * is sparse, then the gaps will be filled with NULL variants */ static void safe_array_from_zval(VARIANT *v, zval *z, int codepage) { SAFEARRAY *sa = NULL; SAFEARRAYBOUND bound; HashPosition pos; int keytype; zend_string *strindex; zend_ulong intindex = 0; VARIANT *va; zval *item; /* find the largest array index, and assert that all keys are integers */ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z), &pos); for (;; zend_hash_move_forward_ex(Z_ARRVAL_P(z), &pos)) { keytype = zend_hash_get_current_key_ex(Z_ARRVAL_P(z), &strindex, &intindex, &pos); if (HASH_KEY_IS_STRING == keytype) { goto bogus; } else if (HASH_KEY_NON_EXISTENT == keytype) { break; } else if (intindex > UINT_MAX) { php_error_docref(NULL, E_WARNING, "COM: max number %u of elements in safe array exceeded", UINT_MAX); break; } } /* allocate the structure */ bound.lLbound = 0; bound.cElements = zend_hash_num_elements(Z_ARRVAL_P(z)); sa = SafeArrayCreate(VT_VARIANT, 1, &bound); /* get a lock on the array itself */ SafeArrayAccessData(sa, &va); va = (VARIANT*)sa->pvData; /* now fill it in */ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(z), &pos); for (;; zend_hash_move_forward_ex(Z_ARRVAL_P(z), &pos)) { if (NULL == (item = zend_hash_get_current_data_ex(Z_ARRVAL_P(z), &pos))) { break; } zend_hash_get_current_key_ex(Z_ARRVAL_P(z), &strindex, &intindex, &pos); php_com_variant_from_zval(&va[intindex], item, codepage); } /* Unlock it and stuff it into our variant */ SafeArrayUnaccessData(sa); V_VT(v) = VT_ARRAY|VT_VARIANT; V_ARRAY(v) = sa; return; bogus: php_error_docref(NULL, E_WARNING, "COM: converting from PHP array to VARIANT array; only arrays with numeric keys are allowed"); V_VT(v) = VT_NULL; if (sa) { SafeArrayUnlock(sa); SafeArrayDestroy(sa); } }