Back to index

php5  5.3.10
com_saproxy.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Wez Furlong  <wez@thebrainroom.com>                          |
00016    +----------------------------------------------------------------------+
00017  */
00018 
00019 /* $Id: com_saproxy.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 /* This module implements a SafeArray proxy which is used internally
00022  * by the engine when resolving multi-dimensional array accesses on
00023  * SafeArray types.
00024  * In addition, the proxy is now able to handle properties of COM objects
00025  * that smell like PHP arrays.
00026  * */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include "config.h"
00030 #endif
00031 
00032 #include "php.h"
00033 #include "php_ini.h"
00034 #include "ext/standard/info.h"
00035 #include "php_com_dotnet.h"
00036 #include "php_com_dotnet_internal.h"
00037 #include "Zend/zend_exceptions.h"
00038 
00039 typedef struct {
00040        /* the object we a proxying for; we hold a refcount to it */
00041        zval *zobj;
00042        php_com_dotnet_object *obj;
00043 
00044        /* how many dimensions we are indirecting to get into this element */
00045        LONG dimensions;
00046        
00047        /* this is an array whose size_is(dimensions) */
00048        zval **indices;
00049 
00050 } php_com_saproxy;
00051 
00052 typedef struct {
00053        zend_object_iterator iter;
00054        zval *proxy_obj;
00055        php_com_saproxy *proxy;
00056        LONG key;
00057        LONG imin, imax;
00058        LONG *indices;
00059 } php_com_saproxy_iter;
00060 
00061 #define SA_FETCH(zv)               (php_com_saproxy*)zend_object_store_get_object(zv TSRMLS_CC)
00062 
00063 static inline void clone_indices(php_com_saproxy *dest, php_com_saproxy *src, int ndims)
00064 {
00065        int i;
00066 
00067        for (i = 0; i < ndims; i++) {
00068               MAKE_STD_ZVAL(dest->indices[i]);
00069               *dest->indices[i] = *src->indices[i];
00070               zval_copy_ctor(dest->indices[i]);
00071        }
00072 }
00073 
00074 static zval *saproxy_property_read(zval *object, zval *member, int type TSRMLS_DC)
00075 {
00076        zval *return_value;
00077        
00078        MAKE_STD_ZVAL(return_value);
00079        ZVAL_NULL(return_value);
00080 
00081        php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC);
00082 
00083        return return_value;
00084 }
00085 
00086 static void saproxy_property_write(zval *object, zval *member, zval *value TSRMLS_DC)
00087 {
00088        php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC);
00089 }
00090 
00091 static zval *saproxy_read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
00092 {
00093        php_com_saproxy *proxy = SA_FETCH(object);
00094        zval *return_value;
00095        UINT dims, i;
00096        SAFEARRAY *sa;
00097        LONG ubound, lbound;
00098        HRESULT res;
00099        
00100        MAKE_STD_ZVAL(return_value);
00101        ZVAL_NULL(return_value);
00102        
00103        if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
00104               VARIANT v;
00105               zval **args;
00106 
00107               /* prop-get using first dimension as the property name,
00108                * all subsequent dimensions and the offset as parameters */
00109 
00110               args = safe_emalloc(proxy->dimensions + 1, sizeof(zval *), 0);
00111 
00112               for (i = 1; i < (UINT) proxy->dimensions; i++) {
00113                      args[i-1] = proxy->indices[i];
00114               }
00115               args[i-1] = offset;
00116 
00117               convert_to_string(proxy->indices[0]);
00118               VariantInit(&v);
00119 
00120               res = php_com_do_invoke(proxy->obj, Z_STRVAL_P(proxy->indices[0]),
00121                             Z_STRLEN_P(proxy->indices[0]), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v,
00122                             proxy->dimensions, args, 0 TSRMLS_CC);
00123 
00124               if (res == SUCCESS) {
00125                      php_com_zval_from_variant(return_value, &v, proxy->obj->code_page TSRMLS_CC);
00126                      VariantClear(&v);
00127               } else if (res == DISP_E_BADPARAMCOUNT) {
00128                      /* return another proxy */
00129                      php_com_saproxy_create(object, return_value, offset TSRMLS_CC);
00130               }
00131 
00132               return return_value;
00133 
00134        } else if (!V_ISARRAY(&proxy->obj->v)) {
00135               php_com_throw_exception(E_INVALIDARG, "invalid read from com proxy object" TSRMLS_CC);
00136               return return_value;
00137        }
00138 
00139        /* the SafeArray case */
00140        
00141        /* offset/index must be an integer */
00142        convert_to_long(offset);
00143        
00144        sa = V_ARRAY(&proxy->obj->v);
00145        dims = SafeArrayGetDim(sa);
00146 
00147        if ((UINT) proxy->dimensions >= dims) {
00148               /* too many dimensions */
00149               php_com_throw_exception(E_INVALIDARG, "too many dimensions!" TSRMLS_CC);
00150               return return_value;
00151        }
00152 
00153        /* bounds check */
00154        SafeArrayGetLBound(sa, proxy->dimensions, &lbound);
00155        SafeArrayGetUBound(sa, proxy->dimensions, &ubound);
00156 
00157        if (Z_LVAL_P(offset) < lbound || Z_LVAL_P(offset) > ubound) {
00158               php_com_throw_exception(DISP_E_BADINDEX, "index out of bounds" TSRMLS_CC);
00159               return return_value;
00160        }
00161        
00162        if (dims - 1 == proxy->dimensions) {
00163               LONG *indices;
00164               VARTYPE vt;
00165               VARIANT v;
00166               
00167               VariantInit(&v);
00168               
00169               /* we can return a real value */
00170               indices = safe_emalloc(dims, sizeof(LONG), 0);
00171 
00172               /* copy indices from proxy */
00173               for (i = 0; i < dims; i++) {
00174                      convert_to_long(proxy->indices[i]);
00175                      indices[i] = Z_LVAL_P(proxy->indices[i]);
00176               }
00177 
00178               /* add user-supplied index */
00179               indices[dims-1] = Z_LVAL_P(offset);
00180 
00181               /* now fetch the value */
00182               if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
00183                      vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
00184               }
00185 
00186               if (vt == VT_VARIANT) {
00187                      res = SafeArrayGetElement(sa, indices, &v);
00188               } else {
00189                      V_VT(&v) = vt;
00190                      res = SafeArrayGetElement(sa, indices, &v.lVal);
00191               }
00192 
00193               efree(indices);
00194 
00195               if (SUCCEEDED(res)) {
00196                      php_com_wrap_variant(return_value, &v, proxy->obj->code_page TSRMLS_CC);
00197               } else {
00198                      php_com_throw_exception(res, NULL TSRMLS_CC);
00199               }
00200 
00201               VariantClear(&v);
00202               
00203        } else {
00204               /* return another proxy */
00205               php_com_saproxy_create(object, return_value, offset TSRMLS_CC);
00206        }
00207 
00208        return return_value;
00209 }
00210 
00211 static void saproxy_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
00212 {
00213        php_com_saproxy *proxy = SA_FETCH(object);
00214        UINT dims, i;
00215        HRESULT res;
00216        VARIANT v;
00217        
00218        if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
00219               /* We do a prop-set using the first dimension as the property name,
00220                * all subsequent dimensions and offset as parameters, with value as
00221                * the final value */
00222               zval **args = safe_emalloc(proxy->dimensions + 2, sizeof(zval *), 0);
00223 
00224               for (i = 1; i < (UINT) proxy->dimensions; i++) {
00225                      args[i-1] = proxy->indices[i];
00226               }
00227               args[i-1] = offset;
00228               args[i] = value;
00229 
00230               convert_to_string(proxy->indices[0]);
00231               VariantInit(&v);
00232               if (SUCCESS == php_com_do_invoke(proxy->obj, Z_STRVAL_P(proxy->indices[0]),
00233                                    Z_STRLEN_P(proxy->indices[0]), DISPATCH_PROPERTYPUT, &v, proxy->dimensions + 1,
00234                                    args, 0 TSRMLS_CC)) {
00235                      VariantClear(&v);
00236               }
00237 
00238               efree(args);
00239               
00240        } else if (V_ISARRAY(&proxy->obj->v)) {
00241               LONG *indices;
00242               VARTYPE vt;
00243 
00244               dims = SafeArrayGetDim(V_ARRAY(&proxy->obj->v));
00245               indices = safe_emalloc(dims, sizeof(LONG), 0);
00246               /* copy indices from proxy */
00247               for (i = 0; i < dims; i++) {
00248                      convert_to_long(proxy->indices[i]);
00249                      indices[i] = Z_LVAL_P(proxy->indices[i]);
00250               }
00251 
00252               /* add user-supplied index */
00253               convert_to_long(offset);
00254               indices[dims-1] = Z_LVAL_P(offset);
00255 
00256               if (FAILED(SafeArrayGetVartype(V_ARRAY(&proxy->obj->v), &vt)) || vt == VT_EMPTY) {
00257                      vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
00258               }
00259 
00260               VariantInit(&v);
00261               php_com_variant_from_zval(&v, value, proxy->obj->code_page TSRMLS_CC);
00262 
00263               if (V_VT(&v) != vt) {
00264                      VariantChangeType(&v, &v, 0, vt);
00265               }
00266 
00267               if (vt == VT_VARIANT) {
00268                      res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v);
00269               } else {
00270                      res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v.lVal);
00271               }
00272        
00273               efree(indices);
00274               VariantClear(&v);
00275 
00276               if (FAILED(res)) {
00277                      php_com_throw_exception(res, NULL TSRMLS_CC);
00278               }
00279        } else {
00280               php_com_throw_exception(E_NOTIMPL, "invalid write to com proxy object" TSRMLS_CC);
00281        }
00282 }
00283 
00284 #if 0
00285 static void saproxy_object_set(zval **property, zval *value TSRMLS_DC)
00286 {
00287 }
00288 
00289 static zval *saproxy_object_get(zval *property TSRMLS_DC)
00290 {
00291        /* Not yet implemented in the engine */
00292        return NULL;
00293 }
00294 #endif
00295 
00296 static int saproxy_property_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
00297 {
00298        /* no properties */
00299        return 0;
00300 }
00301 
00302 static int saproxy_dimension_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
00303 {
00304        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Operation not yet supported on a COM object");
00305        return 0;
00306 }
00307 
00308 static void saproxy_property_delete(zval *object, zval *member TSRMLS_DC)
00309 {
00310        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object");
00311 }
00312 
00313 static void saproxy_dimension_delete(zval *object, zval *offset TSRMLS_DC)
00314 {
00315        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object");
00316 }
00317 
00318 static HashTable *saproxy_properties_get(zval *object TSRMLS_DC)
00319 {
00320        /* no properties */
00321        return NULL;
00322 }
00323 
00324 static union _zend_function *saproxy_method_get(zval **object, char *name, int len TSRMLS_DC)
00325 {
00326        /* no methods */
00327        return NULL;
00328 }
00329 
00330 static int saproxy_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
00331 {
00332        return FAILURE;
00333 }
00334 
00335 static union _zend_function *saproxy_constructor_get(zval *object TSRMLS_DC)
00336 {
00337        /* user cannot instantiate */
00338        return NULL;
00339 }
00340 
00341 static zend_class_entry *saproxy_class_entry_get(const zval *object TSRMLS_DC)
00342 {
00343        return php_com_saproxy_class_entry;
00344 }
00345 
00346 static int saproxy_class_name_get(const zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
00347 {
00348        *class_name = estrndup(php_com_saproxy_class_entry->name, php_com_saproxy_class_entry->name_length);
00349        *class_name_len = php_com_saproxy_class_entry->name_length;
00350        return 0;
00351 }
00352 
00353 static int saproxy_objects_compare(zval *object1, zval *object2 TSRMLS_DC)
00354 {
00355        return -1;
00356 }
00357 
00358 static int saproxy_object_cast(zval *readobj, zval *writeobj, int type TSRMLS_DC)
00359 {
00360        return FAILURE;
00361 }
00362 
00363 static int saproxy_count_elements(zval *object, long *count TSRMLS_DC)
00364 {
00365        php_com_saproxy *proxy = SA_FETCH(object);
00366        LONG ubound, lbound;
00367        
00368        if (!V_ISARRAY(&proxy->obj->v)) {
00369               return FAILURE;
00370        }
00371 
00372        SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &lbound);
00373        SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &ubound);
00374 
00375        *count = ubound - lbound + 1;
00376 
00377        return SUCCESS;
00378 }
00379 
00380 zend_object_handlers php_com_saproxy_handlers = {
00381        ZEND_OBJECTS_STORE_HANDLERS,
00382        saproxy_property_read,
00383        saproxy_property_write,
00384        saproxy_read_dimension,
00385        saproxy_write_dimension,
00386        NULL,
00387        NULL, /* saproxy_object_get, */
00388        NULL, /* saproxy_object_set, */
00389        saproxy_property_exists,
00390        saproxy_property_delete,
00391        saproxy_dimension_exists,
00392        saproxy_dimension_delete,
00393        saproxy_properties_get,
00394        saproxy_method_get,
00395        saproxy_call_method,
00396        saproxy_constructor_get,
00397        saproxy_class_entry_get,
00398        saproxy_class_name_get,
00399        saproxy_objects_compare,
00400        saproxy_object_cast,
00401        saproxy_count_elements
00402 };
00403 
00404 static void saproxy_free_storage(void *object TSRMLS_DC)
00405 {
00406        php_com_saproxy *proxy = (php_com_saproxy *)object;
00407        int i;
00408 
00409        for (i = 0; i < proxy->dimensions; i++) {
00410               if (proxy->indices) {
00411                             FREE_ZVAL(proxy->indices[i]);
00412               }
00413        }
00414 
00415        zval_ptr_dtor(&proxy->zobj);
00416        efree(proxy->indices);
00417        efree(proxy);
00418 }
00419 
00420 static void saproxy_clone(void *object, void **clone_ptr TSRMLS_DC)
00421 {
00422        php_com_saproxy *proxy = (php_com_saproxy *)object;
00423        php_com_saproxy *cloneproxy;
00424 
00425        cloneproxy = emalloc(sizeof(*cloneproxy));
00426        memcpy(cloneproxy, proxy, sizeof(*cloneproxy));
00427 
00428        Z_ADDREF_P(cloneproxy->zobj);
00429        cloneproxy->indices = safe_emalloc(cloneproxy->dimensions, sizeof(zval *), 0);
00430        clone_indices(cloneproxy, proxy, proxy->dimensions);
00431 
00432        *clone_ptr = cloneproxy;
00433 }
00434 
00435 int php_com_saproxy_create(zval *com_object, zval *proxy_out, zval *index TSRMLS_DC)
00436 {
00437        php_com_saproxy *proxy, *rel = NULL;
00438 
00439        proxy = ecalloc(1, sizeof(*proxy));
00440        proxy->dimensions = 1;
00441 
00442        if (Z_OBJCE_P(com_object) == php_com_saproxy_class_entry) {
00443               rel = SA_FETCH(com_object);
00444               proxy->obj = rel->obj;
00445               proxy->zobj = rel->zobj;
00446               proxy->dimensions += rel->dimensions;
00447        } else {
00448               proxy->obj = CDNO_FETCH(com_object);
00449               proxy->zobj = com_object;
00450        }
00451 
00452        Z_ADDREF_P(proxy->zobj);
00453        proxy->indices = safe_emalloc(proxy->dimensions, sizeof(zval *), 0);
00454 
00455        if (rel) {
00456               clone_indices(proxy, rel, rel->dimensions);
00457        }
00458 
00459        MAKE_STD_ZVAL(proxy->indices[proxy->dimensions-1]);
00460        *proxy->indices[proxy->dimensions-1] = *index;
00461        zval_copy_ctor(proxy->indices[proxy->dimensions-1]);
00462 
00463        Z_TYPE_P(proxy_out) = IS_OBJECT;
00464        Z_OBJ_HANDLE_P(proxy_out) = zend_objects_store_put(proxy, NULL, saproxy_free_storage, saproxy_clone TSRMLS_CC);
00465        Z_OBJ_HT_P(proxy_out) = &php_com_saproxy_handlers;
00466        
00467        return 1;
00468 }
00469 
00470 /* iterator */
00471 
00472 static void saproxy_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
00473 {
00474        php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
00475 
00476        zval_ptr_dtor(&I->proxy_obj);
00477 
00478        efree(I->indices);
00479        efree(I);
00480 }
00481 
00482 static int saproxy_iter_valid(zend_object_iterator *iter TSRMLS_DC)
00483 {
00484        php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
00485 
00486        return (I->key < I->imax) ? SUCCESS : FAILURE;
00487 }
00488 
00489 static void saproxy_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
00490 {
00491        php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
00492        VARIANT v;
00493        VARTYPE vt;
00494        zval *return_value, **ptr_ptr;
00495        SAFEARRAY *sa;
00496 
00497        I->indices[I->proxy->dimensions-1] = I->key;
00498        
00499        sa = V_ARRAY(&I->proxy->obj->v);
00500        
00501        if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
00502               vt = V_VT(&I->proxy->obj->v) & ~VT_ARRAY;
00503        }
00504 
00505        VariantInit(&v);
00506        if (vt == VT_VARIANT) {
00507               SafeArrayGetElement(sa, I->indices, &v);
00508        } else {
00509               V_VT(&v) = vt;
00510               SafeArrayGetElement(sa, I->indices, &v.lVal);
00511        }
00512 
00513        MAKE_STD_ZVAL(return_value);
00514        php_com_wrap_variant(return_value, &v, I->proxy->obj->code_page TSRMLS_CC);
00515        VariantClear(&v);
00516 
00517        ptr_ptr = emalloc(sizeof(*ptr_ptr));
00518        *ptr_ptr = return_value;
00519        *data = ptr_ptr;
00520 }
00521 
00522 static int saproxy_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len,
00523        ulong *int_key TSRMLS_DC)
00524 {
00525        php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
00526 
00527        if (I->key == -1) {
00528               return HASH_KEY_NON_EXISTANT;
00529        }
00530        *int_key = (ulong)I->key;
00531        return HASH_KEY_IS_LONG;
00532 }
00533 
00534 static int saproxy_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
00535 {
00536        php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
00537 
00538        if (++I->key >= I->imax) {
00539               I->key = -1;
00540               return FAILURE;
00541        }
00542        return SUCCESS;
00543 }
00544 
00545 static zend_object_iterator_funcs saproxy_iter_funcs = {
00546        saproxy_iter_dtor,
00547        saproxy_iter_valid,
00548        saproxy_iter_get_data,
00549        saproxy_iter_get_key,
00550        saproxy_iter_move_forwards,
00551        NULL
00552 };
00553 
00554 
00555 zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
00556 {
00557        php_com_saproxy *proxy = SA_FETCH(object);
00558        php_com_saproxy_iter *I;
00559        int i;
00560 
00561        if (by_ref) {
00562               zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
00563        }
00564 
00565        I = ecalloc(1, sizeof(*I));
00566        I->iter.funcs = &saproxy_iter_funcs;
00567        I->iter.data = I;
00568 
00569        I->proxy = proxy;
00570        I->proxy_obj = object;
00571        Z_ADDREF_P(I->proxy_obj);
00572 
00573        I->indices = safe_emalloc(proxy->dimensions + 1, sizeof(LONG), 0);
00574        for (i = 0; i < proxy->dimensions; i++) {
00575               convert_to_long(proxy->indices[i]);
00576               I->indices[i] = Z_LVAL_P(proxy->indices[i]);
00577        }
00578 
00579        SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imin);
00580        SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imax);
00581 
00582        I->key = I->imin;    
00583        
00584        return &I->iter;
00585 }
00586