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.h"
23 #define DEBUG_PROPRO 0
25 static inline zval
*get_referenced_zval(zval
*ref
)
27 while (Z_ISREF_P(ref
)) {
28 ref
= Z_REFVAL_P(ref
);
33 php_property_proxy_t
*php_property_proxy_init(zval
*container
,
34 zend_string
*member TSRMLS_DC
)
36 php_property_proxy_t
*proxy
= ecalloc(1, sizeof(*proxy
));
38 ZVAL_COPY(&proxy
->container
, get_referenced_zval(container
));
39 zend_string_addref(member
);
40 proxy
->member
= member
;
45 void php_property_proxy_free(php_property_proxy_t
**proxy
)
48 zval_ptr_dtor(&(*proxy
)->container
);
49 zend_string_release((*proxy
)->member
);
55 static zend_class_entry
*php_property_proxy_class_entry
;
56 static zend_object_handlers php_property_proxy_object_handlers
;
58 zend_class_entry
*php_property_proxy_get_class_entry(void)
60 return php_property_proxy_class_entry
;
63 static inline php_property_proxy_object_t
*get_propro(zval
*object
);
64 static zval
*get_parent_proxied_value(zval
*object
, zval
*return_value TSRMLS_DC
);
65 static zval
*get_proxied_value(zval
*object
, zval
*return_value TSRMLS_DC
);
66 static zval
*read_dimension(zval
*object
, zval
*offset
, int type
, zval
*return_value TSRMLS_DC
);
67 static ZEND_RESULT_CODE
cast_proxied_value(zval
*object
, zval
*return_value
, int type TSRMLS_DC
);
68 static void write_dimension(zval
*object
, zval
*offset
, zval
*value TSRMLS_DC
);
69 static void set_proxied_value(zval
*object
, zval
*value TSRMLS_DC
);
72 /* we do not really care about TS when debugging */
74 static const char space
[] = " ";
75 static const char *inoutstr
[] = {"< return","="," > enter"};
77 static void _walk(php_property_proxy_object_t
*obj
)
80 if (!Z_ISUNDEF(obj
->parent
)) {
81 _walk(get_propro(&obj
->parent
));
84 fprintf(stderr
, ".%s", obj
->proxy
->member
->val
);
89 static void debug_propro(int inout
, const char *f
,
90 php_property_proxy_object_t
*obj
, zval
*offset
, zval
*value TSRMLS_DC
)
92 fprintf(stderr
, "#PP %p %s %s %s ", obj
, &space
[sizeof(space
)-level
],
93 inoutstr
[inout
+1], f
);
103 char *offset_str
= "[]";
107 convert_to_string_ex(o
);
108 offset_str
= Z_STRVAL_P(o
);
111 fprintf(stderr
, ".%s", offset_str
);
113 if (o
&& o
!= offset
) {
117 if (value
&& !Z_ISUNDEF_P(value
)) {
138 fprintf(stderr
, " = (%s) ", t
[Z_TYPE_P(value
)&0xf]);
139 if (!Z_ISUNDEF_P(value
) && Z_TYPE_P(value
) != IS_INDIRECT
) {
140 zend_print_flat_zval_r(value TSRMLS_CC
);
144 fprintf(stderr
, "\n");
147 #define debug_propro(l, f, obj, off, val)
150 static php_property_proxy_object_t
*new_propro(zend_class_entry
*ce
,
151 php_property_proxy_t
*proxy TSRMLS_DC
)
153 php_property_proxy_object_t
*o
;
156 ce
= php_property_proxy_class_entry
;
159 o
= ecalloc(1, sizeof(*o
) + sizeof(zval
) * (ce
->default_properties_count
- 1));
160 zend_object_std_init(&o
->zo
, ce TSRMLS_CC
);
161 object_properties_init(&o
->zo
, ce
);
164 o
->zo
.handlers
= &php_property_proxy_object_handlers
;
166 debug_propro(0, "init", o
, NULL
, NULL TSRMLS_CC
);
171 static zend_object
*create_obj(zend_class_entry
*ce TSRMLS_DC
)
173 return &new_propro(ce
, NULL TSRMLS_CC
)->zo
;
176 static void destroy_obj(zend_object
*object TSRMLS_DC
)
178 php_property_proxy_object_t
*o
= PHP_PROPRO_PTR(object
);
180 debug_propro(0, "dtor", o
, NULL
, NULL TSRMLS_CC
);
183 php_property_proxy_free(&o
->proxy
);
185 if (!Z_ISUNDEF(o
->parent
)) {
186 zval_ptr_dtor(&o
->parent
);
187 ZVAL_UNDEF(&o
->parent
);
191 static inline php_property_proxy_object_t
*get_propro(zval
*object
)
193 object
= get_referenced_zval(object
);
194 switch (Z_TYPE_P(object
)) {
198 EMPTY_SWITCH_DEFAULT_CASE();
200 return PHP_PROPRO_PTR(Z_OBJ_P(object
));
203 static inline zend_bool
got_value(zval
*container
, zval
*value TSRMLS_DC
)
207 if (!Z_ISUNDEF_P(value
)) {
208 if (SUCCESS
== is_identical_function(&identical
, value
, container TSRMLS_CC
)) {
209 if (Z_TYPE(identical
) != IS_TRUE
) {
218 static zval
*get_parent_proxied_value(zval
*object
, zval
*return_value TSRMLS_DC
)
220 php_property_proxy_object_t
*obj
;
222 obj
= get_propro(object
);
223 debug_propro(1, "parent_get", obj
, NULL
, NULL TSRMLS_CC
);
226 if (!Z_ISUNDEF(obj
->parent
)) {
227 get_proxied_value(&obj
->parent
, return_value TSRMLS_CC
);
231 debug_propro(-1, "parent_get", obj
, NULL
, return_value TSRMLS_CC
);
236 static zval
*get_proxied_value(zval
*object
, zval
*return_value TSRMLS_DC
)
238 zval
*hash_value
, *ref
;
239 php_property_proxy_object_t
*obj
;
241 obj
= get_propro(object
);
242 debug_propro(1, "get", obj
, NULL
, NULL TSRMLS_CC
);
245 if (!Z_ISUNDEF(obj
->parent
)) {
248 ZVAL_UNDEF(&parent_value
);
249 get_parent_proxied_value(object
, &parent_value TSRMLS_CC
);
251 if (got_value(&obj
->proxy
->container
, &parent_value TSRMLS_CC
)) {
252 zval_ptr_dtor(&obj
->proxy
->container
);
253 ZVAL_COPY(&obj
->proxy
->container
, &parent_value
);
257 ref
= get_referenced_zval(&obj
->proxy
->container
);
259 switch (Z_TYPE_P(ref
)) {
261 RETVAL_ZVAL(zend_read_property(Z_OBJCE_P(ref
), ref
,
262 obj
->proxy
->member
->val
, obj
->proxy
->member
->len
, 0 TSRMLS_CC
),
267 hash_value
= zend_symtable_find(Z_ARRVAL_P(ref
), obj
->proxy
->member
);
270 RETVAL_ZVAL(hash_value
, 0, 0);
276 debug_propro(-1, "get", obj
, NULL
, return_value TSRMLS_CC
);
281 static ZEND_RESULT_CODE
cast_proxied_value(zval
*object
, zval
*return_value
,
284 get_proxied_value(object
, return_value TSRMLS_CC
);
286 debug_propro(0, "cast", get_propro(object
), NULL
, return_value TSRMLS_CC
);
288 if (!Z_ISUNDEF_P(return_value
)) {
289 convert_to_explicit_type_ex(return_value
, type
);
296 static void set_proxied_value(zval
*object
, zval
*value TSRMLS_DC
)
298 php_property_proxy_object_t
*obj
;
301 obj
= get_propro(object
);
302 debug_propro(1, "set", obj
, NULL
, value TSRMLS_CC
);
305 if (!Z_ISUNDEF(obj
->parent
)) {
308 ZVAL_UNDEF(&parent_value
);
309 get_parent_proxied_value(object
, &parent_value TSRMLS_CC
);
311 if (got_value(&obj
->proxy
->container
, &parent_value TSRMLS_CC
)) {
312 zval_ptr_dtor(&obj
->proxy
->container
);
313 ZVAL_COPY(&obj
->proxy
->container
, &parent_value
);
317 ref
= get_referenced_zval(&obj
->proxy
->container
);
319 switch (Z_TYPE_P(ref
)) {
321 zend_update_property(Z_OBJCE_P(ref
), ref
, obj
->proxy
->member
->val
,
322 obj
->proxy
->member
->len
, value TSRMLS_CC
);
326 convert_to_array(ref
);
330 Z_TRY_ADDREF_P(value
);
331 zend_symtable_update(Z_ARRVAL_P(ref
), obj
->proxy
->member
, value
);
335 if (!Z_ISUNDEF(obj
->parent
)) {
336 set_proxied_value(&obj
->parent
, &obj
->proxy
->container TSRMLS_CC
);
340 debug_propro(-1, "set", obj
, NULL
, NULL TSRMLS_CC
);
343 static zval
*read_dimension(zval
*object
, zval
*offset
, int type
, zval
*return_value TSRMLS_DC
)
348 debug_propro(1, type
== BP_VAR_R
? "dim_read" : "dim_read_ref",
349 get_propro(object
), offset
, NULL TSRMLS_CC
);
351 ZVAL_UNDEF(&proxied_value
);
352 get_proxied_value(object
, &proxied_value TSRMLS_CC
);
355 convert_to_string_ex(o
);
358 if (BP_VAR_R
== type
&& o
&& !Z_ISUNDEF(proxied_value
)) {
359 if (Z_TYPE(proxied_value
) == IS_ARRAY
) {
360 zval
*hash_value
= zend_symtable_find(Z_ARRVAL(proxied_value
),
364 RETVAL_ZVAL(hash_value
, 1, 0);
369 php_property_proxy_t
*proxy
;
370 php_property_proxy_object_t
*proxy_obj
;
372 if (!Z_ISUNDEF(proxied_value
)) {
373 convert_to_array(&proxied_value
);
374 Z_ADDREF(proxied_value
);
376 array_init(&proxied_value
);
377 set_proxied_value(object
, &proxied_value TSRMLS_CC
);
383 member
= zend_long_to_str(zend_hash_next_free_element(
384 Z_ARRVAL(proxied_value
)));
387 proxy
= php_property_proxy_init(&proxied_value
, member TSRMLS_CC
);
388 zval_ptr_dtor(&proxied_value
);
391 zend_string_release(member
);
394 proxy_obj
= new_propro(NULL
, proxy TSRMLS_CC
);
395 ZVAL_COPY(&proxy_obj
->parent
, object
);
396 RETVAL_OBJ(&proxy_obj
->zo
);
399 if (o
&& o
!= offset
) {
403 debug_propro(-1, type
== BP_VAR_R
? "dim_read" : "dim_read_ref",
404 get_propro(object
), offset
, return_value TSRMLS_CC
);
409 static int has_dimension(zval
*object
, zval
*offset
, int check_empty TSRMLS_DC
)
414 debug_propro(1, "dim_exists", get_propro(object
), offset
, NULL TSRMLS_CC
);
416 ZVAL_UNDEF(&proxied_value
);
417 get_proxied_value(object
, &proxied_value TSRMLS_CC
);
418 if (Z_ISUNDEF(proxied_value
)) {
423 convert_to_string_ex(o
);
425 if (Z_TYPE(proxied_value
) == IS_ARRAY
) {
426 zval
*zentry
= zend_symtable_find(Z_ARRVAL(proxied_value
), Z_STR_P(o
));
432 exists
= !Z_ISNULL_P(zentry
);
444 debug_propro(-1, "dim_exists", get_propro(object
), offset
, NULL TSRMLS_CC
);
449 static void write_dimension(zval
*object
, zval
*offset
, zval
*value TSRMLS_DC
)
451 zval proxied_value
, *o
= offset
;
453 debug_propro(1, "dim_write", get_propro(object
), offset
, value TSRMLS_CC
);
455 ZVAL_UNDEF(&proxied_value
);
456 get_proxied_value(object
, &proxied_value TSRMLS_CC
);
458 if (!Z_ISUNDEF(proxied_value
)) {
459 if (Z_TYPE(proxied_value
) == IS_ARRAY
) {
460 Z_ADDREF(proxied_value
);
462 convert_to_array(&proxied_value
);
465 array_init(&proxied_value
);
468 SEPARATE_ZVAL(value
);
469 Z_TRY_ADDREF_P(value
);
472 convert_to_string_ex(o
);
473 zend_symtable_update(Z_ARRVAL(proxied_value
), Z_STR_P(o
), value
);
475 zend_hash_next_index_insert(Z_ARRVAL(proxied_value
), value
);
478 if (o
&& o
!= offset
) {
482 set_proxied_value(object
, &proxied_value TSRMLS_CC
);
484 debug_propro(-1, "dim_write", get_propro(object
), offset
, &proxied_value TSRMLS_CC
);
486 zval_ptr_dtor(&proxied_value
);
489 static void unset_dimension(zval
*object
, zval
*offset TSRMLS_DC
)
493 debug_propro(1, "dim_unset", get_propro(object
), offset
, NULL TSRMLS_CC
);
495 get_proxied_value(object
, &proxied_value TSRMLS_CC
);
497 if (Z_TYPE(proxied_value
) == IS_ARRAY
) {
501 convert_to_string_ex(o
);
502 rv
= zend_symtable_del(Z_ARRVAL(proxied_value
), Z_STR_P(o
));
504 set_proxied_value(object
, &proxied_value TSRMLS_CC
);
512 debug_propro(-1, "dim_unset", get_propro(object
), offset
, &proxied_value TSRMLS_CC
);
515 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct
, 0, 0, 2)
516 ZEND_ARG_INFO(1, object
)
517 ZEND_ARG_INFO(0, member
)
518 ZEND_ARG_OBJ_INFO(0, parent
, php
\\PropertyProxy
, 1)
520 static PHP_METHOD(propro
, __construct
) {
521 zend_error_handling zeh
;
522 zval
*container
, *parent
= NULL
;
525 zend_replace_error_handling(EH_THROW
, NULL
, &zeh TSRMLS_CC
);
526 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "zS|O!",
527 &container
, &member
, &parent
,
528 php_property_proxy_class_entry
)) {
529 php_property_proxy_object_t
*obj
;
530 zval
*ref
= get_referenced_zval(container
);
532 switch (Z_TYPE_P(ref
)) {
537 convert_to_array(ref
);
539 obj
= get_propro(getThis());
540 obj
->proxy
= php_property_proxy_init(container
, member TSRMLS_CC
);
542 ZVAL_COPY(&obj
->parent
, parent
);
545 zend_restore_error_handling(&zeh TSRMLS_CC
);
548 static const zend_function_entry php_property_proxy_method_entry
[] = {
549 PHP_ME(propro
, __construct
, ai_propro_construct
, ZEND_ACC_PUBLIC
)
553 static PHP_MINIT_FUNCTION(propro
)
555 zend_class_entry ce
= {0};
557 INIT_NS_CLASS_ENTRY(ce
, "php", "PropertyProxy",
558 php_property_proxy_method_entry
);
559 php_property_proxy_class_entry
= zend_register_internal_class(&ce TSRMLS_CC
);
560 php_property_proxy_class_entry
->create_object
= create_obj
;
561 php_property_proxy_class_entry
->ce_flags
|= ZEND_ACC_FINAL
;
563 memcpy(&php_property_proxy_object_handlers
, zend_get_std_object_handlers(),
564 sizeof(zend_object_handlers
));
565 php_property_proxy_object_handlers
.offset
= XtOffsetOf(php_property_proxy_object_t
, zo
);
566 php_property_proxy_object_handlers
.dtor_obj
= destroy_obj
;
567 php_property_proxy_object_handlers
.set
= set_proxied_value
;
568 php_property_proxy_object_handlers
.get
= get_proxied_value
;
569 php_property_proxy_object_handlers
.cast_object
= cast_proxied_value
;
570 php_property_proxy_object_handlers
.read_dimension
= read_dimension
;
571 php_property_proxy_object_handlers
.write_dimension
= write_dimension
;
572 php_property_proxy_object_handlers
.has_dimension
= has_dimension
;
573 php_property_proxy_object_handlers
.unset_dimension
= unset_dimension
;
578 PHP_MINFO_FUNCTION(propro
)
580 php_info_print_table_start();
581 php_info_print_table_header(2, "Property proxy support", "enabled");
582 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION
);
583 php_info_print_table_end();
586 static const zend_function_entry propro_functions
[] = {
590 zend_module_entry propro_module_entry
= {
591 STANDARD_MODULE_HEADER
,
600 STANDARD_MODULE_PROPERTIES
603 #ifdef COMPILE_DL_PROPRO
604 ZEND_GET_MODULE(propro
)
613 * vim600: noet sw=4 ts=4 fdm=marker
614 * vim<600: noet sw=4 ts=4