123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- /*
- +----------------------------------------------------------------------+
- | Copyright (c) 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: |
- | https://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> |
- +----------------------------------------------------------------------+
- */
- /* 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 {
- zend_object std;
- /* the object we a proxying for; we hold a refcount to it */
- 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;
- zval data;
- php_com_saproxy *proxy;
- LONG key;
- LONG imin, imax;
- LONG *indices;
- } php_com_saproxy_iter;
- #define SA_FETCH(zv) (php_com_saproxy*)Z_OBJ_P(zv)
- static inline void clone_indices(php_com_saproxy *dest, php_com_saproxy *src, int ndims)
- {
- int i;
- for (i = 0; i < ndims; i++) {
- ZVAL_DUP(&dest->indices[i], &src->indices[i]);
- }
- }
- 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_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 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);
- if (!try_convert_to_string(&proxy->indices[0])) {
- efree(args);
- return rv;
- }
- 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);
- efree(args);
- 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 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);
- if (!try_convert_to_string(&proxy->indices[0])) {
- efree(args);
- return;
- }
- 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 int saproxy_property_exists(zend_object *object, zend_string *member, int check_empty, void **cache_slot)
- {
- /* no properties */
- return 0;
- }
- static int saproxy_dimension_exists(zend_object *object, zval *member, int check_empty)
- {
- /* TODO Add support */
- zend_throw_error(NULL, "Cannot check dimension on a COM object");
- return 0;
- }
- static void saproxy_property_delete(zend_object *object, zend_string *member, void **cache_slot)
- {
- zend_throw_error(NULL, "Cannot delete properties from a COM object");
- }
- static void saproxy_dimension_delete(zend_object *object, zval *offset)
- {
- zend_throw_error(NULL, "Cannot delete dimension from a COM object");
- }
- static HashTable *saproxy_properties_get(zend_object *object)
- {
- /* no properties */
- return NULL;
- }
- static zend_function *saproxy_method_get(zend_object **object, zend_string *name, const zval *key)
- {
- /* no methods */
- return NULL;
- }
- static zend_function *saproxy_constructor_get(zend_object *object)
- {
- /* user cannot instantiate */
- return NULL;
- }
- static zend_string* saproxy_class_name_get(const zend_object *object)
- {
- return zend_string_copy(php_com_saproxy_class_entry->name);
- }
- static int saproxy_objects_compare(zval *object1, zval *object2)
- {
- ZEND_COMPARE_OBJECTS_FALLBACK(object1, object2);
- return -1;
- }
- static int saproxy_object_cast(zend_object *readobj, zval *writeobj, int type)
- {
- return FAILURE;
- }
- static int saproxy_count_elements(zend_object *object, zend_long *count)
- {
- php_com_saproxy *proxy = (php_com_saproxy*) 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;
- }
- static void saproxy_free_storage(zend_object *object)
- {
- 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]);
- //??? }
- //??? }
- OBJ_RELEASE(&proxy->obj->zo);
- zend_object_std_dtor(object);
- efree(proxy->indices);
- }
- static zend_object* saproxy_clone(zend_object *object)
- {
- php_com_saproxy *proxy = (php_com_saproxy *) object;
- php_com_saproxy *cloneproxy;
- cloneproxy = emalloc(sizeof(*cloneproxy));
- memcpy(cloneproxy, proxy, sizeof(*cloneproxy));
- GC_ADDREF(&cloneproxy->obj->zo);
- cloneproxy->indices = safe_emalloc(cloneproxy->dimensions, sizeof(zval), 0);
- clone_indices(cloneproxy, proxy, proxy->dimensions);
- return &cloneproxy->std;
- }
- zend_object_handlers php_com_saproxy_handlers = {
- 0,
- saproxy_free_storage,
- zend_objects_destroy_object,
- saproxy_clone,
- saproxy_property_read,
- saproxy_property_write,
- saproxy_read_dimension,
- saproxy_write_dimension,
- NULL,
- saproxy_property_exists,
- saproxy_property_delete,
- saproxy_dimension_exists,
- saproxy_dimension_delete,
- saproxy_properties_get,
- saproxy_method_get,
- saproxy_constructor_get,
- saproxy_class_name_get,
- saproxy_object_cast,
- saproxy_count_elements,
- NULL, /* get_debug_info */
- NULL, /* get_closure */
- NULL, /* get_gc */
- NULL, /* do_operation */
- saproxy_objects_compare, /* compare */
- NULL, /* get_properties_for */
- };
- int php_com_saproxy_create(zend_object *com_object, zval *proxy_out, zval *index)
- {
- php_com_saproxy *proxy, *rel = NULL;
- proxy = ecalloc(1, sizeof(*proxy));
- proxy->dimensions = 1;
- if (com_object->ce == php_com_saproxy_class_entry) {
- rel = (php_com_saproxy*) com_object;
- proxy->obj = rel->obj;
- proxy->dimensions += rel->dimensions;
- } else {
- proxy->obj = (php_com_dotnet_object*) com_object;
- }
- GC_ADDREF(&proxy->obj->zo);
- proxy->indices = safe_emalloc(proxy->dimensions, sizeof(zval), 0);
- if (rel) {
- clone_indices(proxy, rel, rel->dimensions);
- }
- ZVAL_DUP(&proxy->indices[proxy->dimensions-1], index);
- zend_object_std_init(&proxy->std, php_com_saproxy_class_entry);
- proxy->std.handlers = &php_com_saproxy_handlers;
- ZVAL_OBJ(proxy_out, &proxy->std);
- return 1;
- }
- /* iterator */
- static void saproxy_iter_dtor(zend_object_iterator *iter)
- {
- php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
- zval_ptr_dtor(&I->proxy_obj);
- efree(I->indices);
- efree(I);
- }
- static int saproxy_iter_valid(zend_object_iterator *iter)
- {
- php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
- return (I->key < I->imax) ? SUCCESS : FAILURE;
- }
- static zval* saproxy_iter_get_data(zend_object_iterator *iter)
- {
- php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
- VARIANT v;
- VARTYPE vt;
- 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);
- }
- ZVAL_NULL(&I->data);
- php_com_wrap_variant(&I->data, &v, I->proxy->obj->code_page);
- VariantClear(&v);
- return &I->data;
- }
- static void saproxy_iter_get_key(zend_object_iterator *iter, zval *key)
- {
- php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
- if (I->key == -1) {
- ZVAL_NULL(key);
- } else {
- ZVAL_LONG(key, I->key);
- }
- }
- static void saproxy_iter_move_forwards(zend_object_iterator *iter)
- {
- php_com_saproxy_iter *I = (php_com_saproxy_iter*)Z_PTR(iter->data);
- if (++I->key >= I->imax) {
- I->key = -1;
- }
- }
- static const 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,
- NULL, /* get_gc */
- };
- zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object, int by_ref)
- {
- php_com_saproxy *proxy = SA_FETCH(object);
- php_com_saproxy_iter *I;
- int i;
- if (by_ref) {
- zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
- return NULL;
- }
- I = ecalloc(1, sizeof(*I));
- I->iter.funcs = &saproxy_iter_funcs;
- Z_PTR(I->iter.data) = I;
- I->proxy = proxy;
- Z_ADDREF_P(object);
- ZVAL_OBJ(&I->proxy_obj, Z_OBJ_P(object));
- 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] = (LONG)Z_LVAL(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;
- }
|