[config.w32] Warn that propro was discontinued
[m6w6/ext-propro] / src / php_propro_api.c
index 325fb5ca73f83bf40b5015df9e939c12ee610f98..631f707c3a9a77d9944c77240851a0df6ff4489e 100644 (file)
@@ -47,7 +47,7 @@ static const char *types[] = {
                "resource",
                "reference",
                "constant",
-               "constant AST",
+               "const AST",
                "_BOOL",
                "callable",
                "indirect",
@@ -55,6 +55,19 @@ static const char *types[] = {
                "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;
@@ -71,12 +84,16 @@ static int _walk(php_property_proxy_object_t *obj)
        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;
@@ -87,18 +104,17 @@ static void debug_propro(int inout, const char *f,
        level += inout;
 
        if (proxy) {
-               fprintf(stderr, " container= %-10p <%12s rc=%d ",
-                               Z_REFCOUNTED(proxy->container) ? Z_COUNTED(proxy->container) : NULL,
-                               types[Z_TYPE(proxy->container)],
-                               Z_REFCOUNTED(proxy->container) ? Z_REFCOUNT(proxy->container) : 0);
-               if (Z_ISREF(proxy->container)) {
-                       zval *ref = Z_REFVAL(proxy->container);
-                       fprintf(stderr, " %-12s %p rc=% 2d",
-                                       types[Z_TYPE_P(ref)],
-                                       ref->value.counted,
-                                       Z_REFCOUNTED_P(ref) ? Z_REFCOUNT_P(ref) : -1);
+               ZVAL_UNDEF(&tmp);
+               if (!obj) {
+                       container = &proxy->container;
+               } else if (Z_ISUNDEF(obj->parent)) {
+                       container = &obj->proxy->container;
                }
-               fprintf(stderr, "> ");
+               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);
@@ -124,13 +140,13 @@ static void debug_propro(int inout, const char *f,
        if (value) {
                fprintf(stderr, "%.*s", 32-width, "                                ");
                if (Z_ISUNDEF_P(value)) {
-                       fprintf(stderr, " =UNDEF                              ");
+                       fprintf(stderr, " = UNDEF");
                } else {
-                       fprintf(stderr, " = %-10p <%12s rc=%d %3s> ",
+                       fprintf(stderr, " = %-14p < %-10s rc=%-2d%s> ",
                                        Z_REFCOUNTED_P(value) ? Z_COUNTED_P(value) : NULL,
-                                       types[Z_TYPE_P(value)&0xf],
+                                       _type(value),
                                        Z_REFCOUNTED_P(value) ? Z_REFCOUNT_P(value) : 0,
-                                       Z_ISREF_P(value) ? "ref" : "");
+                                       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);
                        }
@@ -152,12 +168,12 @@ php_property_proxy_t *php_property_proxy_init(zval *container, zend_string *memb
        ZVAL_STR_COPY(&offset, member);
 #endif
 
-       debug_propro(1, "init", NULL, proxy, &offset, NULL);
-
-       ZVAL_COPY(&proxy->container, container);
+       if (container) {
+               ZVAL_COPY(&proxy->container, container);
+       }
        proxy->member = zend_string_copy(member);
 
-       debug_propro(-1, "init", NULL, proxy, &offset, NULL);
+       debug_propro(0, "init", NULL, proxy, &offset, NULL);
 
 #if DEBUG_PROPRO
        zval_dtor(&offset);
@@ -172,13 +188,14 @@ void php_property_proxy_free(php_property_proxy_t **proxy)
        zval offset;
 
        ZVAL_STR_COPY(&offset, (*proxy)->member);
-       debug_propro(1, "dtor", NULL, *proxy, &offset, NULL);
+       debug_propro(0, "free", NULL, *proxy, &offset, NULL);
 #endif
 
        if (*proxy) {
-               zval_ptr_dtor(&(*proxy)->container);
-
-               ZVAL_UNDEF(&(*proxy)->container);
+               if (!Z_ISUNDEF((*proxy)->container)) {
+                       zval_ptr_dtor(&(*proxy)->container);
+                       ZVAL_UNDEF(&(*proxy)->container);
+               }
                zend_string_release((*proxy)->member);
                (*proxy)->member = NULL;
                efree(*proxy);
@@ -186,8 +203,6 @@ void php_property_proxy_free(php_property_proxy_t **proxy)
        }
 
 #if DEBUG_PROPRO
-       debug_propro(-1, "dtor", NULL, NULL, &offset, NULL);
-
        zval_dtor(&offset);
 #endif
 }
@@ -238,6 +253,49 @@ static void destroy_obj(zend_object *object)
        zend_object_std_dtor(object);
 }
 
+static inline php_property_proxy_object_t *get_propro(zval *object)
+{
+       ZEND_ASSERT(Z_TYPE_P(object) == IS_OBJECT);
+       return PHP_PROPRO_PTR(Z_OBJ_P(object));
+}
+
+static HashTable *get_gc(zval *object, zval **table, int *n)
+{
+       php_property_proxy_object_t *o = get_propro(object);
+
+       if (Z_ISUNDEF(o->parent)) {
+               *table = &o->proxy->container;
+       } else {
+               *table = &o->parent;
+       }
+       *n = 1;
+       return NULL;
+}
+
+static HashTable *get_debug_info(zval *object, int *is_temp)
+{
+       HashTable *ht;
+       zval *zmember;
+       php_property_proxy_object_t *obj = get_propro(object);
+
+       ALLOC_HASHTABLE(ht);
+       zend_hash_init(ht, 3, NULL, NULL, 0);
+
+       if (Z_ISUNDEF(obj->parent)) {
+               zend_hash_str_add_empty_element(ht, "parent", sizeof("parent")-1);
+               zend_hash_str_add(ht, "container", sizeof("container")-1, &obj->proxy->container);
+       } else {
+               zend_hash_str_add(ht, "parent", sizeof("parent")-1, &obj->parent);
+               zend_hash_str_add_empty_element(ht, "container", sizeof("container")-1);
+       }
+
+       zmember = zend_hash_str_add_empty_element(ht, "member", sizeof("member")-1);
+       ZVAL_STR(zmember, obj->proxy->member);
+
+       *is_temp = 1;
+       return ht;
+}
+
 static ZEND_RESULT_CODE cast_obj(zval *object, zval *return_value, int type)
 {
        zval tmp;
@@ -269,42 +327,33 @@ static void set_obj(zval *object, zval *value) {
        set_proxied_value(object, value);
 }
 
-static inline php_property_proxy_object_t *get_propro(zval *object)
-{
-       ZEND_ASSERT(Z_TYPE_P(object) == IS_OBJECT);
-       return PHP_PROPRO_PTR(Z_OBJ_P(object));
-}
-
-static inline zval *get_container(zval *object)
+static inline zval *get_container(zval *object, zval *tmp)
 {
        php_property_proxy_object_t *obj = get_propro(object);
+       zval *container;
 
-       if (!Z_ISUNDEF(obj->parent)) {
-               zval *parent_value, tmp;
-
-               ZVAL_UNDEF(&tmp);
-               parent_value = get_proxied_value(&obj->parent, &tmp);
-               ZVAL_DEREF(parent_value);
-               switch (Z_TYPE_P(parent_value)) {
-               case IS_OBJECT:
-               case IS_ARRAY:
-                       zend_assign_to_variable(&obj->proxy->container, parent_value, IS_CV);
-                       break;
-               default:
-                       break;
-               }
+       if (Z_ISUNDEF(obj->parent)) {
+               container = &obj->proxy->container;
+       } else {
+               container = get_proxied_value(&obj->parent, tmp);
        }
-
-       return &obj->proxy->container;
+       return container;
 }
 
 static inline void set_container(zval *object, zval *container)
 {
        php_property_proxy_object_t *obj = get_propro(object);
-       zend_assign_to_variable(&obj->proxy->container, container, IS_CV);
 
-       if (!Z_ISUNDEF(obj->parent)) {
-               set_proxied_value(&obj->parent, &obj->proxy->container);
+       if (Z_ISUNDEF(obj->parent)) {
+               if (container != &obj->proxy->container) {
+                       zval tmp;
+
+                       ZVAL_COPY_VALUE(&tmp, &obj->proxy->container);
+                       ZVAL_COPY(&obj->proxy->container, container);
+                       zval_ptr_dtor(&tmp);
+               }
+       } else {
+               set_proxied_value(&obj->parent, container);
        }
 }
 
@@ -315,9 +364,9 @@ static inline zval *get_container_value(zval *container, zend_string *member, zv
        ZVAL_DEREF(container);
        switch (Z_TYPE_P(container)) {
        case IS_OBJECT:
+               ZVAL_UNDEF(&prop_tmp);
                found_value = zend_read_property(Z_OBJCE_P(container), container,
                                member->val, member->len, 0, &prop_tmp);
-
                break;
 
        case IS_ARRAY:
@@ -332,32 +381,62 @@ static inline zval *get_container_value(zval *container, zend_string *member, zv
        return return_value;
 }
 
-static inline void set_container_value(zval *container, zend_string *member, zval *value)
+static inline void cleanup_container(zval *object, zval *container, zend_bool separated)
+{
+       if (separated) {
+               zval_ptr_dtor(container);
+       }
+}
+
+static inline zend_bool separate_container(zval *object, zval *container)
 {
-       ZVAL_DEREF(container);
        switch (Z_TYPE_P(container)) {
        case IS_OBJECT:
-               zend_update_property(Z_OBJCE_P(container), container,
-                               member->val, member->len, value);
+               return 0;
+
+       case IS_ARRAY:
+               /* always duplicate for PHP-7.0 and 7.1 on travis */
+               ZVAL_ARR(container, zend_array_dup(Z_ARRVAL_P(container)));
                break;
 
        case IS_UNDEF:
                array_init(container);
-               /* no break */
+               break;
+
        default:
                SEPARATE_ZVAL(container);
+               Z_TRY_ADDREF_P(container);
                convert_to_array(container);
-               /* no break */
+               break;
+       }
+
+       return 1;
+}
+
+static inline zval *set_container_value(zval *container, zend_string *member, zval *value)
+{
+       ZVAL_DEREF(container);
+       switch (Z_TYPE_P(container)) {
+       case IS_OBJECT:
+               zend_update_property(Z_OBJCE_P(container), container,
+                               member->val, member->len, value);
+               break;
+
        case IS_ARRAY:
-               SEPARATE_ARRAY(container);
                Z_TRY_ADDREF_P(value);
                if (member) {
-                       zend_symtable_update(Z_ARRVAL_P(container), member, value);
+                       value = zend_symtable_update(Z_ARRVAL_P(container), member, value);
                } else {
-                       zend_hash_next_index_insert(Z_ARRVAL_P(container), value);
+                       value = zend_hash_next_index_insert(Z_ARRVAL_P(container), value);
                }
                break;
+
+       default:
+               ZEND_ASSERT(0);
+               break;
        }
+
+       return value;
 }
 
 
@@ -368,7 +447,10 @@ static zval *get_proxied_value(zval *object, zval *return_value)
        debug_propro(1, "get", obj, NULL, NULL, NULL);
 
        if (obj->proxy) {
-               zval *container = get_container(object);
+               zval tmp, *container;
+
+               ZVAL_UNDEF(&tmp);
+               container = get_container(object, &tmp);
 
                return_value = get_container_value(container, obj->proxy->member, return_value);
        }
@@ -385,10 +467,19 @@ static void set_proxied_value(zval *object, zval *value)
        debug_propro(1, "set", obj, NULL, NULL, value);
 
        if (obj->proxy) {
-               zval *container = get_container(object);
+               zval tmp, *container;
+               zend_bool separated;
 
+               Z_TRY_ADDREF_P(value);
+
+               ZVAL_UNDEF(&tmp);
+               container = get_container(object, &tmp);
+               separated = separate_container(object, container);
                set_container_value(container, obj->proxy->member, value);
                set_container(object, container);
+               cleanup_container(object, container, separated);
+
+               Z_TRY_DELREF_P(value);
 
                debug_propro(0, "set", obj, NULL, NULL, value);
        }
@@ -407,7 +498,7 @@ static zval *read_dimension(zval *object, zval *offset, int type, zval *return_v
        ZVAL_UNDEF(&tmp);
        value = get_proxied_value(object, &tmp);
 
-       if (type == BP_VAR_R) {
+       if (type == BP_VAR_R || type == BP_VAR_IS) {
                ZEND_ASSERT(member);
 
                if (!Z_ISUNDEF_P(value)) {
@@ -419,38 +510,26 @@ static zval *read_dimension(zval *object, zval *offset, int type, zval *return_v
        } else {
                php_property_proxy_t *proxy;
                php_property_proxy_object_t *proxy_obj;
-               zval *array = value;
-               zend_bool created = Z_ISUNDEF_P(value);
 
-               ZVAL_DEREF(array);
-               if (Z_REFCOUNTED_P(array) && Z_REFCOUNT_P(array) > 1) {
-                       created = 1;
-                       SEPARATE_ZVAL_NOREF(array);
-               }
-               if (Z_TYPE_P(array) != IS_ARRAY) {
-                       created = 1;
-                       if (Z_ISUNDEF_P(array)) {
-                               array_init(array);
-                       } else {
-                               convert_to_array(array);
-                       }
+               if (Z_ISUNDEF_P(value)) {
+                       ZVAL_NULL(value);
                }
 
                if (!member) {
-                       member = zend_long_to_str(zend_hash_next_free_element(
-                                       Z_ARRVAL_P(array)));
+                       if (Z_TYPE_P(value) == IS_ARRAY) {
+                               member = zend_long_to_str(zend_hash_next_free_element(
+                                       Z_ARRVAL_P(value)));
+                       } else if (Z_TYPE_P(value) != IS_OBJECT){
+                               member = zend_long_to_str(0);
+                       }
                }
 
-               proxy = php_property_proxy_init(value, member);
+               proxy = php_property_proxy_init(NULL, member);
                proxy_obj = php_property_proxy_object_new_ex(NULL, proxy);
                ZVAL_COPY(&proxy_obj->parent, object);
                RETVAL_OBJ(&proxy_obj->zo);
 
-               if (created) {
-                       Z_DELREF_P(value);
-               }
-
-               debug_propro(0, created ? "dim_R pp C" : "dim_R pp", get_propro(object), NULL, offset, return_value);
+               debug_propro(0, "dim_R pp", get_propro(object), NULL, offset, return_value);
        }
 
        if (member) {
@@ -473,7 +552,6 @@ static int has_dimension(zval *object, zval *offset, int check_empty)
        ZVAL_UNDEF(&tmp);
        value = get_proxied_value(object, &tmp);
 
-       exists = 0;
        if (!Z_ISUNDEF_P(value)) {
                zend_string *zs = zval_get_string(offset);
 
@@ -481,9 +559,7 @@ static int has_dimension(zval *object, zval *offset, int check_empty)
                if (Z_TYPE_P(value) == IS_ARRAY) {
                        zval *zentry = zend_symtable_find(Z_ARRVAL_P(value), zs);
 
-                       if (!zentry) {
-                               exists = 0;
-                       } else {
+                       if (zentry) {
                                if (check_empty) {
                                        exists = !Z_ISNULL_P(zentry);
                                } else {
@@ -504,22 +580,25 @@ static void write_dimension(zval *object, zval *offset, zval *input_value)
 {
        zval *array, tmp;
        zend_string *zs = NULL;
+       zend_bool separated;
 
        debug_propro(1, "dim_w", get_propro(object), NULL, offset, input_value);
 
-       ZVAL_UNDEF(&tmp);
-       array = get_proxied_value(object, &tmp);
-
        if (offset) {
                zs = zval_get_string(offset);
        }
+
+       ZVAL_UNDEF(&tmp);
+       array = get_proxied_value(object, &tmp);
+       separated = separate_container(object, array);
        set_container_value(array, zs, input_value);
+       set_proxied_value(object, array);
+       cleanup_container(object, array, separated);
+
        if (zs) {
                zend_string_release(zs);
        }
 
-       set_proxied_value(object, array);
-
        debug_propro(-1, "dim_w", get_propro(object), NULL, offset, input_value);
 }
 
@@ -549,40 +628,31 @@ static void unset_dimension(zval *object, zval *offset)
 }
 
 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct, 0, 0, 2)
-       ZEND_ARG_INFO(1, object)
+       ZEND_ARG_INFO(0, 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 *reference, *container, *parent = NULL;
+       zval *reference, *parent = NULL;
        zend_string *member;
 
        zend_replace_error_handling(EH_THROW, NULL, &zeh);
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "zS|O!",
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "o!S|O!",
                        &reference, &member, &parent,
                        php_property_proxy_class_entry)) {
                php_property_proxy_object_t *obj;
 
-               container = reference;
-               ZVAL_DEREF(container);
-               switch (Z_TYPE_P(container)) {
-               case IS_ARRAY:
-                       SEPARATE_ARRAY(container);
-                       break;
-               case IS_OBJECT:
-                       break;
-               default:
-                       SEPARATE_ZVAL(container);
-                       convert_to_array(container);
-                       break;
-               }
-
                obj = get_propro(getThis());
-               obj->proxy = php_property_proxy_init(reference, member);
 
                if (parent) {
                        ZVAL_COPY(&obj->parent, parent);
+                       obj->proxy = php_property_proxy_init(NULL, member);
+               } else if (reference) {
+                       zval *container = reference;
+                       obj->proxy = php_property_proxy_init(container, member);
+               } else {
+                       php_error(E_WARNING, "Either object or parent must be set");
                }
        }
        zend_restore_error_handling(&zeh);
@@ -607,6 +677,8 @@ static PHP_MINIT_FUNCTION(propro)
                        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.get_gc = get_gc;
+       php_property_proxy_object_handlers.get_debug_info = get_debug_info;
        php_property_proxy_object_handlers.set = set_obj;
        php_property_proxy_object_handlers.get = get_obj;
        php_property_proxy_object_handlers.cast_object = cast_obj;