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 void _walk(php_property_proxy_object_t
*obj
)
61 if (!Z_ISUNDEF(obj
->parent
)) {
62 _walk(get_propro(&obj
->parent
));
65 fprintf(stderr
, ".%s", obj
->proxy
->member
->val
);
70 static void debug_propro(int inout
, const char *f
,
71 php_property_proxy_object_t
*obj
,
72 php_property_proxy_t
*proxy
,
73 zval
*offset
, zval
*value
)
79 fprintf(stderr
, "#PP %14p %*c %s %s\t", proxy
, level
, ' ', inoutstr
[inout
+ 1], f
);
84 fprintf(stderr
, " container=%p (%s", Z_COUNTED(proxy
->container
),
85 types
[Z_TYPE(proxy
->container
)]);
86 if (Z_REFCOUNTED(proxy
->container
) && Z_REFCOUNT(proxy
->container
) != 1) {
87 fprintf(stderr
, " rc=%d", Z_REFCOUNT(proxy
->container
));
89 if (Z_ISREF(proxy
->container
)) {
90 zval
*ref
= Z_REFVAL(proxy
->container
);
91 fprintf(stderr
, " %s %p", types
[Z_TYPE_P(ref
)], ref
->value
.counted
);
92 if (Z_REFCOUNTED_P(ref
) && Z_REFCOUNT_P(ref
) != 1) {
93 fprintf(stderr
, " rc=%d", Z_REFCOUNT_P(ref
));
96 fprintf(stderr
, ") ");
105 char *offset_str
= "[]";
109 convert_to_string_ex(o
);
110 offset_str
= Z_STRVAL_P(o
);
113 fprintf(stderr
, ".%s", offset_str
);
115 if (o
&& o
!= offset
) {
120 if (Z_ISUNDEF_P(value
)) {
121 fprintf(stderr
, " =UNDEF");
123 fprintf(stderr
, " =%p (%s", Z_COUNTED_P(value
), types
[Z_TYPE_P(value
)&0xf]);
124 if (Z_REFCOUNTED_P(value
) && Z_REFCOUNT_P(value
) != 1) {
125 fprintf(stderr
, " rc=%d", Z_REFCOUNT_P(value
));
127 if (Z_ISREF_P(value
)) {
128 fprintf(stderr
, " ref");
130 fprintf(stderr
, ") ");
131 if (!Z_ISUNDEF_P(value
) && Z_TYPE_P(value
) != IS_INDIRECT
) {
132 zend_print_flat_zval_r(value TSRMLS_CC
);
137 fprintf(stderr
, "\n");
140 #define debug_propro(l, f, obj, proxy, off, val)
143 php_property_proxy_t
*php_property_proxy_init(zval
*container
, zend_string
*member
)
145 php_property_proxy_t
*proxy
= ecalloc(1, sizeof(*proxy
));
149 ZVAL_STR_COPY(&offset
, member
);
152 debug_propro(1, "init", NULL
, proxy
, &offset
, NULL
);
154 ZVAL_COPY(&proxy
->container
, container
);
155 proxy
->member
= zend_string_copy(member
);
157 debug_propro(-1, "init", NULL
, proxy
, &offset
, NULL
);
166 void php_property_proxy_free(php_property_proxy_t
**proxy
)
171 ZVAL_STR_COPY(&offset
, (*proxy
)->member
);
174 debug_propro(1, "dtor", NULL
, *proxy
, &offset
, NULL
);
177 zval_ptr_dtor(&(*proxy
)->container
);
179 ZVAL_UNDEF(&(*proxy
)->container
);
180 zend_string_release((*proxy
)->member
);
181 (*proxy
)->member
= NULL
;
186 debug_propro(-1, "dtor", NULL
, NULL
, &offset
, NULL
);
193 static zend_class_entry
*php_property_proxy_class_entry
;
194 static zend_object_handlers php_property_proxy_object_handlers
;
196 zend_class_entry
*php_property_proxy_get_class_entry(void)
198 return php_property_proxy_class_entry
;
201 php_property_proxy_object_t
*php_property_proxy_object_new_ex(
202 zend_class_entry
*ce
, php_property_proxy_t
*proxy
)
204 php_property_proxy_object_t
*o
;
207 ce
= php_property_proxy_class_entry
;
210 o
= ecalloc(1, sizeof(*o
) + sizeof(zval
) * (ce
->default_properties_count
- 1));
211 zend_object_std_init(&o
->zo
, ce
);
212 object_properties_init(&o
->zo
, ce
);
215 o
->zo
.handlers
= &php_property_proxy_object_handlers
;
220 zend_object
*php_property_proxy_object_new(zend_class_entry
*ce
)
222 return &php_property_proxy_object_new_ex(ce
, NULL
)->zo
;
225 static void destroy_obj(zend_object
*object
)
227 php_property_proxy_object_t
*o
= PHP_PROPRO_PTR(object
);
230 php_property_proxy_free(&o
->proxy
);
232 if (!Z_ISUNDEF(o
->parent
)) {
233 zval_ptr_dtor(&o
->parent
);
234 ZVAL_UNDEF(&o
->parent
);
236 zend_object_std_dtor(object
);
239 static ZEND_RESULT_CODE
cast_obj(zval
*object
, zval
*return_value
, int type
)
244 RETVAL_ZVAL(get_proxied_value(object
, &tmp
), 1, 0);
246 debug_propro(0, "cast", get_propro(object
), NULL
, NULL
, return_value
);
248 if (!Z_ISUNDEF_P(return_value
)) {
249 ZVAL_DEREF(return_value
);
250 convert_to_explicit_type_ex(return_value
, type
);
257 static zval
*get_obj(zval
*object
, zval
*return_value
)
262 RETVAL_ZVAL(get_proxied_value(object
, &tmp
), 1, 0);
266 static void set_obj(zval
*object
, zval
*value
) {
267 set_proxied_value(object
, value
);
270 static inline php_property_proxy_object_t
*get_propro(zval
*object
)
272 ZEND_ASSERT(Z_TYPE_P(object
) == IS_OBJECT
);
273 return PHP_PROPRO_PTR(Z_OBJ_P(object
));
276 static inline zval
*get_container(zval
*object
)
278 php_property_proxy_object_t
*obj
= get_propro(object
);
280 if (!Z_ISUNDEF(obj
->parent
)) {
281 zval
*parent_value
, tmp
;
284 parent_value
= get_proxied_value(&obj
->parent
, &tmp
);
285 ZVAL_DEREF(parent_value
);
286 switch (Z_TYPE_P(parent_value
)) {
289 zend_assign_to_variable(&obj
->proxy
->container
, parent_value
, IS_CV
);
296 return &obj
->proxy
->container
;
299 static inline void set_container(zval
*object
, zval
*container
)
301 php_property_proxy_object_t
*obj
= get_propro(object
);
303 zend_assign_to_variable(&obj
->proxy
->container
, container
, IS_CV
);
305 if (!Z_ISUNDEF(obj
->parent
)) {
306 set_proxied_value(&obj
->parent
, &obj
->proxy
->container
);
310 static inline zval
*get_container_value(zval
*container
, zend_string
*member
, zval
*return_value
)
312 zval
*found_value
= NULL
, prop_tmp
;
314 ZVAL_DEREF(container
);
315 switch (Z_TYPE_P(container
)) {
317 found_value
= zend_read_property(Z_OBJCE_P(container
), container
,
318 member
->val
, member
->len
, 0, &prop_tmp
);
323 found_value
= zend_symtable_find(Z_ARRVAL_P(container
), member
);
328 RETVAL_ZVAL(found_value
, 0, 0);
329 //ZVAL_COPY_VALUE(return_value, found_value);
335 static inline void set_container_value(zval
*container
, zend_string
*member
, zval
*value
)
337 ZVAL_DEREF(container
);
338 switch (Z_TYPE_P(container
)) {
340 zend_update_property(Z_OBJCE_P(container
), container
,
341 member
->val
, member
->len
, value
);
345 SEPARATE_ARRAY(container
);
346 Z_TRY_ADDREF_P(value
);
347 zend_symtable_update(Z_ARRVAL_P(container
), member
, value
);
356 static zval
*get_proxied_value(zval
*object
, zval
*return_value
)
358 php_property_proxy_object_t
*obj
= get_propro(object
);
360 debug_propro(1, "get", obj
, NULL
, NULL
, NULL
);
363 zval
*container
= get_container(object
);
365 return_value
= get_container_value(container
, obj
->proxy
->member
, return_value
);
368 debug_propro(-1, "get", obj
, NULL
, NULL
, return_value
);
373 static void set_proxied_value(zval
*object
, zval
*value
)
375 php_property_proxy_object_t
*obj
= get_propro(object
);
377 debug_propro(1, "set", obj
, NULL
, NULL
, value
);
380 zval
*container
= get_container(object
);
382 set_container_value(container
, obj
->proxy
->member
, value
);
383 set_container(object
, container
);
385 debug_propro(0, "set", obj
, NULL
, NULL
, value
);
388 debug_propro(-1, "set", obj
, NULL
, NULL
, value
);
391 static zval
*read_dimension(zval
*object
, zval
*offset
, int type
, zval
*return_value
)
394 zend_string
*member
= offset
? zval_get_string(offset
) : NULL
;
396 debug_propro(1, type
== BP_VAR_R
? "dim_r" : "dim_R",
397 get_propro(object
), NULL
, offset
, NULL
);
400 value
= get_proxied_value(object
, &tmp
);
402 if (type
== BP_VAR_R
) {
405 if (!Z_ISUNDEF_P(value
)) {
409 RETVAL_ZVAL(get_container_value(value
, member
, &tmp
), 1, 0);
412 php_property_proxy_t
*proxy
;
413 php_property_proxy_object_t
*proxy_obj
;
415 zend_bool created
= Z_ISUNDEF_P(value
);
418 if (Z_REFCOUNTED_P(array
) && Z_REFCOUNT_P(array
) > 1) {
420 SEPARATE_ZVAL_NOREF(array
);
422 if (Z_TYPE_P(array
) != IS_ARRAY
) {
424 if (Z_ISUNDEF_P(array
)) {
427 convert_to_array(array
);
432 member
= zend_long_to_str(zend_hash_next_free_element(
436 proxy
= php_property_proxy_init(value
, member
);
437 proxy_obj
= php_property_proxy_object_new_ex(NULL
, proxy
);
438 ZVAL_COPY(&proxy_obj
->parent
, object
);
439 RETVAL_OBJ(&proxy_obj
->zo
);
447 zend_string_release(member
);
450 debug_propro(-1, type
== BP_VAR_R
? "dim_r" : "dim_R",
451 get_propro(object
), NULL
, offset
, return_value
);
456 static int has_dimension(zval
*object
, zval
*offset
, int check_empty
)
461 debug_propro(1, "dim_e", get_propro(object
), NULL
, offset
, NULL
);
464 value
= get_proxied_value(object
, &tmp
);
467 if (!Z_ISUNDEF_P(value
)) {
468 zend_string
*zs
= zval_get_string(offset
);
471 if (Z_TYPE_P(value
) == IS_ARRAY
) {
472 zval
*zentry
= zend_symtable_find(Z_ARRVAL_P(value
), zs
);
478 exists
= !Z_ISNULL_P(zentry
);
485 zend_string_release(zs
);
488 debug_propro(-1, "dim_e", get_propro(object
), NULL
, offset
, NULL
);
493 static void write_dimension(zval
*object
, zval
*offset
, zval
*input_value
)
495 zend_bool created
= 0;
496 zval
*array
, *value
, tmp
;
498 debug_propro(1, "dim_w", get_propro(object
), NULL
, offset
, input_value
);
501 value
= get_proxied_value(object
, &tmp
);
504 if (Z_ISUNDEF_P(value
)) {
507 } else if (Z_TYPE_P(array
) == IS_ARRAY
) {
508 if (Z_REFCOUNT_P(array
) > 1) {
509 SEPARATE_ARRAY(array
);
514 if (Z_REFCOUNTED_P(array
) && Z_REFCOUNT_P(array
) > 1) {
515 SEPARATE_ZVAL(array
);
518 if (Z_TYPE_P(array
) != IS_ARRAY
) {
519 convert_to_array(array
);
524 Z_TRY_ADDREF_P(input_value
);
526 debug_propro(0, "dim_w", get_propro(object
), NULL
, offset
, input_value
);
529 zend_string
*zs
= zval_get_string(offset
);
530 zend_symtable_update(Z_ARRVAL_P(array
), zs
, input_value
);
531 zend_string_release(zs
);
533 zend_hash_next_index_insert(Z_ARRVAL_P(array
), input_value
);
536 set_proxied_value(object
, value
);
542 debug_propro(-1, "dim_w", get_propro(object
), NULL
, offset
, input_value
);
545 static void unset_dimension(zval
*object
, zval
*offset
)
547 zval
*array
, *value
, tmp
;
549 debug_propro(1, "dim_u", get_propro(object
), NULL
, offset
, NULL
);
552 value
= get_proxied_value(object
, &tmp
);
556 if (Z_TYPE_P(array
) == IS_ARRAY
) {
557 zend_string
*o
= zval_get_string(offset
);
559 SEPARATE_ARRAY(array
);
560 zend_symtable_del(Z_ARRVAL_P(array
), o
);
562 set_proxied_value(object
, value
);
564 zend_string_release(o
);
567 debug_propro(-1, "dim_u", get_propro(object
), NULL
, offset
, NULL
);
570 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct
, 0, 0, 2)
571 ZEND_ARG_INFO(1, object
)
572 ZEND_ARG_INFO(0, member
)
573 ZEND_ARG_OBJ_INFO(0, parent
, php
\\PropertyProxy
, 1)
575 static PHP_METHOD(propro
, __construct
) {
576 zend_error_handling zeh
;
577 zval
*reference
, *container
, *parent
= NULL
;
580 zend_replace_error_handling(EH_THROW
, NULL
, &zeh
);
581 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "zS|O!",
582 &reference
, &member
, &parent
,
583 php_property_proxy_class_entry
)) {
584 php_property_proxy_object_t
*obj
;
586 container
= reference
;
587 ZVAL_DEREF(container
);
588 switch (Z_TYPE_P(container
)) {
590 SEPARATE_ARRAY(container
);
595 SEPARATE_ZVAL(container
);
596 convert_to_array(container
);
600 obj
= get_propro(getThis());
601 obj
->proxy
= php_property_proxy_init(reference
, member
);
604 ZVAL_COPY(&obj
->parent
, parent
);
607 zend_restore_error_handling(&zeh
);
610 static const zend_function_entry php_property_proxy_method_entry
[] = {
611 PHP_ME(propro
, __construct
, ai_propro_construct
, ZEND_ACC_PUBLIC
)
615 static PHP_MINIT_FUNCTION(propro
)
617 zend_class_entry ce
= {0};
619 INIT_NS_CLASS_ENTRY(ce
, "php", "PropertyProxy",
620 php_property_proxy_method_entry
);
621 php_property_proxy_class_entry
= zend_register_internal_class(&ce
);
622 php_property_proxy_class_entry
->create_object
= php_property_proxy_object_new
;
623 php_property_proxy_class_entry
->ce_flags
|= ZEND_ACC_FINAL
;
625 memcpy(&php_property_proxy_object_handlers
, zend_get_std_object_handlers(),
626 sizeof(zend_object_handlers
));
627 php_property_proxy_object_handlers
.offset
= XtOffsetOf(php_property_proxy_object_t
, zo
);
628 php_property_proxy_object_handlers
.free_obj
= destroy_obj
;
629 php_property_proxy_object_handlers
.set
= set_obj
;
630 php_property_proxy_object_handlers
.get
= get_obj
;
631 php_property_proxy_object_handlers
.cast_object
= cast_obj
;
632 php_property_proxy_object_handlers
.read_dimension
= read_dimension
;
633 php_property_proxy_object_handlers
.write_dimension
= write_dimension
;
634 php_property_proxy_object_handlers
.has_dimension
= has_dimension
;
635 php_property_proxy_object_handlers
.unset_dimension
= unset_dimension
;
640 PHP_MINFO_FUNCTION(propro
)
642 php_info_print_table_start();
643 php_info_print_table_header(2, "Property proxy support", "enabled");
644 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION
);
645 php_info_print_table_end();
648 static const zend_function_entry propro_functions
[] = {
652 zend_module_entry propro_module_entry
= {
653 STANDARD_MODULE_HEADER
,
662 STANDARD_MODULE_PROPERTIES
665 #ifdef COMPILE_DL_PROPRO
666 ZEND_GET_MODULE(propro
)
675 * vim600: noet sw=4 ts=4 fdm=marker
676 * vim<600: noet sw=4 ts=4