2 +--------------------------------------------------------------------+
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2013 Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
19 #include <ext/standard/info.h>
21 #include "php_propro_api.h"
23 #define DEBUG_PROPRO 0
25 static inline php_property_proxy_object_t
*get_propro(zval
*object
);
26 static inline zval
*get_proxied_value(zval
*object
, zval
*return_value
);
27 static inline void set_proxied_value(zval
*object
, zval
*value
);
29 static zval
*read_dimension(zval
*object
, zval
*offset
, int type
, zval
*return_value
);
30 static ZEND_RESULT_CODE
cast_obj(zval
*object
, zval
*return_value
, int type
);
31 static void write_dimension(zval
*object
, zval
*offset
, zval
*input_value
);
34 /* we do not really care about TS when debugging */
36 static const char *inoutstr
[] = {"< return","= "," > enter "};
37 static const char *types
[] = {
58 static const char *_type(zval
*zv
)
60 switch (Z_TYPE_P(zv
)) {
62 if (zv
->value
.obj
->ce
== php_property_proxy_get_class_entry()) {
67 return types
[Z_TYPE_P(zv
)];
71 static int _walk(php_property_proxy_object_t
*obj
)
76 if (!Z_ISUNDEF(obj
->parent
)) {
77 p
+= _walk(get_propro(&obj
->parent
));
80 p
+= fprintf(stderr
, ".%s", obj
->proxy
->member
->val
);
87 static inline zval
*get_container(zval
*object
, zval
*tmp
);
88 static inline zval
*get_container_value(zval
*container
, zend_string
*member
, zval
*return_value
);
90 static void debug_propro(int inout
, const char *f
,
91 php_property_proxy_object_t
*obj
,
92 php_property_proxy_t
*proxy
,
93 zval
*offset
, zval
*value
)
96 zval tmp
, *container
= &tmp
;
102 fprintf(stderr
, "#PP %14p %*c %s %s\t", proxy
, level
, ' ', inoutstr
[inout
+ 1], f
);
109 container
= &proxy
->container
;
110 } else if (Z_ISUNDEF(obj
->parent
)) {
111 container
= &obj
->proxy
->container
;
113 fprintf(stderr
, " container= %-14p < %-10s rc=%-2d%s> ",
114 Z_REFCOUNTED_P(container
) ? Z_COUNTED_P(container
) : NULL
,
116 Z_REFCOUNTED_P(container
) ? Z_REFCOUNT_P(container
) : 0,
117 Z_ISREF_P(container
) ? "(ref) ":"(noref)");
126 char *offset_str
= "[]";
130 convert_to_string_ex(o
);
131 offset_str
= Z_STRVAL_P(o
);
134 width
+= fprintf(stderr
, ".%s", offset_str
);
136 if (o
&& o
!= offset
) {
141 fprintf(stderr
, "%.*s", 32-width
, " ");
142 if (Z_ISUNDEF_P(value
)) {
143 fprintf(stderr
, " = UNDEF");
145 fprintf(stderr
, " = %-14p < %-10s rc=%-2d%s> ",
146 Z_REFCOUNTED_P(value
) ? Z_COUNTED_P(value
) : NULL
,
148 Z_REFCOUNTED_P(value
) ? Z_REFCOUNT_P(value
) : 0,
149 Z_ISREF_P(value
) ? "(ref) " : "(noref)");
150 if (!Z_ISUNDEF_P(value
) && Z_TYPE_P(value
) != IS_INDIRECT
) {
151 zend_print_flat_zval_r(value TSRMLS_CC
);
156 fprintf(stderr
, "\n");
159 #define debug_propro(l, f, obj, proxy, off, val)
162 php_property_proxy_t
*php_property_proxy_init(zval
*container
, zend_string
*member
)
164 php_property_proxy_t
*proxy
= ecalloc(1, sizeof(*proxy
));
168 ZVAL_STR_COPY(&offset
, member
);
172 ZVAL_COPY(&proxy
->container
, container
);
174 proxy
->member
= zend_string_copy(member
);
176 debug_propro(0, "init", NULL
, proxy
, &offset
, NULL
);
185 void php_property_proxy_free(php_property_proxy_t
**proxy
)
190 ZVAL_STR_COPY(&offset
, (*proxy
)->member
);
191 debug_propro(0, "free", NULL
, *proxy
, &offset
, NULL
);
195 if (!Z_ISUNDEF((*proxy
)->container
)) {
196 zval_ptr_dtor(&(*proxy
)->container
);
197 ZVAL_UNDEF(&(*proxy
)->container
);
199 zend_string_release((*proxy
)->member
);
200 (*proxy
)->member
= NULL
;
210 static zend_class_entry
*php_property_proxy_class_entry
;
211 static zend_object_handlers php_property_proxy_object_handlers
;
213 zend_class_entry
*php_property_proxy_get_class_entry(void)
215 return php_property_proxy_class_entry
;
218 php_property_proxy_object_t
*php_property_proxy_object_new_ex(
219 zend_class_entry
*ce
, php_property_proxy_t
*proxy
)
221 php_property_proxy_object_t
*o
;
224 ce
= php_property_proxy_class_entry
;
227 o
= ecalloc(1, sizeof(*o
) + sizeof(zval
) * (ce
->default_properties_count
- 1));
228 zend_object_std_init(&o
->zo
, ce
);
229 object_properties_init(&o
->zo
, ce
);
232 o
->zo
.handlers
= &php_property_proxy_object_handlers
;
237 zend_object
*php_property_proxy_object_new(zend_class_entry
*ce
)
239 return &php_property_proxy_object_new_ex(ce
, NULL
)->zo
;
242 static void destroy_obj(zend_object
*object
)
244 php_property_proxy_object_t
*o
= PHP_PROPRO_PTR(object
);
247 php_property_proxy_free(&o
->proxy
);
249 if (!Z_ISUNDEF(o
->parent
)) {
250 zval_ptr_dtor(&o
->parent
);
251 ZVAL_UNDEF(&o
->parent
);
253 zend_object_std_dtor(object
);
256 static inline php_property_proxy_object_t
*get_propro(zval
*object
)
258 ZEND_ASSERT(Z_TYPE_P(object
) == IS_OBJECT
);
259 return PHP_PROPRO_PTR(Z_OBJ_P(object
));
262 static HashTable
*get_gc(zval
*object
, zval
**table
, int *n
)
264 php_property_proxy_object_t
*o
= get_propro(object
);
266 if (Z_ISUNDEF(o
->parent
)) {
267 *table
= &o
->proxy
->container
;
275 static HashTable
*get_debug_info(zval
*object
, int *is_temp
)
279 php_property_proxy_object_t
*obj
= get_propro(object
);
282 zend_hash_init(ht
, 3, NULL
, NULL
, 0);
284 if (Z_ISUNDEF(obj
->parent
)) {
285 zend_hash_str_add_empty_element(ht
, "parent", sizeof("parent")-1);
286 zend_hash_str_add(ht
, "container", sizeof("container")-1, &obj
->proxy
->container
);
288 zend_hash_str_add(ht
, "parent", sizeof("parent")-1, &obj
->parent
);
289 zend_hash_str_add_empty_element(ht
, "container", sizeof("container")-1);
292 zmember
= zend_hash_str_add_empty_element(ht
, "member", sizeof("member")-1);
293 ZVAL_STR(zmember
, obj
->proxy
->member
);
299 static ZEND_RESULT_CODE
cast_obj(zval
*object
, zval
*return_value
, int type
)
304 RETVAL_ZVAL(get_proxied_value(object
, &tmp
), 1, 0);
306 debug_propro(0, "cast", get_propro(object
), NULL
, NULL
, return_value
);
308 if (!Z_ISUNDEF_P(return_value
)) {
309 ZVAL_DEREF(return_value
);
310 convert_to_explicit_type_ex(return_value
, type
);
317 static zval
*get_obj(zval
*object
, zval
*return_value
)
322 RETVAL_ZVAL(get_proxied_value(object
, &tmp
), 1, 0);
326 static void set_obj(zval
*object
, zval
*value
) {
327 set_proxied_value(object
, value
);
330 static inline zval
*get_container(zval
*object
, zval
*tmp
)
332 php_property_proxy_object_t
*obj
= get_propro(object
);
335 if (Z_ISUNDEF(obj
->parent
)) {
336 container
= &obj
->proxy
->container
;
338 container
= get_proxied_value(&obj
->parent
, tmp
);
343 static inline void set_container(zval
*object
, zval
*container
)
345 php_property_proxy_object_t
*obj
= get_propro(object
);
347 if (Z_ISUNDEF(obj
->parent
)) {
348 if (container
!= &obj
->proxy
->container
) {
351 ZVAL_COPY_VALUE(&tmp
, &obj
->proxy
->container
);
352 ZVAL_COPY(&obj
->proxy
->container
, container
);
356 set_proxied_value(&obj
->parent
, container
);
360 static inline zval
*get_container_value(zval
*container
, zend_string
*member
, zval
*return_value
)
362 zval
*found_value
= NULL
, prop_tmp
;
364 ZVAL_DEREF(container
);
365 switch (Z_TYPE_P(container
)) {
367 ZVAL_UNDEF(&prop_tmp
);
368 found_value
= zend_read_property(Z_OBJCE_P(container
), container
,
369 member
->val
, member
->len
, 0, &prop_tmp
);
373 found_value
= zend_symtable_find(Z_ARRVAL_P(container
), member
);
378 RETVAL_ZVAL(found_value
, 0, 0);
384 static inline void cleanup_container(zval
*object
, zval
*container
, zend_bool separated
)
387 zval_ptr_dtor(container
);
391 static inline zend_bool
separate_container(zval
*object
, zval
*container
)
393 switch (Z_TYPE_P(container
)) {
398 /* always duplicate for PHP-7.0 and 7.1 on travis */
399 ZVAL_ARR(container
, zend_array_dup(Z_ARRVAL_P(container
)));
403 array_init(container
);
407 SEPARATE_ZVAL(container
);
408 Z_TRY_ADDREF_P(container
);
409 convert_to_array(container
);
416 static inline zval
*set_container_value(zval
*container
, zend_string
*member
, zval
*value
)
418 ZVAL_DEREF(container
);
419 switch (Z_TYPE_P(container
)) {
421 zend_update_property(Z_OBJCE_P(container
), container
,
422 member
->val
, member
->len
, value
);
426 Z_TRY_ADDREF_P(value
);
428 value
= zend_symtable_update(Z_ARRVAL_P(container
), member
, value
);
430 value
= zend_hash_next_index_insert(Z_ARRVAL_P(container
), value
);
443 static zval
*get_proxied_value(zval
*object
, zval
*return_value
)
445 php_property_proxy_object_t
*obj
= get_propro(object
);
447 debug_propro(1, "get", obj
, NULL
, NULL
, NULL
);
450 zval tmp
, *container
;
453 container
= get_container(object
, &tmp
);
455 return_value
= get_container_value(container
, obj
->proxy
->member
, return_value
);
458 debug_propro(-1, "get", obj
, NULL
, NULL
, return_value
);
463 static void set_proxied_value(zval
*object
, zval
*value
)
465 php_property_proxy_object_t
*obj
= get_propro(object
);
467 debug_propro(1, "set", obj
, NULL
, NULL
, value
);
470 zval tmp
, *container
;
473 Z_TRY_ADDREF_P(value
);
476 container
= get_container(object
, &tmp
);
477 separated
= separate_container(object
, container
);
478 set_container_value(container
, obj
->proxy
->member
, value
);
479 set_container(object
, container
);
480 cleanup_container(object
, container
, separated
);
482 Z_TRY_DELREF_P(value
);
484 debug_propro(0, "set", obj
, NULL
, NULL
, value
);
487 debug_propro(-1, "set", obj
, NULL
, NULL
, value
);
490 static zval
*read_dimension(zval
*object
, zval
*offset
, int type
, zval
*return_value
)
493 zend_string
*member
= offset
? zval_get_string(offset
) : NULL
;
495 debug_propro(1, type
== BP_VAR_R
? "dim_r" : "dim_R",
496 get_propro(object
), NULL
, offset
, NULL
);
499 value
= get_proxied_value(object
, &tmp
);
501 if (type
== BP_VAR_R
|| type
== BP_VAR_IS
) {
504 if (!Z_ISUNDEF_P(value
)) {
508 RETVAL_ZVAL(get_container_value(value
, member
, &tmp
), 1, 0);
511 php_property_proxy_t
*proxy
;
512 php_property_proxy_object_t
*proxy_obj
;
514 if (Z_ISUNDEF_P(value
)) {
519 if (Z_TYPE_P(value
) == IS_ARRAY
) {
520 member
= zend_long_to_str(zend_hash_next_free_element(
522 } else if (Z_TYPE_P(value
) != IS_OBJECT
){
523 member
= zend_long_to_str(0);
527 proxy
= php_property_proxy_init(NULL
, member
);
528 proxy_obj
= php_property_proxy_object_new_ex(NULL
, proxy
);
529 ZVAL_COPY(&proxy_obj
->parent
, object
);
530 RETVAL_OBJ(&proxy_obj
->zo
);
532 debug_propro(0, "dim_R pp", get_propro(object
), NULL
, offset
, return_value
);
536 zend_string_release(member
);
539 debug_propro(-1, type
== BP_VAR_R
? "dim_r" : "dim_R",
540 get_propro(object
), NULL
, offset
, return_value
);
545 static int has_dimension(zval
*object
, zval
*offset
, int check_empty
)
550 debug_propro(1, "dim_e", get_propro(object
), NULL
, offset
, NULL
);
553 value
= get_proxied_value(object
, &tmp
);
555 if (!Z_ISUNDEF_P(value
)) {
556 zend_string
*zs
= zval_get_string(offset
);
559 if (Z_TYPE_P(value
) == IS_ARRAY
) {
560 zval
*zentry
= zend_symtable_find(Z_ARRVAL_P(value
), zs
);
564 exists
= !Z_ISNULL_P(zentry
);
571 zend_string_release(zs
);
574 debug_propro(-1, "dim_e", get_propro(object
), NULL
, offset
, NULL
);
579 static void write_dimension(zval
*object
, zval
*offset
, zval
*input_value
)
582 zend_string
*zs
= NULL
;
585 debug_propro(1, "dim_w", get_propro(object
), NULL
, offset
, input_value
);
588 zs
= zval_get_string(offset
);
592 array
= get_proxied_value(object
, &tmp
);
593 separated
= separate_container(object
, array
);
594 set_container_value(array
, zs
, input_value
);
595 set_proxied_value(object
, array
);
596 cleanup_container(object
, array
, separated
);
599 zend_string_release(zs
);
602 debug_propro(-1, "dim_w", get_propro(object
), NULL
, offset
, input_value
);
605 static void unset_dimension(zval
*object
, zval
*offset
)
607 zval
*array
, *value
, tmp
;
609 debug_propro(1, "dim_u", get_propro(object
), NULL
, offset
, NULL
);
612 value
= get_proxied_value(object
, &tmp
);
616 if (Z_TYPE_P(array
) == IS_ARRAY
) {
617 zend_string
*o
= zval_get_string(offset
);
619 SEPARATE_ARRAY(array
);
620 zend_symtable_del(Z_ARRVAL_P(array
), o
);
622 set_proxied_value(object
, value
);
624 zend_string_release(o
);
627 debug_propro(-1, "dim_u", get_propro(object
), NULL
, offset
, NULL
);
630 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct
, 0, 0, 2)
631 ZEND_ARG_INFO(0, object
)
632 ZEND_ARG_INFO(0, member
)
633 ZEND_ARG_OBJ_INFO(0, parent
, php
\\PropertyProxy
, 1)
635 static PHP_METHOD(propro
, __construct
) {
636 zend_error_handling zeh
;
637 zval
*reference
, *parent
= NULL
;
640 zend_replace_error_handling(EH_THROW
, NULL
, &zeh
);
641 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "o!S|O!",
642 &reference
, &member
, &parent
,
643 php_property_proxy_class_entry
)) {
644 php_property_proxy_object_t
*obj
;
646 obj
= get_propro(getThis());
649 ZVAL_COPY(&obj
->parent
, parent
);
650 obj
->proxy
= php_property_proxy_init(NULL
, member
);
651 } else if (reference
) {
652 zval
*container
= reference
;
653 obj
->proxy
= php_property_proxy_init(container
, member
);
655 php_error(E_WARNING
, "Either object or parent must be set");
658 zend_restore_error_handling(&zeh
);
661 static const zend_function_entry php_property_proxy_method_entry
[] = {
662 PHP_ME(propro
, __construct
, ai_propro_construct
, ZEND_ACC_PUBLIC
)
666 static PHP_MINIT_FUNCTION(propro
)
668 zend_class_entry ce
= {0};
670 INIT_NS_CLASS_ENTRY(ce
, "php", "PropertyProxy",
671 php_property_proxy_method_entry
);
672 php_property_proxy_class_entry
= zend_register_internal_class(&ce
);
673 php_property_proxy_class_entry
->create_object
= php_property_proxy_object_new
;
674 php_property_proxy_class_entry
->ce_flags
|= ZEND_ACC_FINAL
;
676 memcpy(&php_property_proxy_object_handlers
, zend_get_std_object_handlers(),
677 sizeof(zend_object_handlers
));
678 php_property_proxy_object_handlers
.offset
= XtOffsetOf(php_property_proxy_object_t
, zo
);
679 php_property_proxy_object_handlers
.free_obj
= destroy_obj
;
680 php_property_proxy_object_handlers
.get_gc
= get_gc
;
681 php_property_proxy_object_handlers
.get_debug_info
= get_debug_info
;
682 php_property_proxy_object_handlers
.set
= set_obj
;
683 php_property_proxy_object_handlers
.get
= get_obj
;
684 php_property_proxy_object_handlers
.cast_object
= cast_obj
;
685 php_property_proxy_object_handlers
.read_dimension
= read_dimension
;
686 php_property_proxy_object_handlers
.write_dimension
= write_dimension
;
687 php_property_proxy_object_handlers
.has_dimension
= has_dimension
;
688 php_property_proxy_object_handlers
.unset_dimension
= unset_dimension
;
693 PHP_MINFO_FUNCTION(propro
)
695 php_info_print_table_start();
696 php_info_print_table_header(2, "Property proxy support", "enabled");
697 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION
);
698 php_info_print_table_end();
701 static const zend_function_entry propro_functions
[] = {
705 zend_module_entry propro_module_entry
= {
706 STANDARD_MODULE_HEADER
,
715 STANDARD_MODULE_PROPERTIES
718 #ifdef COMPILE_DL_PROPRO
719 ZEND_GET_MODULE(propro
)
728 * vim600: noet sw=4 ts=4 fdm=marker
729 * vim<600: noet sw=4 ts=4