diff options
Diffstat (limited to 'ext/com_dotnet/com_saproxy.c')
-rw-r--r-- | ext/com_dotnet/com_saproxy.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/ext/com_dotnet/com_saproxy.c b/ext/com_dotnet/com_saproxy.c new file mode 100644 index 0000000..ad92849 --- /dev/null +++ b/ext/com_dotnet/com_saproxy.c @@ -0,0 +1,586 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Wez Furlong <wez@thebrainroom.com> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* This module implements a SafeArray proxy which is used internally + * by the engine when resolving multi-dimensional array accesses on + * SafeArray types. + * In addition, the proxy is now able to handle properties of COM objects + * that smell like PHP arrays. + * */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_com_dotnet.h" +#include "php_com_dotnet_internal.h" +#include "Zend/zend_exceptions.h" + +typedef struct { + /* the object we a proxying for; we hold a refcount to it */ + zval *zobj; + php_com_dotnet_object *obj; + + /* how many dimensions we are indirecting to get into this element */ + LONG dimensions; + + /* this is an array whose size_is(dimensions) */ + zval **indices; + +} php_com_saproxy; + +typedef struct { + zend_object_iterator iter; + zval *proxy_obj; + php_com_saproxy *proxy; + LONG key; + LONG imin, imax; + LONG *indices; +} php_com_saproxy_iter; + +#define SA_FETCH(zv) (php_com_saproxy*)zend_object_store_get_object(zv TSRMLS_CC) + +static inline void clone_indices(php_com_saproxy *dest, php_com_saproxy *src, int ndims) +{ + int i; + + for (i = 0; i < ndims; i++) { + MAKE_STD_ZVAL(dest->indices[i]); + *dest->indices[i] = *src->indices[i]; + zval_copy_ctor(dest->indices[i]); + } +} + +static zval *saproxy_property_read(zval *object, zval *member, int type, const zend_literal *key 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 saproxy_property_write(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) +{ + php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC); +} + +static zval *saproxy_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) +{ + php_com_saproxy *proxy = SA_FETCH(object); + zval *return_value; + UINT dims, i; + SAFEARRAY *sa; + LONG ubound, lbound; + HRESULT res; + + MAKE_STD_ZVAL(return_value); + ZVAL_NULL(return_value); + + 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]; + } + args[i-1] = offset; + + convert_to_string(proxy->indices[0]); + VariantInit(&v); + + res = php_com_do_invoke(proxy->obj, Z_STRVAL_P(proxy->indices[0]), + Z_STRLEN_P(proxy->indices[0]), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v, + proxy->dimensions, args, 0 TSRMLS_CC); + + if (res == SUCCESS) { + php_com_zval_from_variant(return_value, &v, proxy->obj->code_page TSRMLS_CC); + VariantClear(&v); + } else if (res == DISP_E_BADPARAMCOUNT) { + /* return another proxy */ + php_com_saproxy_create(object, return_value, offset TSRMLS_CC); + } + + return return_value; + + } else if (!V_ISARRAY(&proxy->obj->v)) { + php_com_throw_exception(E_INVALIDARG, "invalid read from com proxy object" TSRMLS_CC); + return return_value; + } + + /* 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!" TSRMLS_CC); + return return_value; + } + + /* 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" TSRMLS_CC); + return return_value; + } + + 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] = Z_LVAL_P(proxy->indices[i]); + } + + /* add user-supplied index */ + indices[dims-1] = 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(return_value, &v, proxy->obj->code_page TSRMLS_CC); + } else { + php_com_throw_exception(res, NULL TSRMLS_CC); + } + + VariantClear(&v); + + } else { + /* return another proxy */ + php_com_saproxy_create(object, return_value, offset TSRMLS_CC); + } + + return return_value; +} + +static void saproxy_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) +{ + php_com_saproxy *proxy = SA_FETCH(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++) { + args[i-1] = proxy->indices[i]; + } + args[i-1] = offset; + args[i] = value; + + convert_to_string(proxy->indices[0]); + VariantInit(&v); + if (SUCCESS == php_com_do_invoke(proxy->obj, Z_STRVAL_P(proxy->indices[0]), + Z_STRLEN_P(proxy->indices[0]), DISPATCH_PROPERTYPUT, &v, proxy->dimensions + 1, + args, 0 TSRMLS_CC)) { + 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] = Z_LVAL_P(proxy->indices[i]); + } + + /* add user-supplied index */ + convert_to_long(offset); + indices[dims-1] = 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 TSRMLS_CC); + + 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 TSRMLS_CC); + } + } else { + php_com_throw_exception(E_NOTIMPL, "invalid write to com proxy object" TSRMLS_CC); + } +} + +#if 0 +static void saproxy_object_set(zval **property, zval *value TSRMLS_DC) +{ +} + +static zval *saproxy_object_get(zval *property TSRMLS_DC) +{ + /* Not yet implemented in the engine */ + return NULL; +} +#endif + +static int saproxy_property_exists(zval *object, zval *member, int check_empty, const zend_literal *key TSRMLS_DC) +{ + /* no properties */ + return 0; +} + +static int saproxy_dimension_exists(zval *object, zval *member, int check_empty TSRMLS_DC) +{ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Operation not yet supported on a COM object"); + return 0; +} + +static void saproxy_property_delete(zval *object, zval *member, const zend_literal *key TSRMLS_DC) +{ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object"); +} + +static void saproxy_dimension_delete(zval *object, zval *offset TSRMLS_DC) +{ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object"); +} + +static HashTable *saproxy_properties_get(zval *object TSRMLS_DC) +{ + /* no properties */ + return NULL; +} + +static union _zend_function *saproxy_method_get(zval **object, const char *name, int len, const zend_literal *key TSRMLS_DC) +{ + /* no methods */ + return NULL; +} + +static int saproxy_call_method(const char *method, INTERNAL_FUNCTION_PARAMETERS) +{ + return FAILURE; +} + +static union _zend_function *saproxy_constructor_get(zval *object TSRMLS_DC) +{ + /* user cannot instantiate */ + return NULL; +} + +static zend_class_entry *saproxy_class_entry_get(const zval *object TSRMLS_DC) +{ + return php_com_saproxy_class_entry; +} + +static int saproxy_class_name_get(const zval *object, const char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC) +{ + *class_name = estrndup(php_com_saproxy_class_entry->name, php_com_saproxy_class_entry->name_length); + *class_name_len = php_com_saproxy_class_entry->name_length; + return 0; +} + +static int saproxy_objects_compare(zval *object1, zval *object2 TSRMLS_DC) +{ + return -1; +} + +static int saproxy_object_cast(zval *readobj, zval *writeobj, int type TSRMLS_DC) +{ + return FAILURE; +} + +static int saproxy_count_elements(zval *object, long *count TSRMLS_DC) +{ + php_com_saproxy *proxy = SA_FETCH(object); + LONG ubound, lbound; + + if (!V_ISARRAY(&proxy->obj->v)) { + return FAILURE; + } + + SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &lbound); + SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &ubound); + + *count = ubound - lbound + 1; + + return SUCCESS; +} + +zend_object_handlers php_com_saproxy_handlers = { + ZEND_OBJECTS_STORE_HANDLERS, + saproxy_property_read, + saproxy_property_write, + saproxy_read_dimension, + saproxy_write_dimension, + NULL, + NULL, /* saproxy_object_get, */ + NULL, /* saproxy_object_set, */ + saproxy_property_exists, + saproxy_property_delete, + saproxy_dimension_exists, + saproxy_dimension_delete, + saproxy_properties_get, + saproxy_method_get, + saproxy_call_method, + saproxy_constructor_get, + saproxy_class_entry_get, + saproxy_class_name_get, + saproxy_objects_compare, + saproxy_object_cast, + saproxy_count_elements +}; + +static void saproxy_free_storage(void *object TSRMLS_DC) +{ + php_com_saproxy *proxy = (php_com_saproxy *)object; + int i; + + for (i = 0; i < proxy->dimensions; i++) { + if (proxy->indices) { + FREE_ZVAL(proxy->indices[i]); + } + } + + zval_ptr_dtor(&proxy->zobj); + efree(proxy->indices); + efree(proxy); +} + +static void saproxy_clone(void *object, void **clone_ptr TSRMLS_DC) +{ + php_com_saproxy *proxy = (php_com_saproxy *)object; + php_com_saproxy *cloneproxy; + + cloneproxy = emalloc(sizeof(*cloneproxy)); + memcpy(cloneproxy, proxy, sizeof(*cloneproxy)); + + Z_ADDREF_P(cloneproxy->zobj); + cloneproxy->indices = safe_emalloc(cloneproxy->dimensions, sizeof(zval *), 0); + clone_indices(cloneproxy, proxy, proxy->dimensions); + + *clone_ptr = cloneproxy; +} + +int php_com_saproxy_create(zval *com_object, zval *proxy_out, zval *index TSRMLS_DC) +{ + php_com_saproxy *proxy, *rel = NULL; + + proxy = ecalloc(1, sizeof(*proxy)); + proxy->dimensions = 1; + + if (Z_OBJCE_P(com_object) == php_com_saproxy_class_entry) { + rel = SA_FETCH(com_object); + proxy->obj = rel->obj; + proxy->zobj = rel->zobj; + proxy->dimensions += rel->dimensions; + } else { + proxy->obj = CDNO_FETCH(com_object); + proxy->zobj = com_object; + } + + Z_ADDREF_P(proxy->zobj); + proxy->indices = safe_emalloc(proxy->dimensions, sizeof(zval *), 0); + + if (rel) { + clone_indices(proxy, rel, rel->dimensions); + } + + MAKE_STD_ZVAL(proxy->indices[proxy->dimensions-1]); + *proxy->indices[proxy->dimensions-1] = *index; + zval_copy_ctor(proxy->indices[proxy->dimensions-1]); + + Z_TYPE_P(proxy_out) = IS_OBJECT; + Z_OBJ_HANDLE_P(proxy_out) = zend_objects_store_put(proxy, NULL, saproxy_free_storage, saproxy_clone TSRMLS_CC); + Z_OBJ_HT_P(proxy_out) = &php_com_saproxy_handlers; + + return 1; +} + +/* iterator */ + +static void saproxy_iter_dtor(zend_object_iterator *iter TSRMLS_DC) +{ + php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data; + + zval_ptr_dtor(&I->proxy_obj); + + efree(I->indices); + efree(I); +} + +static int saproxy_iter_valid(zend_object_iterator *iter TSRMLS_DC) +{ + php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data; + + return (I->key < I->imax) ? SUCCESS : FAILURE; +} + +static void saproxy_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC) +{ + php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data; + VARIANT v; + VARTYPE vt; + zval *return_value, **ptr_ptr; + SAFEARRAY *sa; + + I->indices[I->proxy->dimensions-1] = I->key; + + sa = V_ARRAY(&I->proxy->obj->v); + + if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) { + vt = V_VT(&I->proxy->obj->v) & ~VT_ARRAY; + } + + VariantInit(&v); + if (vt == VT_VARIANT) { + SafeArrayGetElement(sa, I->indices, &v); + } else { + V_VT(&v) = vt; + SafeArrayGetElement(sa, I->indices, &v.lVal); + } + + MAKE_STD_ZVAL(return_value); + php_com_wrap_variant(return_value, &v, I->proxy->obj->code_page TSRMLS_CC); + VariantClear(&v); + + ptr_ptr = emalloc(sizeof(*ptr_ptr)); + *ptr_ptr = return_value; + *data = ptr_ptr; +} + +static int saproxy_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, + ulong *int_key TSRMLS_DC) +{ + php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data; + + if (I->key == -1) { + return HASH_KEY_NON_EXISTANT; + } + *int_key = (ulong)I->key; + return HASH_KEY_IS_LONG; +} + +static int saproxy_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC) +{ + php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data; + + if (++I->key >= I->imax) { + I->key = -1; + return FAILURE; + } + return SUCCESS; +} + +static zend_object_iterator_funcs saproxy_iter_funcs = { + saproxy_iter_dtor, + saproxy_iter_valid, + saproxy_iter_get_data, + saproxy_iter_get_key, + saproxy_iter_move_forwards, + NULL +}; + + +zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) +{ + php_com_saproxy *proxy = SA_FETCH(object); + php_com_saproxy_iter *I; + int i; + + if (by_ref) { + zend_error(E_ERROR, "An iterator cannot be used with foreach by reference"); + } + + I = ecalloc(1, sizeof(*I)); + I->iter.funcs = &saproxy_iter_funcs; + I->iter.data = I; + + I->proxy = proxy; + I->proxy_obj = object; + Z_ADDREF_P(I->proxy_obj); + + I->indices = safe_emalloc(proxy->dimensions + 1, sizeof(LONG), 0); + for (i = 0; i < proxy->dimensions; i++) { + convert_to_long(proxy->indices[i]); + I->indices[i] = Z_LVAL_P(proxy->indices[i]); + } + + SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imin); + SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imax); + + I->key = I->imin; + + return &I->iter; +} + |