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 1
25 static inline php_property_proxy_object_t
*get_propro(zval
*object
);
26 static zval
*get_parent_proxied_value(zval
*object
, zval
*return_value
);
27 static zval
*get_proxied_value(zval
*object
, zval
*return_value
);
28 static 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_proxied_value(zval
*object
, zval
*return_value
, int type
);
31 static void write_dimension(zval
*object
, zval
*offset
, zval
*value
);
34 /* we do not really care about TS when debugging */
36 static const char space
[] = " ";
37 static const char *inoutstr
[] = {"< return","="," > enter"};
38 static const char *types
[] = {
59 static void _walk(php_property_proxy_object_t
*obj
)
62 if (!Z_ISUNDEF(obj
->parent
)) {
63 _walk(get_propro(&obj
->parent
));
66 fprintf(stderr
, ".%s", obj
->proxy
->member
->val
);
71 static void debug_propro(int inout
, const char *f
,
72 php_property_proxy_object_t
*obj
,
73 php_property_proxy_t
*proxy
,
74 zval
*offset
, zval
*value
)
80 fprintf(stderr
, "#PP %p (%p) %s %s %s ",
82 proxy
->container
.value
.counted
,
83 &space
[sizeof(space
)-level
],
84 inoutstr
[inout
+1], f
);
94 char *offset_str
= "[]";
98 convert_to_string_ex(o
);
99 offset_str
= Z_STRVAL_P(o
);
102 fprintf(stderr
, ".%s", offset_str
);
104 if (o
&& o
!= offset
) {
109 if (Z_ISUNDEF_P(value
)) {
110 fprintf(stderr
, " = UNDEF");
112 fprintf(stderr
, " = (%s", types
[Z_TYPE_P(value
)&0xf]);
113 if (Z_REFCOUNTED_P(value
)) {
114 fprintf(stderr
, " rc=%u is_ref=%d", Z_REFCOUNT_P(value
), Z_ISREF_P(value
));
116 fprintf(stderr
, ") ");
117 if (!Z_ISUNDEF_P(value
) && Z_TYPE_P(value
) != IS_INDIRECT
) {
118 zend_print_flat_zval_r(value TSRMLS_CC
);
123 fprintf(stderr
, "\n");
126 #define debug_propro(l, f, obj, proxy, off, val)
129 php_property_proxy_t
*php_property_proxy_init(zval
*container
, zend_string
*member
)
131 php_property_proxy_t
*proxy
= ecalloc(1, sizeof(*proxy
));
135 ZVAL_STR_COPY(&offset
, member
);
138 debug_propro(1, "init", NULL
, proxy
, &offset
, container
);
140 ZVAL_COPY(&proxy
->container
, container
);
141 if (Z_TYPE_P(container
) == IS_ARRAY
) {
142 HT_ALLOW_COW_VIOLATION(Z_ARRVAL_P(container
));
144 proxy
->member
= zend_string_copy(member
);
146 debug_propro(-1, "init", NULL
, proxy
, &offset
, container
);
155 void php_property_proxy_free(php_property_proxy_t
**proxy
)
158 php_property_proxy_t
*proxy_ptr
= *proxy
;
161 ZVAL_STR_COPY(&offset
, proxy_ptr
->member
);
164 debug_propro(1, "dtor", NULL
, *proxy
, &offset
, NULL
);
167 debug_propro(0, "dtor", NULL
, *proxy
, &offset
, &(*proxy
)->container
);
169 zval_ptr_dtor(&(*proxy
)->container
);
170 zend_string_release((*proxy
)->member
);
175 debug_propro(-1, "dtor", NULL
, proxy_ptr
, &offset
, NULL
);
182 static zend_class_entry
*php_property_proxy_class_entry
;
183 static zend_object_handlers php_property_proxy_object_handlers
;
185 zend_class_entry
*php_property_proxy_get_class_entry(void)
187 return php_property_proxy_class_entry
;
190 php_property_proxy_object_t
*php_property_proxy_object_new_ex(
191 zend_class_entry
*ce
, php_property_proxy_t
*proxy
)
193 php_property_proxy_object_t
*o
;
196 ce
= php_property_proxy_class_entry
;
199 o
= ecalloc(1, sizeof(*o
) + sizeof(zval
) * (ce
->default_properties_count
- 1));
200 zend_object_std_init(&o
->zo
, ce
);
201 object_properties_init(&o
->zo
, ce
);
204 o
->zo
.handlers
= &php_property_proxy_object_handlers
;
209 zend_object
*php_property_proxy_object_new(zend_class_entry
*ce
)
211 return &php_property_proxy_object_new_ex(ce
, NULL
)->zo
;
214 static void destroy_obj(zend_object
*object
)
216 php_property_proxy_object_t
*o
= PHP_PROPRO_PTR(object
);
219 php_property_proxy_free(&o
->proxy
);
221 if (!Z_ISUNDEF(o
->parent
)) {
222 zval_ptr_dtor(&o
->parent
);
223 ZVAL_UNDEF(&o
->parent
);
225 zend_object_std_dtor(object
);
228 static inline php_property_proxy_object_t
*get_propro(zval
*object
)
230 switch (Z_TYPE_P(object
)) {
234 EMPTY_SWITCH_DEFAULT_CASE();
236 return PHP_PROPRO_PTR(Z_OBJ_P(object
));
239 static inline zend_bool
got_value(zval
*container
, zval
*value
)
243 if (!Z_ISUNDEF_P(value
)) {
244 if (SUCCESS
== is_identical_function(&identical
, value
, container
)) {
245 if (Z_TYPE(identical
) != IS_TRUE
) {
254 static zval
*get_parent_proxied_value(zval
*object
, zval
*return_value
)
256 php_property_proxy_object_t
*obj
;
258 obj
= get_propro(object
);
259 debug_propro(1, "parent_get", obj
, NULL
, NULL
, NULL
);
262 if (!Z_ISUNDEF(obj
->parent
)) {
263 get_proxied_value(&obj
->parent
, return_value
);
267 debug_propro(-1, "parent_get", obj
, NULL
, NULL
, return_value
);
272 static zval
*get_proxied_value(zval
*object
, zval
*return_value
)
274 php_property_proxy_object_t
*obj
;
276 obj
= get_propro(object
);
277 debug_propro(1, "get", obj
, NULL
, NULL
, NULL
);
280 zval parent_value
, *ref
, *found_value
= NULL
, prop_tmp
;
282 ZVAL_UNDEF(&parent_value
);
283 if (!Z_ISUNDEF(obj
->parent
)) {
284 get_parent_proxied_value(object
, &parent_value
);
288 if (Z_ISUNDEF(parent_value
)) {
289 ref
= &obj
->proxy
->container
;
291 zval_ptr_dtor(&obj
->proxy
->container
);
292 ZVAL_COPY(&obj
->proxy
->container
, &parent_value
);
297 switch (Z_TYPE_P(ref
)) {
299 found_value
= zend_read_property(Z_OBJCE_P(ref
), ref
,
300 obj
->proxy
->member
->val
, obj
->proxy
->member
->len
, 0,
306 found_value
= zend_symtable_find(Z_ARRVAL_P(ref
), obj
->proxy
->member
);
311 RETVAL_ZVAL(found_value
, 1, 0);
314 if (!Z_ISUNDEF(obj
->parent
)) {
315 zval_ptr_dtor(&parent_value
);
319 debug_propro(-1, "get", obj
, NULL
, NULL
, return_value
);
324 static ZEND_RESULT_CODE
cast_proxied_value(zval
*object
, zval
*return_value
,
327 get_proxied_value(object
, return_value
);
329 debug_propro(0, "cast", get_propro(object
), NULL
, NULL
, return_value
);
331 if (!Z_ISUNDEF_P(return_value
)) {
332 convert_to_explicit_type_ex(return_value
, type
);
339 static void set_proxied_value(zval
*object
, zval
*value
)
341 zval parent_value
, *ref
= NULL
;
342 php_property_proxy_object_t
*obj
;
344 obj
= get_propro(object
);
345 debug_propro(1, "set", obj
, NULL
, NULL
, value
);
348 ZVAL_UNDEF(&parent_value
);
349 if (!Z_ISUNDEF(obj
->parent
)) {
350 get_parent_proxied_value(object
, &parent_value
);
353 if (Z_ISUNDEF(parent_value
)) {
354 ref
= &obj
->proxy
->container
;
356 zval_ptr_dtor(&obj
->proxy
->container
);
357 ZVAL_COPY(&obj
->proxy
->container
, &parent_value
);
362 switch (Z_TYPE_P(ref
)) {
364 zend_update_property(Z_OBJCE_P(ref
), ref
,
365 obj
->proxy
->member
->val
, obj
->proxy
->member
->len
, value
);
369 Z_TRY_ADDREF_P(value
);
370 zend_symtable_update(Z_ARRVAL_P(ref
), obj
->proxy
->member
, value
);
375 fprintf(stderr
, "set_proxied_value: %d: %s\n",
377 types
[Z_TYPE_P(ref
)]);
382 debug_propro(0, "set", obj
, NULL
, NULL
, ref
);
384 if (!Z_ISUNDEF(obj
->parent
)) {
385 set_proxied_value(&obj
->parent
, ref
);
386 zval_ptr_dtor(&parent_value
);
390 debug_propro(-1, "set", obj
, NULL
, NULL
, value
);
393 static zval
*read_dimension(zval
*object
, zval
*offset
, int type
, zval
*return_value
)
396 zend_string
*member
= offset
? zval_get_string(offset
) : NULL
;
398 debug_propro(1, type
== BP_VAR_R
? "dim_read" : "dim_read_ref",
399 get_propro(object
), NULL
, offset
, NULL
);
401 ZVAL_UNDEF(&proxied_value
);
402 get_proxied_value(object
, &proxied_value
);
404 if (BP_VAR_R
== type
&& member
&& !Z_ISUNDEF(proxied_value
)) {
405 if (Z_TYPE(proxied_value
) == IS_ARRAY
) {
408 hash_value
= zend_symtable_find(Z_ARRVAL(proxied_value
), member
);
411 RETVAL_ZVAL(hash_value
, 1, 0);
415 php_property_proxy_t
*proxy
;
416 php_property_proxy_object_t
*proxy_obj
;
418 if (!Z_ISUNDEF(proxied_value
)) {
419 SEPARATE_ZVAL(&proxied_value
);
420 convert_to_array(&proxied_value
);
422 array_init(&proxied_value
);
425 set_proxied_value(object
, &proxied_value
);
428 member
= zend_long_to_str(zend_hash_next_free_element(
429 Z_ARRVAL_P(Z_REFVAL(proxied_value
))));
432 proxy
= php_property_proxy_init(&proxied_value
, member
);
434 proxy_obj
= php_property_proxy_object_new_ex(NULL
, proxy
);
435 ZVAL_COPY(&proxy_obj
->parent
, object
);
436 RETVAL_OBJ(&proxy_obj
->zo
);
440 zend_string_release(member
);
443 zval_ptr_dtor(&proxied_value
);
445 debug_propro(-1, type
== BP_VAR_R
? "dim_read" : "dim_read_ref",
446 get_propro(object
), NULL
, offset
, return_value
);
451 static int has_dimension(zval
*object
, zval
*offset
, int check_empty
)
456 debug_propro(1, "dim_exists", get_propro(object
), NULL
, offset
, NULL
);
458 ZVAL_UNDEF(&proxied_value
);
459 get_proxied_value(object
, &proxied_value
);
461 if (Z_ISUNDEF(proxied_value
)) {
464 zend_string
*zs
= zval_get_string(offset
);
466 if (Z_TYPE(proxied_value
) == IS_ARRAY
) {
467 zval
*zentry
= zend_symtable_find(Z_ARRVAL(proxied_value
), zs
);
473 exists
= !Z_ISNULL_P(zentry
);
480 zend_string_release(zs
);
483 zval_ptr_dtor(&proxied_value
);
485 debug_propro(-1, "dim_exists", get_propro(object
), NULL
, offset
, NULL
);
490 static void write_dimension(zval
*object
, zval
*offset
, zval
*value
)
494 debug_propro(1, "dim_write", get_propro(object
), NULL
, offset
, value
);
496 ZVAL_UNDEF(&proxied_value
);
497 get_proxied_value(object
, &proxied_value
);
499 if (!Z_ISUNDEF(proxied_value
)) {
500 if (Z_TYPE(proxied_value
) == IS_ARRAY
) {
501 SEPARATE_ZVAL(&proxied_value
);
503 convert_to_array(&proxied_value
);
506 array_init(&proxied_value
);
509 Z_TRY_ADDREF_P(value
);
511 HT_ALLOW_COW_VIOLATION(Z_ARRVAL(proxied_value
));
513 zend_string
*zs
= zval_get_string(offset
);
514 zend_symtable_update(Z_ARRVAL(proxied_value
), zs
, value
);
515 zend_string_release(zs
);
517 zend_hash_next_index_insert(Z_ARRVAL(proxied_value
), value
);
520 set_proxied_value(object
, &proxied_value
);
522 zval_ptr_dtor(&proxied_value
);
524 debug_propro(-1, "dim_write", get_propro(object
), NULL
, offset
, &proxied_value
);
527 static void unset_dimension(zval
*object
, zval
*offset
)
531 debug_propro(1, "dim_unset", get_propro(object
), NULL
, offset
, NULL
);
533 ZVAL_UNDEF(&proxied_value
);
534 get_proxied_value(object
, &proxied_value
);
536 if (Z_TYPE(proxied_value
) == IS_ARRAY
) {
537 zend_string
*o
= zval_get_string(offset
);
539 SEPARATE_ZVAL(&proxied_value
);
541 zend_symtable_del(Z_ARRVAL(proxied_value
), o
);
542 set_proxied_value(object
, &proxied_value
);
544 zend_string_release(o
);
547 zval_ptr_dtor(&proxied_value
);
549 debug_propro(-1, "dim_unset", get_propro(object
), NULL
, offset
, &proxied_value
);
552 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct
, 0, 0, 2)
553 ZEND_ARG_INFO(1, object
)
554 ZEND_ARG_INFO(0, member
)
555 ZEND_ARG_OBJ_INFO(0, parent
, php
\\PropertyProxy
, 1)
557 static PHP_METHOD(propro
, __construct
) {
558 zend_error_handling zeh
;
559 zval
*container
, *parent
= NULL
;
562 zend_replace_error_handling(EH_THROW
, NULL
, &zeh
);
563 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS(), "z/S|O!",
564 &container
, &member
, &parent
,
565 php_property_proxy_class_entry
)) {
566 php_property_proxy_object_t
*obj
;
568 switch (Z_TYPE_P(container
)) {
573 convert_to_array(container
);
576 obj
= get_propro(getThis());
577 obj
->proxy
= php_property_proxy_init(container
, member
);
580 ZVAL_COPY(&obj
->parent
, parent
);
583 zend_restore_error_handling(&zeh
);
586 static const zend_function_entry php_property_proxy_method_entry
[] = {
587 PHP_ME(propro
, __construct
, ai_propro_construct
, ZEND_ACC_PUBLIC
)
591 static PHP_MINIT_FUNCTION(propro
)
593 zend_class_entry ce
= {0};
595 INIT_NS_CLASS_ENTRY(ce
, "php", "PropertyProxy",
596 php_property_proxy_method_entry
);
597 php_property_proxy_class_entry
= zend_register_internal_class(&ce
);
598 php_property_proxy_class_entry
->create_object
= php_property_proxy_object_new
;
599 php_property_proxy_class_entry
->ce_flags
|= ZEND_ACC_FINAL
;
601 memcpy(&php_property_proxy_object_handlers
, zend_get_std_object_handlers(),
602 sizeof(zend_object_handlers
));
603 php_property_proxy_object_handlers
.offset
= XtOffsetOf(php_property_proxy_object_t
, zo
);
604 php_property_proxy_object_handlers
.free_obj
= destroy_obj
;
605 php_property_proxy_object_handlers
.set
= set_proxied_value
;
606 php_property_proxy_object_handlers
.get
= get_proxied_value
;
607 php_property_proxy_object_handlers
.cast_object
= cast_proxied_value
;
608 php_property_proxy_object_handlers
.read_dimension
= read_dimension
;
609 php_property_proxy_object_handlers
.write_dimension
= write_dimension
;
610 php_property_proxy_object_handlers
.has_dimension
= has_dimension
;
611 php_property_proxy_object_handlers
.unset_dimension
= unset_dimension
;
616 PHP_MINFO_FUNCTION(propro
)
618 php_info_print_table_start();
619 php_info_print_table_header(2, "Property proxy support", "enabled");
620 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION
);
621 php_info_print_table_end();
624 static const zend_function_entry propro_functions
[] = {
628 zend_module_entry propro_module_entry
= {
629 STANDARD_MODULE_HEADER
,
638 STANDARD_MODULE_PROPERTIES
641 #ifdef COMPILE_DL_PROPRO
642 ZEND_GET_MODULE(propro
)
651 * vim600: noet sw=4 ts=4 fdm=marker
652 * vim<600: noet sw=4 ts=4