Merge branch 'v1.0.x'
authorMichael Wallner <mike@php.net>
Wed, 30 Sep 2015 09:24:31 +0000 (11:24 +0200)
committerMichael Wallner <mike@php.net>
Wed, 30 Sep 2015 09:24:31 +0000 (11:24 +0200)
1  2 
README.md
package.xml
scripts/gen_travis_yml.php
src/php_propro_api.c
src/php_propro_api.h

diff --cc README.md
index 333d2c1d5d77213b5d6fb25c674605980b673eac,a4aaa1fdb4639c985962adbdbaaee4f12a937fe7..4c3eb31951a19f18bb444830860f0a3c82b32115
+++ b/README.md
@@@ -1,8 -1,9 +1,9 @@@
  # ext-propro
  
 -[![Build Status](https://travis-ci.org/m6w6/ext-propro.svg?branch=v1.0.x)](https://travis-ci.org/m6w6/ext-propro)
 +[![Build Status](https://travis-ci.org/m6w6/ext-propro.svg?branch=master)](https://travis-ci.org/m6w6/ext-propro)
  
- Property Proxy
+ The "Property Proxy" extension provides a fairly transparent proxy for internal
+ object properties hidden in custom non-zval implementations.
  
  ## Documentation
  
diff --cc package.xml
Simple merge
index 0000000000000000000000000000000000000000,cc49a29245b6d0ae9277da38eb0ba02803dafeaa..0283920380250307204579ae6786a7e6c713fbf9
mode 000000,100755..100755
--- /dev/null
@@@ -1,0 -1,34 +1,34 @@@
 -      "PHP" => ["5.4", "5.5", "5.6"],
+ #!/usr/bin/env php
+ # autogenerated file; do not edit
+ sudo: false
+ language: c
+ addons:
+  apt:
+   packages:
+    - php5-cli
+    - php-pear
+ env:
+  matrix:
+ <?php
+ $gen = include "./travis/pecl/gen-matrix.php";
+ $env = $gen([
++      "PHP" => ["master"],
+       "enable_debug",
+       "enable_maintainer_zts",
+ ]);
+ foreach ($env as $e) {
+       printf("  - %s\n", $e);
+ }
+ ?>
+ before_script:
+  - make -f travis/pecl/Makefile php
+  - make -f travis/pecl/Makefile ext PECL=propro
+ script:
+  - make -f travis/pecl/Makefile test
index 0000000000000000000000000000000000000000,d7232b36b3a4b2025360667ee07f597ac8a9abe9..c92d6d4a3f7754da92505947919ca129ee9d3073
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,590 +1,597 @@@
 -#if PHP_VERSION_ID < 50500
 -#undef SUCCESS
 -#undef FAILURE
 -typedef enum {
 -      SUCCESS = 0,
 -      FAILURE = -1
 -} ZEND_RESULT_CODE;
 -#endif
 -
+ /*
+     +--------------------------------------------------------------------+
+     | PECL :: propro                                                     |
+     +--------------------------------------------------------------------+
+     | Redistribution and use in source and binary forms, with or without |
+     | modification, are permitted provided that the conditions mentioned |
+     | in the accompanying LICENSE file are met.                          |
+     +--------------------------------------------------------------------+
+     | Copyright (c) 2013 Michael Wallner <mike@php.net>                  |
+     +--------------------------------------------------------------------+
+ */
+ #ifdef HAVE_CONFIG_H
+ #     include "config.h"
+ #endif
+ #include <php.h>
+ #include <ext/standard/info.h>
+ #include "php_propro_api.h"
 -#if PHP_VERSION_ID < 50400
 -#     define object_properties_init(o, ce) \
 -      zend_hash_copy(((zend_object *) o)->properties, &(ce->default_properties), \
 -                      (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*))
 -#endif
+ #define DEBUG_PROPRO 0
 -php_property_proxy_t *php_property_proxy_init(zval *container,
 -              const char *member_str, size_t member_len TSRMLS_DC)
++static inline zval *get_referenced_zval(zval *ref)
++{
++      while (Z_ISREF_P(ref)) {
++              ref = Z_REFVAL_P(ref);
++      }
++      return ref;
++}
 -      Z_ADDREF_P(container);
 -      proxy->container = container;
 -      proxy->member_str = estrndup(member_str, member_len);
 -      proxy->member_len = member_len;
++php_property_proxy_t *php_property_proxy_init(zval *container, zend_string *member)
+ {
+       php_property_proxy_t *proxy = ecalloc(1, sizeof(*proxy));
 -              efree((*proxy)->member_str);
++      ZVAL_COPY(&proxy->container, get_referenced_zval(container));
++      proxy->member = zend_string_copy(member);
+       return proxy;
+ }
+ void php_property_proxy_free(php_property_proxy_t **proxy)
+ {
+       if (*proxy) {
+               zval_ptr_dtor(&(*proxy)->container);
 -zend_object_value php_property_proxy_object_new(zend_class_entry *ce TSRMLS_DC)
 -{
 -      return php_property_proxy_object_new_ex(ce, NULL, NULL TSRMLS_CC);
 -}
 -
 -static void php_property_proxy_object_free(void *object TSRMLS_DC)
 -{
 -      php_property_proxy_object_t *o = object;
 -
 -#if DEBUG_PROPRO
 -      fprintf(stderr, "#PP %p free\n", o);
 -#endif
 -
 -      if (o->proxy) {
 -              php_property_proxy_free(&o->proxy);
 -      }
 -      if (o->parent) {
 -              zend_objects_store_del_ref_by_handle_ex(o->parent->zv.handle,
 -                              o->parent->zv.handlers TSRMLS_CC);
 -              o->parent = NULL;
 -      }
 -      zend_object_std_dtor((zend_object *) o TSRMLS_CC);
 -      efree(o);
 -}
 -
 -zend_object_value php_property_proxy_object_new_ex(zend_class_entry *ce,
 -              php_property_proxy_t *proxy, php_property_proxy_object_t **ptr TSRMLS_DC)
 -{
 -      php_property_proxy_object_t *o;
 -
 -      o = ecalloc(1, sizeof(*o));
 -      zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
 -      object_properties_init((zend_object *) o, ce);
 -
 -      if (ptr) {
 -              *ptr = o;
 -      }
 -      o->proxy = proxy;
 -
 -      o->zv.handle = zend_objects_store_put(o, NULL,
 -                      php_property_proxy_object_free, NULL TSRMLS_CC);
 -      o->zv.handlers = &php_property_proxy_object_handlers;
 -
 -#if DEBUG_PROPRO
 -      fprintf(stderr, "#PP %p init\n", o);
 -#endif
 -
 -      return o->zv;
 -}
++              zend_string_release((*proxy)->member);
+               efree(*proxy);
+               *proxy = NULL;
+       }
+ }
+ static zend_class_entry *php_property_proxy_class_entry;
+ static zend_object_handlers php_property_proxy_object_handlers;
+ zend_class_entry *php_property_proxy_get_class_entry(void)
+ {
+       return php_property_proxy_class_entry;
+ }
 -static const char *inoutstr[] = {"< return",""," > enter"};
 -static void _walk(php_property_proxy_object_t *obj TSRMLS_DC)
++static inline php_property_proxy_object_t *get_propro(zval *object);
++static zval *get_parent_proxied_value(zval *object, zval *return_value);
++static zval *get_proxied_value(zval *object, zval *return_value);
++static zval *read_dimension(zval *object, zval *offset, int type, zval *return_value);
++static ZEND_RESULT_CODE cast_proxied_value(zval *object, zval *return_value, int type);
++static void write_dimension(zval *object, zval *offset, zval *value);
++static void set_proxied_value(zval *object, zval *value);
+ #if DEBUG_PROPRO
+ /* we do not really care about TS when debugging */
+ static int level = 1;
+ static const char space[] = "                               ";
 -              _walk(obj->parent TSRMLS_CC);
 -              fprintf(stderr, ".%s", obj->proxy->member_str);
++static const char *inoutstr[] = {"< return","="," > enter"};
++
++static void _walk(php_property_proxy_object_t *obj)
+ {
+       if (obj) {
 -static void debug_propro(int inout, const char *f, zval *object, zval *offset,
 -              zval *value TSRMLS_DC)
++              if (!Z_ISUNDEF(obj->parent)) {
++                      _walk(get_propro(&obj->parent));
++              }
++              if (obj->proxy) {
++                      fprintf(stderr, ".%s", obj->proxy->member->val);
++              }
+       }
+ }
 -      php_property_proxy_object_t *obj;
 -
 -      obj = zend_object_store_get_object(object TSRMLS_CC);
++static void debug_propro(int inout, const char *f,
++              php_property_proxy_object_t *obj, zval *offset, zval *value TSRMLS_DC)
+ {
 -      _walk(obj TSRMLS_CC);
+       fprintf(stderr, "#PP %p %s %s %s ", obj, &space[sizeof(space)-level],
+                       inoutstr[inout+1], f);
+       level += inout;
 -                      convert_to_string_ex(&o);
++      _walk(obj);
+       if (*f++=='d'
+       &&      *f++=='i'
+       &&      *f++=='m'
+       ) {
+               char *offset_str = "[]";
+               zval *o = offset;
+               if (o) {
 -                      zval_ptr_dtor(&o);
++                      convert_to_string_ex(o);
+                       offset_str = Z_STRVAL_P(o);
+               }
+               fprintf(stderr, ".%s", offset_str);
+               if (o && o != offset) {
 -      if (value) {
++                      zval_ptr_dtor(o);
+               }
+       }
 -                              "bool",
++      if (value && !Z_ISUNDEF_P(value)) {
+               const char *t[] = {
++                              "UNDEF",
+                               "NULL",
++                              "FALSE",
++                              "TRUE",
+                               "int",
+                               "float",
 -                              "string",
++                              "string",
+                               "Array",
+                               "Object",
 -                              "const",
 -                              "const Array",
 -                              "callable"
+                               "resource",
 -              zend_print_flat_zval_r(value TSRMLS_CC);
++                              "reference",
++                              "constant",
++                              "constant AST",
++                              "_BOOL",
++                              "callable",
++                              "indirect",
++                              "---",
++                              "pointer"
+               };
+               fprintf(stderr, " = (%s) ", t[Z_TYPE_P(value)&0xf]);
 -static zval *get_parent_proxied_value(zval *object TSRMLS_DC);
 -static zval *get_proxied_value(zval *object TSRMLS_DC);
 -static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
 -static ZEND_RESULT_CODE cast_proxied_value(zval *object, zval *return_value,
 -              int type TSRMLS_DC);
 -static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC);
 -static void set_proxied_value(zval **object, zval *value TSRMLS_DC);
++              if (!Z_ISUNDEF_P(value) && Z_TYPE_P(value) != IS_INDIRECT) {
++                      zend_print_flat_zval_r(value TSRMLS_CC);
++              }
+       }
+       fprintf(stderr, "\n");
+ }
+ #else
+ #define debug_propro(l, f, obj, off, val)
+ #endif
 -static zval *get_parent_proxied_value(zval *object TSRMLS_DC)
++php_property_proxy_object_t *php_property_proxy_object_new_ex(
++              zend_class_entry *ce, php_property_proxy_t *proxy)
++{
++      php_property_proxy_object_t *o;
++
++      if (!ce) {
++              ce = php_property_proxy_class_entry;
++      }
++
++      o = ecalloc(1, sizeof(*o) + sizeof(zval) * (ce->default_properties_count - 1));
++      zend_object_std_init(&o->zo, ce);
++      object_properties_init(&o->zo, ce);
++
++      o->proxy = proxy;
++      o->zo.handlers = &php_property_proxy_object_handlers;
++
++      debug_propro(0, "init", o, NULL, NULL);
++
++      return o;
++}
++
++zend_object *php_property_proxy_object_new(zend_class_entry *ce)
++{
++      return &php_property_proxy_object_new_ex(ce, NULL)->zo;
++}
++
++static void destroy_obj(zend_object *object)
++{
++      php_property_proxy_object_t *o = PHP_PROPRO_PTR(object);
++
++      debug_propro(0, "dtor", o, NULL, NULL);
++
++      if (o->proxy) {
++              php_property_proxy_free(&o->proxy);
++      }
++      if (!Z_ISUNDEF(o->parent)) {
++              zval_ptr_dtor(&o->parent);
++              ZVAL_UNDEF(&o->parent);
++      }
++      zend_object_std_dtor(object);
++}
++
++static inline php_property_proxy_object_t *get_propro(zval *object)
++{
++      object = get_referenced_zval(object);
++      switch (Z_TYPE_P(object)) {
++      case IS_OBJECT:
++              break;
 -      zval *value = NULL;
++      EMPTY_SWITCH_DEFAULT_CASE();
++      }
++      return PHP_PROPRO_PTR(Z_OBJ_P(object));
++}
++
++static inline zend_bool got_value(zval *container, zval *value)
++{
++      zval identical;
++
++      if (!Z_ISUNDEF_P(value)) {
++              if (SUCCESS == is_identical_function(&identical, value, container)) {
++                      if (Z_TYPE(identical) != IS_TRUE) {
++                              return 1;
++                      }
++              }
++      }
++
++      return 0;
++}
++
++static zval *get_parent_proxied_value(zval *object, zval *return_value)
+ {
 -      obj = zend_object_store_get_object(object TSRMLS_CC);
+       php_property_proxy_object_t *obj;
 -              if (obj->parent) {
 -                      zval *parent;
 -
 -                      MAKE_STD_ZVAL(parent);
 -                      parent->type = IS_OBJECT;
 -                      parent->value.obj = obj->parent->zv;
 -                      zend_objects_store_add_ref_by_handle(
 -                                      obj->parent->zv.handle TSRMLS_CC);
 -                      value = get_proxied_value(parent TSRMLS_CC);
 -                      zval_ptr_dtor(&parent);
++      obj = get_propro(object);
++      debug_propro(1, "parent_get", obj, NULL, NULL);
++
+       if (obj->proxy) {
 -      return value;
++              if (!Z_ISUNDEF(obj->parent)) {
++                      get_proxied_value(&obj->parent, return_value);
+               }
+       }
 -static zval *get_proxied_value(zval *object TSRMLS_DC)
++      debug_propro(-1, "parent_get", obj, NULL, return_value);
++
++      return return_value;
+ }
 -      zval **hash_value, *value = NULL;
++static zval *get_proxied_value(zval *object, zval *return_value)
+ {
 -      ZEND_RESULT_CODE rv;
++      zval *hash_value, *ref, prop_tmp;
+       php_property_proxy_object_t *obj;
 -      obj = zend_object_store_get_object(object TSRMLS_CC);
 -      debug_propro(1, "get", object, NULL, NULL TSRMLS_CC);
 -              if (obj->parent) {
 -                      zval *parent_value = get_parent_proxied_value(object TSRMLS_CC);
++      obj = get_propro(object);
++      debug_propro(1, "get", obj, NULL, NULL);
+       if (obj->proxy) {
 -                      if (parent_value && parent_value != obj->proxy->container) {
 -                              Z_ADDREF_P(parent_value);
++              if (!Z_ISUNDEF(obj->parent)) {
++                      zval parent_value;
 -                              obj->proxy->container = parent_value;
++                      ZVAL_UNDEF(&parent_value);
++                      get_parent_proxied_value(object, &parent_value);
++
++                      if (got_value(&obj->proxy->container, &parent_value)) {
+                               zval_ptr_dtor(&obj->proxy->container);
 -              switch (Z_TYPE_P(obj->proxy->container)) {
++                              ZVAL_COPY(&obj->proxy->container, &parent_value);
+                       }
+               }
 -                      value = zend_read_property(Z_OBJCE_P(obj->proxy->container),
 -                                      obj->proxy->container, obj->proxy->member_str,
 -                                      obj->proxy->member_len, 0 TSRMLS_CC);
++
++              ref = get_referenced_zval(&obj->proxy->container);
++
++              switch (Z_TYPE_P(ref)) {
+               case IS_OBJECT:
 -                      rv = zend_symtable_find(Z_ARRVAL_P(obj->proxy->container),
 -                                      obj->proxy->member_str, obj->proxy->member_len + 1,
 -                                      (void *) &hash_value);
++                      RETVAL_ZVAL(zend_read_property(Z_OBJCE_P(ref), ref,
++                                      obj->proxy->member->val, obj->proxy->member->len, 0, &prop_tmp),
++                                      0, 0);
+                       break;
+               case IS_ARRAY:
 -                      if (SUCCESS == rv) {
 -                              value = *hash_value;
++                      hash_value = zend_symtable_find(Z_ARRVAL_P(ref), obj->proxy->member);
 -      debug_propro(-1, "get", object, NULL, value TSRMLS_CC);
++                      if (hash_value) {
++                              RETVAL_ZVAL(hash_value, 0, 0);
+                       }
+                       break;
+               }
+       }
 -      return value;
++      debug_propro(-1, "get", obj, NULL, return_value);
 -              int type TSRMLS_DC)
++      return return_value;
+ }
+ static ZEND_RESULT_CODE cast_proxied_value(zval *object, zval *return_value,
 -      zval *proxied_value;
++              int type)
+ {
 -      if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
 -              RETVAL_ZVAL(proxied_value, 1, 0);
 -              if (Z_TYPE_P(proxied_value) != type) {
 -                      convert_to_explicit_type(return_value, type);
 -              }
++      get_proxied_value(object, return_value);
 -static void set_proxied_value(zval **object, zval *value TSRMLS_DC)
++      debug_propro(0, "cast", get_propro(object), NULL, return_value);
++
++      if (!Z_ISUNDEF_P(return_value)) {
++              convert_to_explicit_type_ex(return_value, type);
+               return SUCCESS;
+       }
+       return FAILURE;
+ }
 -      obj = zend_object_store_get_object(*object TSRMLS_CC);
 -      debug_propro(1, "set", *object, NULL, value TSRMLS_CC);
++static void set_proxied_value(zval *object, zval *value)
+ {
+       php_property_proxy_object_t *obj;
++      zval *ref;
 -              if (obj->parent) {
 -                      zval *parent_value = get_parent_proxied_value(*object TSRMLS_CC);
++      obj = get_propro(object);
++      debug_propro(1, "set", obj, NULL, value TSRMLS_CC);
+       if (obj->proxy) {
 -                      if (parent_value && parent_value != obj->proxy->container) {
 -                              Z_ADDREF_P(parent_value);
++              if (!Z_ISUNDEF(obj->parent)) {
++                      zval parent_value;
++
++                      ZVAL_UNDEF(&parent_value);
++                      get_parent_proxied_value(object, &parent_value);
 -                              obj->proxy->container = parent_value;
++                      if (got_value(&obj->proxy->container, &parent_value)) {
+                               zval_ptr_dtor(&obj->proxy->container);
 -              switch (Z_TYPE_P(obj->proxy->container)) {
++                              ZVAL_COPY(&obj->proxy->container, &parent_value);
+                       }
+               }
 -                      zend_update_property(Z_OBJCE_P(obj->proxy->container),
 -                                      obj->proxy->container, obj->proxy->member_str,
 -                                      obj->proxy->member_len, value TSRMLS_CC);
++              ref = get_referenced_zval(&obj->proxy->container);
++
++              switch (Z_TYPE_P(ref)) {
+               case IS_OBJECT:
 -                      Z_ADDREF_P(value);
 -                      zend_symtable_update(Z_ARRVAL_P(obj->proxy->container),
 -                                      obj->proxy->member_str, obj->proxy->member_len + 1,
 -                                      (void *) &value, sizeof(zval *), NULL);
++                      zend_update_property(Z_OBJCE_P(ref), ref, obj->proxy->member->val,
++                                      obj->proxy->member->len, value);
+                       break;
++              default:
++                      convert_to_array(ref);
++                      /* no break */
++
+               case IS_ARRAY:
 -              if (obj->parent) {
 -                      zval *zparent;
 -                      MAKE_STD_ZVAL(zparent);
 -                      zparent->type = IS_OBJECT;
 -                      zparent->value.obj = obj->parent->zv;
 -                      zend_objects_store_add_ref_by_handle(
 -                                      obj->parent->zv.handle TSRMLS_CC);
 -                      set_proxied_value(&zparent, obj->proxy->container TSRMLS_CC);
 -                      zval_ptr_dtor(&zparent);
++                      Z_TRY_ADDREF_P(value);
++                      zend_symtable_update(Z_ARRVAL_P(ref), obj->proxy->member, value);
+                       break;
+               }
 -      debug_propro(-1, "set", *object, NULL, NULL TSRMLS_CC);
++              if (!Z_ISUNDEF(obj->parent)) {
++                      set_proxied_value(&obj->parent, &obj->proxy->container);
+               }
+       }
 -static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
++      debug_propro(-1, "set", obj, NULL, NULL);
+ }
 -      zval *value = NULL;
 -      zval *proxied_value;
 -      zval *o = offset;
++static zval *read_dimension(zval *object, zval *offset, int type, zval *return_value)
+ {
 -      debug_propro(1, type == BP_VAR_R ? "dim_read" : "dim_read_ref", object,
 -                      offset, NULL TSRMLS_CC);
++      zval proxied_value;
++      zend_string *member = offset ? zval_get_string(offset) : NULL;
 -      proxied_value = get_proxied_value(object TSRMLS_CC);
 -      convert_to_string_ex(&o);
++      debug_propro(1, type == BP_VAR_R ? "dim_read" : "dim_read_ref",
++                      get_propro(object), offset, NULL);
 -      if (BP_VAR_R == type && proxied_value) {
 -              if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
 -                      zval **hash_value;
 -                      ZEND_RESULT_CODE rv = zend_symtable_find(Z_ARRVAL_P(proxied_value),
 -                                      Z_STRVAL_P(o), Z_STRLEN_P(o), (void *) &hash_value);
++      ZVAL_UNDEF(&proxied_value);
++      get_proxied_value(object, &proxied_value);
 -                      if (SUCCESS == rv) {
 -                              Z_ADDREF_PP(hash_value);
 -                              value = *hash_value;
++      if (BP_VAR_R == type && member && !Z_ISUNDEF(proxied_value)) {
++              if (Z_TYPE(proxied_value) == IS_ARRAY) {
++                      zval *hash_value = zend_symtable_find(Z_ARRVAL(proxied_value),
++                                      member);
 -              if (proxied_value) {
 -                      convert_to_array(proxied_value);
 -                      Z_ADDREF_P(proxied_value);
++                      if (hash_value) {
++                              RETVAL_ZVAL(hash_value, 1, 0);
+                       }
+               }
+       } else {
+               php_property_proxy_t *proxy;
+               php_property_proxy_object_t *proxy_obj;
 -                      MAKE_STD_ZVAL(proxied_value);
 -                      array_init(proxied_value);
 -                      set_proxied_value(&object, proxied_value TSRMLS_CC);
++              if (!Z_ISUNDEF(proxied_value)) {
++                      convert_to_array(&proxied_value);
++                      Z_ADDREF(proxied_value);
+               } else {
 -              proxy = php_property_proxy_init(proxied_value, Z_STRVAL_P(o),
 -                              Z_STRLEN_P(o) TSRMLS_CC);
++                      array_init(&proxied_value);
++                      set_proxied_value(object, &proxied_value);
++              }
++
++              if (!member) {
++                      member = zend_long_to_str(zend_hash_next_free_element(
++                                      Z_ARRVAL(proxied_value)));
+               }
 -              MAKE_STD_ZVAL(value);
 -              Z_SET_REFCOUNT_P(value, 0);
 -              value->type = IS_OBJECT;
 -              value->value.obj = php_property_proxy_object_new_ex(
 -                              php_property_proxy_get_class_entry(), proxy,
 -                              &proxy_obj TSRMLS_CC);
 -              proxy_obj->parent = zend_object_store_get_object(object TSRMLS_CC);
 -              zend_objects_store_add_ref_by_handle(
 -                              proxy_obj->parent->zv.handle TSRMLS_CC);
++              proxy = php_property_proxy_init(&proxied_value, member);
+               zval_ptr_dtor(&proxied_value);
 -      if (o && o != offset) {
 -              zval_ptr_dtor(&o);
++
++              proxy_obj = php_property_proxy_object_new_ex(NULL, proxy);
++              ZVAL_COPY(&proxy_obj->parent, object);
++              RETVAL_OBJ(&proxy_obj->zo);
+       }
 -      debug_propro(-1, type == BP_VAR_R ? "dim_read" : "dim_read_ref", object,
 -                      offset, value TSRMLS_CC);
++
++      if (member) {
++              zend_string_release(member);
+       }
 -      return value;
++      debug_propro(-1, type == BP_VAR_R ? "dim_read" : "dim_read_ref",
++                      get_propro(object), offset, return_value);
 -static int has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC)
++      return return_value;
+ }
 -      zval *proxied_value;
++static int has_dimension(zval *object, zval *offset, int check_empty)
+ {
 -      debug_propro(1, "dim_exists", object, offset, NULL TSRMLS_CC);
++      zval proxied_value;
+       int exists = 0;
 -      proxied_value = get_proxied_value(object TSRMLS_CC);
 -      if (!proxied_value) {
++      debug_propro(1, "dim_exists", get_propro(object), offset, NULL);
 -              zval *o = offset;
++      ZVAL_UNDEF(&proxied_value);
++      get_proxied_value(object, &proxied_value);
++      if (Z_ISUNDEF(proxied_value)) {
+               exists = 0;
+       } else {
 -              convert_to_string_ex(&o);
++              zend_string *zs = zval_get_string(offset);
 -              if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
 -                      zval **zentry;
 -                      ZEND_RESULT_CODE rv = zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &zentry);
 -
 -                      if (SUCCESS != rv) {
++              if (Z_TYPE(proxied_value) == IS_ARRAY) {
++                      zval *zentry = zend_symtable_find(Z_ARRVAL(proxied_value), zs);
 -                                      exists = Z_TYPE_PP(zentry) != IS_NULL;
++                      if (!zentry) {
+                               exists = 0;
+                       } else {
+                               if (check_empty) {
 -              if (o != offset) {
 -                      zval_ptr_dtor(&o);
 -              }
++                                      exists = !Z_ISNULL_P(zentry);
+                               } else {
+                                       exists = 1;
+                               }
+                       }
+               }
 -      debug_propro(-1, "dim_exists", object, offset, NULL TSRMLS_CC);
++              zend_string_release(zs);
+       }
 -static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
++      debug_propro(-1, "dim_exists", get_propro(object), offset, NULL);
+       return exists;
+ }
 -      zval *proxied_value, *o = offset;
++static void write_dimension(zval *object, zval *offset, zval *value)
+ {
 -      debug_propro(1, "dim_write", object, offset, value TSRMLS_CC);
++      zval proxied_value;
 -      proxied_value = get_proxied_value(object TSRMLS_CC);
++      debug_propro(1, "dim_write", get_propro(object), offset, value);
 -      if (proxied_value) {
 -              convert_to_array(proxied_value);
 -              Z_ADDREF_P(proxied_value);
++      ZVAL_UNDEF(&proxied_value);
++      get_proxied_value(object, &proxied_value);
 -              MAKE_STD_ZVAL(proxied_value);
 -              array_init(proxied_value);
++      if (!Z_ISUNDEF(proxied_value)) {
++              if (Z_TYPE(proxied_value) == IS_ARRAY) {
++                      Z_ADDREF(proxied_value);
++              } else {
++                      convert_to_array(&proxied_value);
++              }
+       } else {
 -      if (Z_REFCOUNT_P(value) > 1) {
 -              SEPARATE_ZVAL(&value);
 -      }
 -      Z_ADDREF_P(value);
++              array_init(&proxied_value);
+       }
 -      if (o) {
 -              convert_to_string_ex(&o);
 -              zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o),
 -                              Z_STRLEN_P(o) + 1, (void *) &value, sizeof(zval *), NULL);
++      SEPARATE_ZVAL(value);
++      Z_TRY_ADDREF_P(value);
 -              zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value), (void *) &value,
 -                              sizeof(zval *), NULL);
++      if (offset) {
++              zend_string *zs = zval_get_string(offset);
++              zend_symtable_update(Z_ARRVAL(proxied_value), zs, value);
++              zend_string_release(zs);
+       } else {
 -      if (o && o != offset) {
 -              zval_ptr_dtor(&o);
 -      }
++              zend_hash_next_index_insert(Z_ARRVAL(proxied_value), value);
+       }
 -      set_proxied_value(&object, proxied_value TSRMLS_CC);
 -
 -      debug_propro(-1, "dim_write", object, offset, proxied_value TSRMLS_CC);
++      set_proxied_value(object, &proxied_value);
 -static void unset_dimension(zval *object, zval *offset TSRMLS_DC)
++      debug_propro(-1, "dim_write", get_propro(object), offset, &proxied_value);
+       zval_ptr_dtor(&proxied_value);
+ }
 -      zval *proxied_value;
++static void unset_dimension(zval *object, zval *offset)
+ {
 -      debug_propro(1, "dim_unset", object, offset, NULL TSRMLS_CC);
++      zval proxied_value;
 -      proxied_value = get_proxied_value(object TSRMLS_CC);
++      debug_propro(1, "dim_unset", get_propro(object), offset, NULL);
 -      if (proxied_value && Z_TYPE_P(proxied_value) == IS_ARRAY) {
++      ZVAL_UNDEF(&proxied_value);
++      get_proxied_value(object, &proxied_value);
 -              convert_to_string_ex(&o);
 -              rv = zend_symtable_del(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o),
 -                              Z_STRLEN_P(o) + 1);
 -
++      if (Z_TYPE(proxied_value) == IS_ARRAY) {
+               zval *o = offset;
+               ZEND_RESULT_CODE rv;
 -                      set_proxied_value(&object, proxied_value TSRMLS_CC);
++              convert_to_string_ex(o);
++              rv = zend_symtable_del(Z_ARRVAL(proxied_value), Z_STR_P(o));
+               if (SUCCESS == rv) {
 -                      zval_ptr_dtor(&o);
++                      set_proxied_value(object, &proxied_value);
+               }
+               if (o != offset) {
 -      debug_propro(-1, "dim_unset", object, offset, proxied_value TSRMLS_CC);
++                      zval_ptr_dtor(o);
+               }
+       }
 -      char *member_str;
 -      int member_len;
++      debug_propro(-1, "dim_unset", get_propro(object), offset, &proxied_value);
+ }
+ ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct, 0, 0, 2)
+       ZEND_ARG_INFO(1, object)
+       ZEND_ARG_INFO(0, member)
+       ZEND_ARG_OBJ_INFO(0, parent, php\\PropertyProxy, 1)
+ ZEND_END_ARG_INFO();
+ static PHP_METHOD(propro, __construct) {
+       zend_error_handling zeh;
+       zval *container, *parent = NULL;
 -      zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
 -      if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|O!",
 -                      &container, &member_str, &member_len, &parent,
++      zend_string *member;
 -              obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 -              obj->proxy = php_property_proxy_init(container, member_str,
 -                              member_len TSRMLS_CC);
++      zend_replace_error_handling(EH_THROW, NULL, &zeh);
++      if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "zS|O!",
++                      &container, &member, &parent,
+                       php_property_proxy_class_entry)) {
+               php_property_proxy_object_t *obj;
++              zval *ref = get_referenced_zval(container);
 -                      zend_objects_store_add_ref(parent TSRMLS_CC);
 -                      obj->parent = zend_object_store_get_object(parent TSRMLS_CC);
++              switch (Z_TYPE_P(ref)) {
++              case IS_OBJECT:
++              case IS_ARRAY:
++                      break;
++              default:
++                      convert_to_array(ref);
++              }
++              obj = get_propro(getThis());
++              obj->proxy = php_property_proxy_init(container, member);
+               if (parent) {
 -      zend_restore_error_handling(&zeh TSRMLS_CC);
++                      ZVAL_COPY(&obj->parent, parent);
+               }
+       }
 -      php_property_proxy_class_entry = zend_register_internal_class_ex(&ce, NULL,
 -                      NULL TSRMLS_CC);
 -      php_property_proxy_class_entry->create_object =
 -                      php_property_proxy_object_new;
 -      php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL_CLASS;
++      zend_restore_error_handling(&zeh);
+ }
+ static const zend_function_entry php_property_proxy_method_entry[] = {
+       PHP_ME(propro, __construct, ai_propro_construct, ZEND_ACC_PUBLIC)
+       {0}
+ };
+ static PHP_MINIT_FUNCTION(propro)
+ {
+       zend_class_entry ce = {0};
+       INIT_NS_CLASS_ENTRY(ce, "php", "PropertyProxy",
+                       php_property_proxy_method_entry);
++      php_property_proxy_class_entry = zend_register_internal_class(&ce);
++      php_property_proxy_class_entry->create_object = php_property_proxy_object_new;
++      php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL;
+       memcpy(&php_property_proxy_object_handlers, zend_get_std_object_handlers(),
+                       sizeof(zend_object_handlers));
++      php_property_proxy_object_handlers.offset = XtOffsetOf(php_property_proxy_object_t, zo);
++      php_property_proxy_object_handlers.free_obj = destroy_obj;
+       php_property_proxy_object_handlers.set = set_proxied_value;
+       php_property_proxy_object_handlers.get = get_proxied_value;
+       php_property_proxy_object_handlers.cast_object = cast_proxied_value;
+       php_property_proxy_object_handlers.read_dimension = read_dimension;
+       php_property_proxy_object_handlers.write_dimension = write_dimension;
+       php_property_proxy_object_handlers.has_dimension = has_dimension;
+       php_property_proxy_object_handlers.unset_dimension = unset_dimension;
+       return SUCCESS;
+ }
+ PHP_MINFO_FUNCTION(propro)
+ {
+       php_info_print_table_start();
+       php_info_print_table_header(2, "Property proxy support", "enabled");
+       php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION);
+       php_info_print_table_end();
+ }
+ static const zend_function_entry propro_functions[] = {
+       {0}
+ };
+ zend_module_entry propro_module_entry = {
+       STANDARD_MODULE_HEADER,
+       "propro",
+       propro_functions,
+       PHP_MINIT(propro),
+       NULL,
+       NULL,
+       NULL,
+       PHP_MINFO(propro),
+       PHP_PROPRO_VERSION,
+       STANDARD_MODULE_PROPERTIES
+ };
+ #ifdef COMPILE_DL_PROPRO
+ ZEND_GET_MODULE(propro)
+ #endif
+ /*
+  * Local variables:
+  * tab-width: 4
+  * c-basic-offset: 4
+  * End:
+  * vim600: noet sw=4 ts=4 fdm=marker
+  * vim<600: noet sw=4 ts=4
+  */
index 0000000000000000000000000000000000000000,a2add4fff791527fc18ec396d900da38ce7edc62..d82055ffa6d5c01b7bb98e02796221a66d2bf2c5
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,148 +1,128 @@@
 -      zval *container;
+ /*
+     +--------------------------------------------------------------------+
+     | PECL :: propro                                                     |
+     +--------------------------------------------------------------------+
+     | Redistribution and use in source and binary forms, with or without |
+     | modification, are permitted provided that the conditions mentioned |
+     | in the accompanying LICENSE file are met.                          |
+     +--------------------------------------------------------------------+
+     | Copyright (c) 2013 Michael Wallner <mike@php.net>                  |
+     +--------------------------------------------------------------------+
+ */
+ #ifndef PHP_PROPRO_API_H
+ #define PHP_PROPRO_API_H
+ #include "php_propro.h"
+ /**
+  * The internal property proxy.
+  *
+  * Container for the object/array holding the proxied property.
+  */
+ struct php_property_proxy {
+       /** The container holding the property */
 -      char *member_str;
 -      /** The length of the name */
 -      size_t member_len;
++      zval container;
+       /** The name of the proxied property */
 - * static zval *my_read_prop(zval *object, zval *member, int type, zend_literal *key TSRMLS_DC)
++      zend_string *member;
+ };
+ typedef struct php_property_proxy php_property_proxy_t;
+ /**
+  * The userland object.
+  *
+  * Return an object instance of php\\PropertyProxy to make your C-struct
+  * member accessible by reference from PHP userland.
+  *
+  * Example:
+  * ~~~~~~~~~~{.c}
 - *     my_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
 - *     my_prophandler_t *handler;
 - *     zval *return_value, *copy = my_cast(IS_STRING, member);
++ * static zval *my_read_prop(zval *object, zval *member, int type, void **cache_slot, zval *tmp)
+  * {
 - *     if (SUCCESS == my_get_prophandler(Z_STRVAL_P(copy), Z_STRLEN_P(copy), &handler)) {
 - *         ALLOC_ZVAL(return_value);
 - *         Z_SET_REFCOUNT_P(return_value, 0);
 - *         Z_UNSET_ISREF_P(return_value);
++ *    zval *return_value;
++ *    zend_string *member_name = zval_get_string(member);
++ *    my_prophandler_t *handler = my_get_prophandler(member_name);
+  *
 - *         if (type == BP_VAR_R) {
 - *             handler->read(obj, return_value TSRMLS_CC);
 - *         } else {
 - *             //
 - *             // This is the interesting part
 - *             //
 - *             php_property_proxy_t *proxy;
 - *             zend_object_value proxy_ov;
 - *             zend_class_entry *proxy_ce;
++ *    if (!handler || type == BP_VAR_R || type == BP_VAR_IS) {
++ *            return_value = zend_get_std_object_handlers()->read_property(object, member, type, cache_slot, tmp);
+  *
 - *             proxy = php_property_proxy_init(object, Z_STRVAL_P(copy), Z_STRLEN_P(copy) TSRMLS_CC);
 - *             proxy_ce = php_property_proxy_get_class_entry();
 - *             proxy_ov = php_property_proxy_object_new_ex(proxy_ce, proxy, NULL TSRMLS_CC);
 - *             RETVAL_OBJVAL(proxy_ov, 0);
 - *         }
 - *     } else {
 - *         zend_object_handlers *oh = zend_get_std_object_handlers();
 - *         return_value = oh->read_property(object, member, type, key TSRMLS_CC);
 - *     }
++ *            if (handler) {
++ *                    handler->read(object, tmp);
+  *
 - *     zval_ptr_dtor(&copy);
++ *                    zval_ptr_dtor(return_value);
++ *                    ZVAL_COPY_VALUE(return_value, tmp);
++ *            }
++ *    } else {
++ *            return_value = php_property_proxy_zval(object, member_name);
++ *    }
+  *
 - *     return return_value;
++ *    zend_string_release(member_name);
+  *
 -      /** The std zend_object */
 -      zend_object zo;
 -      /** The object value for easy zval creation */
 -      zend_object_value zv;
++ *    return return_value;
+  * }
+  * ~~~~~~~~~~
+  */
+ struct php_property_proxy_object {
 -      /** A reference to any parent property proxy object */
 -      struct php_property_proxy_object *parent;
+       /** The actual property proxy */
+       php_property_proxy_t *proxy;
 - * @param member_str the name of the proxied property
 - * @param member_len the length of the name
++      /** Any parent property proxy object */
++      zval parent;
++      /** The std zend_object */
++      zend_object zo;
+ };
+ typedef struct php_property_proxy_object php_property_proxy_object_t;
+ /**
+  * Create a property proxy
+  *
+  * The property proxy will forward reads and writes to itself to the
+  * proxied property with name \a member_str of \a container.
+  *
+  * @param container the container holding the property
 -              const char *member_str, size_t member_len TSRMLS_DC);
++ * @param member the name of the proxied property
+  * @return a new property proxy
+  */
+ PHP_PROPRO_API php_property_proxy_t *php_property_proxy_init(zval *container,
 - * @return the zval object value
++              zend_string *member);
+ /**
+  * Destroy and free a property proxy.
+  *
+  * The destruction of the property proxy object calls this.
+  *
+  * @param proxy a pointer to the allocated property proxy
+  */
+ PHP_PROPRO_API void php_property_proxy_free(php_property_proxy_t **proxy);
+ /**
+  * Get the zend_class_entry of php\\PropertyProxy
+  * @return the class entry pointer
+  */
+ PHP_PROPRO_API zend_class_entry *php_property_proxy_get_class_entry(void);
+ /**
+  * Instantiate a new php\\PropertyProxy
+  * @param ce the property proxy or derived class entry
 -PHP_PROPRO_API zend_object_value php_property_proxy_object_new(
 -              zend_class_entry *ce TSRMLS_DC);
++ * @return the zend object
+  */
 - * @param ptr a pointer to store the resulting property proxy object
 - * @return the zval object value
++PHP_PROPRO_API zend_object *php_property_proxy_object_new(zend_class_entry *ce);
+ /**
+  * Instantiate a new php\\PropertyProxy with \a proxy
+  * @param ce the property proxy or derived class entry
+  * @param proxy the internal property proxy
 -PHP_PROPRO_API zend_object_value php_property_proxy_object_new_ex(
 -              zend_class_entry *ce, php_property_proxy_t *proxy,
 -              php_property_proxy_object_t **ptr TSRMLS_DC);
++ * @return the property proxy
+  */
++PHP_PROPRO_API php_property_proxy_object_t *php_property_proxy_object_new_ex(
++              zend_class_entry *ce, php_property_proxy_t *proxy);
+ #endif        /* PHP_PROPRO_API_H */
+ /*
+  * Local variables:
+  * tab-width: 4
+  * c-basic-offset: 4
+  * End:
+  * vim600: noet sw=4 ts=4 fdm=marker
+  * vim<600: noet sw=4 ts=4
+  */