+static inline php_property_proxy_object_t *get_propro(zval *object);
+static inline zval *get_proxied_value(zval *object, zval *return_value);
+static inline void set_proxied_value(zval *object, zval *value);
+
+static zval *read_dimension(zval *object, zval *offset, int type, zval *return_value);
+static ZEND_RESULT_CODE cast_obj(zval *object, zval *return_value, int type);
+static void write_dimension(zval *object, zval *offset, zval *input_value);
+
+#if DEBUG_PROPRO
+/* we do not really care about TS when debugging */
+static int level = 1;
+static const char *inoutstr[] = {"< return","= "," > enter "};
+static const char *types[] = {
+ "UNDEF",
+ "NULL",
+ "FALSE",
+ "TRUE",
+ "int",
+ "float",
+ "string",
+ "Array",
+ "Object",
+ "resource",
+ "reference",
+ "constant",
+ "const AST",
+ "_BOOL",
+ "callable",
+ "indirect",
+ "---",
+ "pointer"
+};
+
+static const char *_type(zval *zv)
+{
+ switch (Z_TYPE_P(zv)) {
+ case IS_OBJECT:
+ if (zv->value.obj->ce == php_property_proxy_get_class_entry()) {
+ return "PROPRO";
+ }
+ /* no break */
+ default:
+ return types[Z_TYPE_P(zv)];
+ }
+}
+
+static int _walk(php_property_proxy_object_t *obj)
+{
+ int p = 0;
+
+ if (obj) {
+ if (!Z_ISUNDEF(obj->parent)) {
+ p += _walk(get_propro(&obj->parent));
+ }
+ if (obj->proxy) {
+ p += fprintf(stderr, ".%s", obj->proxy->member->val);
+ }
+ }
+
+ return p;
+}
+
+static inline zval *get_container(zval *object, zval *tmp);
+static inline zval *get_container_value(zval *container, zend_string *member, zval *return_value);
+
+static void debug_propro(int inout, const char *f,
+ php_property_proxy_object_t *obj,
+ php_property_proxy_t *proxy,
+ zval *offset, zval *value)
+{
+ int width;
+ zval tmp, *container = &tmp;
+
+ if (!proxy && obj) {
+ proxy = obj->proxy;
+ }
+
+ fprintf(stderr, "#PP %14p %*c %s %s\t", proxy, level, ' ', inoutstr[inout + 1], f);
+
+ level += inout;
+
+ if (proxy) {
+ ZVAL_UNDEF(&tmp);
+ if (!obj) {
+ container = &proxy->container;
+ } else if (Z_ISUNDEF(obj->parent)) {
+ container = &obj->proxy->container;
+ }
+ fprintf(stderr, " container= %-14p < %-10s rc=%-2d%s> ",
+ Z_REFCOUNTED_P(container) ? Z_COUNTED_P(container) : NULL,
+ _type(container),
+ Z_REFCOUNTED_P(container) ? Z_REFCOUNT_P(container) : 0,
+ Z_ISREF_P(container) ? "(ref) ":"(noref)");
+ }
+
+ width = _walk(obj);
+
+ if (*f++=='d'
+ && *f++=='i'
+ && *f++=='m'
+ ) {
+ char *offset_str = "[]";
+ zval *o = offset;
+
+ if (o) {
+ convert_to_string_ex(o);
+ offset_str = Z_STRVAL_P(o);
+ }
+
+ width += fprintf(stderr, ".%s", offset_str);
+
+ if (o && o != offset) {
+ zval_ptr_dtor(o);
+ }
+ }
+ if (value) {
+ fprintf(stderr, "%.*s", 32-width, " ");
+ if (Z_ISUNDEF_P(value)) {
+ fprintf(stderr, " = UNDEF");
+ } else {
+ fprintf(stderr, " = %-14p < %-10s rc=%-2d%s> ",
+ Z_REFCOUNTED_P(value) ? Z_COUNTED_P(value) : NULL,
+ _type(value),
+ Z_REFCOUNTED_P(value) ? Z_REFCOUNT_P(value) : 0,
+ Z_ISREF_P(value) ? "(ref) " : "(noref)");
+ 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, proxy, off, val)