root/ext/com_dotnet/com_saproxy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clone_indices
  2. saproxy_property_read
  3. saproxy_property_write
  4. saproxy_read_dimension
  5. saproxy_write_dimension
  6. saproxy_object_set
  7. saproxy_object_get
  8. saproxy_property_exists
  9. saproxy_dimension_exists
  10. saproxy_property_delete
  11. saproxy_dimension_delete
  12. saproxy_properties_get
  13. saproxy_method_get
  14. saproxy_call_method
  15. saproxy_constructor_get
  16. saproxy_class_entry_get
  17. saproxy_class_name_get
  18. saproxy_objects_compare
  19. saproxy_object_cast
  20. saproxy_count_elements
  21. saproxy_free_storage
  22. saproxy_clone
  23. php_com_saproxy_create
  24. saproxy_iter_dtor
  25. saproxy_iter_valid
  26. saproxy_iter_get_data
  27. saproxy_iter_get_key
  28. saproxy_iter_move_forwards
  29. php_com_saproxy_iter_get

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 5                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 The PHP Group                                |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 3.01 of the PHP license,      |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.php.net/license/3_01.txt                                  |
  11    | If you did not receive a copy of the PHP license and are unable to   |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@php.net so we can mail you a copy immediately.               |
  14    +----------------------------------------------------------------------+
  15    | Author: Wez Furlong  <wez@thebrainroom.com>                          |
  16    +----------------------------------------------------------------------+
  17  */
  18 
  19 /* $Id$ */
  20 
  21 /* This module implements a SafeArray proxy which is used internally
  22  * by the engine when resolving multi-dimensional array accesses on
  23  * SafeArray types.
  24  * In addition, the proxy is now able to handle properties of COM objects
  25  * that smell like PHP arrays.
  26  * */
  27 
  28 #ifdef HAVE_CONFIG_H
  29 #include "config.h"
  30 #endif
  31 
  32 #include "php.h"
  33 #include "php_ini.h"
  34 #include "ext/standard/info.h"
  35 #include "php_com_dotnet.h"
  36 #include "php_com_dotnet_internal.h"
  37 #include "Zend/zend_exceptions.h"
  38 
  39 typedef struct {
  40         /* the object we a proxying for; we hold a refcount to it */
  41         zval *zobj;
  42         php_com_dotnet_object *obj;
  43 
  44         /* how many dimensions we are indirecting to get into this element */
  45         LONG dimensions;
  46         
  47         /* this is an array whose size_is(dimensions) */
  48         zval **indices;
  49 
  50 } php_com_saproxy;
  51 
  52 typedef struct {
  53         zend_object_iterator iter;
  54         zval *proxy_obj;
  55         php_com_saproxy *proxy;
  56         LONG key;
  57         LONG imin, imax;
  58         LONG *indices;
  59 } php_com_saproxy_iter;
  60 
  61 #define SA_FETCH(zv)                    (php_com_saproxy*)zend_object_store_get_object(zv TSRMLS_CC)
  62 
  63 static inline void clone_indices(php_com_saproxy *dest, php_com_saproxy *src, int ndims)
  64 {
  65         int i;
  66 
  67         for (i = 0; i < ndims; i++) {
  68                 MAKE_STD_ZVAL(dest->indices[i]);
  69                 *dest->indices[i] = *src->indices[i];
  70                 zval_copy_ctor(dest->indices[i]);
  71         }
  72 }
  73 
  74 static zval *saproxy_property_read(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
  75 {
  76         zval *return_value;
  77         
  78         MAKE_STD_ZVAL(return_value);
  79         ZVAL_NULL(return_value);
  80 
  81         php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC);
  82 
  83         return return_value;
  84 }
  85 
  86 static void saproxy_property_write(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
  87 {
  88         php_com_throw_exception(E_INVALIDARG, "safearray has no properties" TSRMLS_CC);
  89 }
  90 
  91 static zval *saproxy_read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
  92 {
  93         php_com_saproxy *proxy = SA_FETCH(object);
  94         zval *return_value;
  95         UINT dims, i;
  96         SAFEARRAY *sa;
  97         LONG ubound, lbound;
  98         HRESULT res;
  99         
 100         MAKE_STD_ZVAL(return_value);
 101         ZVAL_NULL(return_value);
 102         
 103         if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
 104                 VARIANT v;
 105                 zval **args;
 106 
 107                 /* prop-get using first dimension as the property name,
 108                  * all subsequent dimensions and the offset as parameters */
 109 
 110                 args = safe_emalloc(proxy->dimensions + 1, sizeof(zval *), 0);
 111 
 112                 for (i = 1; i < (UINT) proxy->dimensions; i++) {
 113                         args[i-1] = proxy->indices[i];
 114                 }
 115                 args[i-1] = offset;
 116 
 117                 convert_to_string(proxy->indices[0]);
 118                 VariantInit(&v);
 119 
 120                 res = php_com_do_invoke(proxy->obj, Z_STRVAL_P(proxy->indices[0]),
 121                                 Z_STRLEN_P(proxy->indices[0]), DISPATCH_METHOD|DISPATCH_PROPERTYGET, &v,
 122                                 proxy->dimensions, args, 0 TSRMLS_CC);
 123 
 124                 if (res == SUCCESS) {
 125                         php_com_zval_from_variant(return_value, &v, proxy->obj->code_page TSRMLS_CC);
 126                         VariantClear(&v);
 127                 } else if (res == DISP_E_BADPARAMCOUNT) {
 128                         /* return another proxy */
 129                         php_com_saproxy_create(object, return_value, offset TSRMLS_CC);
 130                 }
 131 
 132                 return return_value;
 133 
 134         } else if (!V_ISARRAY(&proxy->obj->v)) {
 135                 php_com_throw_exception(E_INVALIDARG, "invalid read from com proxy object" TSRMLS_CC);
 136                 return return_value;
 137         }
 138 
 139         /* the SafeArray case */
 140         
 141         /* offset/index must be an integer */
 142         convert_to_long(offset);
 143         
 144         sa = V_ARRAY(&proxy->obj->v);
 145         dims = SafeArrayGetDim(sa);
 146 
 147         if ((UINT) proxy->dimensions >= dims) {
 148                 /* too many dimensions */
 149                 php_com_throw_exception(E_INVALIDARG, "too many dimensions!" TSRMLS_CC);
 150                 return return_value;
 151         }
 152 
 153         /* bounds check */
 154         SafeArrayGetLBound(sa, proxy->dimensions, &lbound);
 155         SafeArrayGetUBound(sa, proxy->dimensions, &ubound);
 156 
 157         if (Z_LVAL_P(offset) < lbound || Z_LVAL_P(offset) > ubound) {
 158                 php_com_throw_exception(DISP_E_BADINDEX, "index out of bounds" TSRMLS_CC);
 159                 return return_value;
 160         }
 161         
 162         if (dims - 1 == proxy->dimensions) {
 163                 LONG *indices;
 164                 VARTYPE vt;
 165                 VARIANT v;
 166                 
 167                 VariantInit(&v);
 168                 
 169                 /* we can return a real value */
 170                 indices = safe_emalloc(dims, sizeof(LONG), 0);
 171 
 172                 /* copy indices from proxy */
 173                 for (i = 0; i < dims; i++) {
 174                         convert_to_long(proxy->indices[i]);
 175                         indices[i] = Z_LVAL_P(proxy->indices[i]);
 176                 }
 177 
 178                 /* add user-supplied index */
 179                 indices[dims-1] = Z_LVAL_P(offset);
 180 
 181                 /* now fetch the value */
 182                 if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
 183                         vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
 184                 }
 185 
 186                 if (vt == VT_VARIANT) {
 187                         res = SafeArrayGetElement(sa, indices, &v);
 188                 } else {
 189                         V_VT(&v) = vt;
 190                         res = SafeArrayGetElement(sa, indices, &v.lVal);
 191                 }
 192 
 193                 efree(indices);
 194 
 195                 if (SUCCEEDED(res)) {
 196                         php_com_wrap_variant(return_value, &v, proxy->obj->code_page TSRMLS_CC);
 197                 } else {
 198                         php_com_throw_exception(res, NULL TSRMLS_CC);
 199                 }
 200 
 201                 VariantClear(&v);
 202                 
 203         } else {
 204                 /* return another proxy */
 205                 php_com_saproxy_create(object, return_value, offset TSRMLS_CC);
 206         }
 207 
 208         return return_value;
 209 }
 210 
 211 static void saproxy_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
 212 {
 213         php_com_saproxy *proxy = SA_FETCH(object);
 214         UINT dims, i;
 215         HRESULT res;
 216         VARIANT v;
 217         
 218         if (V_VT(&proxy->obj->v) == VT_DISPATCH) {
 219                 /* We do a prop-set using the first dimension as the property name,
 220                  * all subsequent dimensions and offset as parameters, with value as
 221                  * the final value */
 222                 zval **args = safe_emalloc(proxy->dimensions + 2, sizeof(zval *), 0);
 223 
 224                 for (i = 1; i < (UINT) proxy->dimensions; i++) {
 225                         args[i-1] = proxy->indices[i];
 226                 }
 227                 args[i-1] = offset;
 228                 args[i] = value;
 229 
 230                 convert_to_string(proxy->indices[0]);
 231                 VariantInit(&v);
 232                 if (SUCCESS == php_com_do_invoke(proxy->obj, Z_STRVAL_P(proxy->indices[0]),
 233                                         Z_STRLEN_P(proxy->indices[0]), DISPATCH_PROPERTYPUT, &v, proxy->dimensions + 1,
 234                                         args, 0 TSRMLS_CC)) {
 235                         VariantClear(&v);
 236                 }
 237 
 238                 efree(args);
 239                 
 240         } else if (V_ISARRAY(&proxy->obj->v)) {
 241                 LONG *indices;
 242                 VARTYPE vt;
 243 
 244                 dims = SafeArrayGetDim(V_ARRAY(&proxy->obj->v));
 245                 indices = safe_emalloc(dims, sizeof(LONG), 0);
 246                 /* copy indices from proxy */
 247                 for (i = 0; i < dims; i++) {
 248                         convert_to_long(proxy->indices[i]);
 249                         indices[i] = Z_LVAL_P(proxy->indices[i]);
 250                 }
 251 
 252                 /* add user-supplied index */
 253                 convert_to_long(offset);
 254                 indices[dims-1] = Z_LVAL_P(offset);
 255 
 256                 if (FAILED(SafeArrayGetVartype(V_ARRAY(&proxy->obj->v), &vt)) || vt == VT_EMPTY) {
 257                         vt = V_VT(&proxy->obj->v) & ~VT_ARRAY;
 258                 }
 259 
 260                 VariantInit(&v);
 261                 php_com_variant_from_zval(&v, value, proxy->obj->code_page TSRMLS_CC);
 262 
 263                 if (V_VT(&v) != vt) {
 264                         VariantChangeType(&v, &v, 0, vt);
 265                 }
 266 
 267                 if (vt == VT_VARIANT) {
 268                         res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v);
 269                 } else {
 270                         res = SafeArrayPutElement(V_ARRAY(&proxy->obj->v), indices, &v.lVal);
 271                 }
 272         
 273                 efree(indices);
 274                 VariantClear(&v);
 275 
 276                 if (FAILED(res)) {
 277                         php_com_throw_exception(res, NULL TSRMLS_CC);
 278                 }
 279         } else {
 280                 php_com_throw_exception(E_NOTIMPL, "invalid write to com proxy object" TSRMLS_CC);
 281         }
 282 }
 283 
 284 #if 0
 285 static void saproxy_object_set(zval **property, zval *value TSRMLS_DC)
 286 {
 287 }
 288 
 289 static zval *saproxy_object_get(zval *property TSRMLS_DC)
 290 {
 291         /* Not yet implemented in the engine */
 292         return NULL;
 293 }
 294 #endif
 295 
 296 static int saproxy_property_exists(zval *object, zval *member, int check_empty, const zend_literal *key TSRMLS_DC)
 297 {
 298         /* no properties */
 299         return 0;
 300 }
 301 
 302 static int saproxy_dimension_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
 303 {
 304         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Operation not yet supported on a COM object");
 305         return 0;
 306 }
 307 
 308 static void saproxy_property_delete(zval *object, zval *member, const zend_literal *key TSRMLS_DC)
 309 {
 310         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object");
 311 }
 312 
 313 static void saproxy_dimension_delete(zval *object, zval *offset TSRMLS_DC)
 314 {
 315         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a COM object");
 316 }
 317 
 318 static HashTable *saproxy_properties_get(zval *object TSRMLS_DC)
 319 {
 320         /* no properties */
 321         return NULL;
 322 }
 323 
 324 static union _zend_function *saproxy_method_get(zval **object, const char *name, int len, const zend_literal *key TSRMLS_DC)
 325 {
 326         /* no methods */
 327         return NULL;
 328 }
 329 
 330 static int saproxy_call_method(const char *method, INTERNAL_FUNCTION_PARAMETERS)
 331 {
 332         return FAILURE;
 333 }
 334 
 335 static union _zend_function *saproxy_constructor_get(zval *object TSRMLS_DC)
 336 {
 337         /* user cannot instantiate */
 338         return NULL;
 339 }
 340 
 341 static zend_class_entry *saproxy_class_entry_get(const zval *object TSRMLS_DC)
 342 {
 343         return php_com_saproxy_class_entry;
 344 }
 345 
 346 static int saproxy_class_name_get(const zval *object, const char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
 347 {
 348         *class_name = estrndup(php_com_saproxy_class_entry->name, php_com_saproxy_class_entry->name_length);
 349         *class_name_len = php_com_saproxy_class_entry->name_length;
 350         return 0;
 351 }
 352 
 353 static int saproxy_objects_compare(zval *object1, zval *object2 TSRMLS_DC)
 354 {
 355         return -1;
 356 }
 357 
 358 static int saproxy_object_cast(zval *readobj, zval *writeobj, int type TSRMLS_DC)
 359 {
 360         return FAILURE;
 361 }
 362 
 363 static int saproxy_count_elements(zval *object, long *count TSRMLS_DC)
 364 {
 365         php_com_saproxy *proxy = SA_FETCH(object);
 366         LONG ubound, lbound;
 367         
 368         if (!V_ISARRAY(&proxy->obj->v)) {
 369                 return FAILURE;
 370         }
 371 
 372         SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &lbound);
 373         SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &ubound);
 374 
 375         *count = ubound - lbound + 1;
 376 
 377         return SUCCESS;
 378 }
 379 
 380 zend_object_handlers php_com_saproxy_handlers = {
 381         ZEND_OBJECTS_STORE_HANDLERS,
 382         saproxy_property_read,
 383         saproxy_property_write,
 384         saproxy_read_dimension,
 385         saproxy_write_dimension,
 386         NULL,
 387         NULL, /* saproxy_object_get, */
 388         NULL, /* saproxy_object_set, */
 389         saproxy_property_exists,
 390         saproxy_property_delete,
 391         saproxy_dimension_exists,
 392         saproxy_dimension_delete,
 393         saproxy_properties_get,
 394         saproxy_method_get,
 395         saproxy_call_method,
 396         saproxy_constructor_get,
 397         saproxy_class_entry_get,
 398         saproxy_class_name_get,
 399         saproxy_objects_compare,
 400         saproxy_object_cast,
 401         saproxy_count_elements
 402 };
 403 
 404 static void saproxy_free_storage(void *object TSRMLS_DC)
 405 {
 406         php_com_saproxy *proxy = (php_com_saproxy *)object;
 407         int i;
 408 
 409         for (i = 0; i < proxy->dimensions; i++) {
 410                 if (proxy->indices) {
 411                                 FREE_ZVAL(proxy->indices[i]);
 412                 }
 413         }
 414 
 415         zval_ptr_dtor(&proxy->zobj);
 416         efree(proxy->indices);
 417         efree(proxy);
 418 }
 419 
 420 static void saproxy_clone(void *object, void **clone_ptr TSRMLS_DC)
 421 {
 422         php_com_saproxy *proxy = (php_com_saproxy *)object;
 423         php_com_saproxy *cloneproxy;
 424 
 425         cloneproxy = emalloc(sizeof(*cloneproxy));
 426         memcpy(cloneproxy, proxy, sizeof(*cloneproxy));
 427 
 428         Z_ADDREF_P(cloneproxy->zobj);
 429         cloneproxy->indices = safe_emalloc(cloneproxy->dimensions, sizeof(zval *), 0);
 430         clone_indices(cloneproxy, proxy, proxy->dimensions);
 431 
 432         *clone_ptr = cloneproxy;
 433 }
 434 
 435 int php_com_saproxy_create(zval *com_object, zval *proxy_out, zval *index TSRMLS_DC)
 436 {
 437         php_com_saproxy *proxy, *rel = NULL;
 438 
 439         proxy = ecalloc(1, sizeof(*proxy));
 440         proxy->dimensions = 1;
 441 
 442         if (Z_OBJCE_P(com_object) == php_com_saproxy_class_entry) {
 443                 rel = SA_FETCH(com_object);
 444                 proxy->obj = rel->obj;
 445                 proxy->zobj = rel->zobj;
 446                 proxy->dimensions += rel->dimensions;
 447         } else {
 448                 proxy->obj = CDNO_FETCH(com_object);
 449                 proxy->zobj = com_object;
 450         }
 451 
 452         Z_ADDREF_P(proxy->zobj);
 453         proxy->indices = safe_emalloc(proxy->dimensions, sizeof(zval *), 0);
 454 
 455         if (rel) {
 456                 clone_indices(proxy, rel, rel->dimensions);
 457         }
 458 
 459         MAKE_STD_ZVAL(proxy->indices[proxy->dimensions-1]);
 460         *proxy->indices[proxy->dimensions-1] = *index;
 461         zval_copy_ctor(proxy->indices[proxy->dimensions-1]);
 462 
 463         Z_TYPE_P(proxy_out) = IS_OBJECT;
 464         Z_OBJ_HANDLE_P(proxy_out) = zend_objects_store_put(proxy, NULL, saproxy_free_storage, saproxy_clone TSRMLS_CC);
 465         Z_OBJ_HT_P(proxy_out) = &php_com_saproxy_handlers;
 466         
 467         return 1;
 468 }
 469 
 470 /* iterator */
 471 
 472 static void saproxy_iter_dtor(zend_object_iterator *iter TSRMLS_DC)
 473 {
 474         php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
 475 
 476         zval_ptr_dtor(&I->proxy_obj);
 477 
 478         efree(I->indices);
 479         efree(I);
 480 }
 481 
 482 static int saproxy_iter_valid(zend_object_iterator *iter TSRMLS_DC)
 483 {
 484         php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
 485 
 486         return (I->key < I->imax) ? SUCCESS : FAILURE;
 487 }
 488 
 489 static void saproxy_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
 490 {
 491         php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
 492         VARIANT v;
 493         VARTYPE vt;
 494         zval *return_value, **ptr_ptr;
 495         SAFEARRAY *sa;
 496 
 497         I->indices[I->proxy->dimensions-1] = I->key;
 498         
 499         sa = V_ARRAY(&I->proxy->obj->v);
 500         
 501         if (FAILED(SafeArrayGetVartype(sa, &vt)) || vt == VT_EMPTY) {
 502                 vt = V_VT(&I->proxy->obj->v) & ~VT_ARRAY;
 503         }
 504 
 505         VariantInit(&v);
 506         if (vt == VT_VARIANT) {
 507                 SafeArrayGetElement(sa, I->indices, &v);
 508         } else {
 509                 V_VT(&v) = vt;
 510                 SafeArrayGetElement(sa, I->indices, &v.lVal);
 511         }
 512 
 513         MAKE_STD_ZVAL(return_value);
 514         php_com_wrap_variant(return_value, &v, I->proxy->obj->code_page TSRMLS_CC);
 515         VariantClear(&v);
 516 
 517         ptr_ptr = emalloc(sizeof(*ptr_ptr));
 518         *ptr_ptr = return_value;
 519         *data = ptr_ptr;
 520 }
 521 
 522 static void saproxy_iter_get_key(zend_object_iterator *iter, zval *key TSRMLS_DC)
 523 {
 524         php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
 525 
 526         if (I->key == -1) {
 527                 ZVAL_NULL(key);
 528         } else {
 529                 ZVAL_LONG(key, I->key);
 530         }
 531 }
 532 
 533 static int saproxy_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC)
 534 {
 535         php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data;
 536 
 537         if (++I->key >= I->imax) {
 538                 I->key = -1;
 539                 return FAILURE;
 540         }
 541         return SUCCESS;
 542 }
 543 
 544 static zend_object_iterator_funcs saproxy_iter_funcs = {
 545         saproxy_iter_dtor,
 546         saproxy_iter_valid,
 547         saproxy_iter_get_data,
 548         saproxy_iter_get_key,
 549         saproxy_iter_move_forwards,
 550         NULL
 551 };
 552 
 553 
 554 zend_object_iterator *php_com_saproxy_iter_get(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
 555 {
 556         php_com_saproxy *proxy = SA_FETCH(object);
 557         php_com_saproxy_iter *I;
 558         int i;
 559 
 560         if (by_ref) {
 561                 zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
 562         }
 563 
 564         I = ecalloc(1, sizeof(*I));
 565         I->iter.funcs = &saproxy_iter_funcs;
 566         I->iter.data = I;
 567 
 568         I->proxy = proxy;
 569         I->proxy_obj = object;
 570         Z_ADDREF_P(I->proxy_obj);
 571 
 572         I->indices = safe_emalloc(proxy->dimensions + 1, sizeof(LONG), 0);
 573         for (i = 0; i < proxy->dimensions; i++) {
 574                 convert_to_long(proxy->indices[i]);
 575                 I->indices[i] = Z_LVAL_P(proxy->indices[i]);
 576         }
 577 
 578         SafeArrayGetLBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imin);
 579         SafeArrayGetUBound(V_ARRAY(&proxy->obj->v), proxy->dimensions, &I->imax);
 580 
 581         I->key = I->imin;       
 582         
 583         return &I->iter;
 584 }
 585 

/* [<][>][^][v][top][bottom][index][help] */