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"
25 #define DEBUG_PROPRO 0
30 # define zval_ptr_dtor(zpp) _zval_ptr_dtor(*(zpp) ZEND_FILE_LINE_CC)
33 #if PHP_VERSION_ID < 50400
34 # define object_properties_init(o, ce) \
35 zend_hash_copy(((zend_object *) o)->properties, &(ce->default_properties), \
36 (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*))
39 php_property_proxy_t
*php_property_proxy_init(zval
*container
,
40 const char *member_str
, size_t member_len TSRMLS_DC
)
42 php_property_proxy_t
*proxy
= ecalloc(1, sizeof(*proxy
));
44 Z_ADDREF_P(container
);
45 proxy
->container
= container
;
46 proxy
->member_str
= estrndup(member_str
, member_len
);
47 proxy
->member_len
= member_len
;
52 void php_property_proxy_free(php_property_proxy_t
**proxy
)
55 zval_ptr_dtor(&(*proxy
)->container
);
56 efree((*proxy
)->member_str
);
62 static zend_class_entry
*php_property_proxy_class_entry
;
63 static zend_object_handlers php_property_proxy_object_handlers
;
65 zend_class_entry
*php_property_proxy_get_class_entry(void)
67 return php_property_proxy_class_entry
;
70 zend_object_value
php_property_proxy_object_new(zend_class_entry
*ce TSRMLS_DC
)
72 return php_property_proxy_object_new_ex(ce
, NULL
, NULL TSRMLS_CC
);
75 static void php_property_proxy_object_free(void *object TSRMLS_DC
)
77 php_property_proxy_object_t
*o
= object
;
80 fprintf(stderr
, "#PP %p free\n", o
);
84 php_property_proxy_free(&o
->proxy
);
88 zend_objects_store_del(&o
->parent
->zv
);
90 zend_objects_store_del_ref_by_handle_ex(o
->parent
->zv
.handle
,
91 o
->parent
->zv
.handlers TSRMLS_CC
);
95 zend_object_std_dtor((zend_object
*) o TSRMLS_CC
);
99 zend_object_value
php_property_proxy_object_new_ex(zend_class_entry
*ce
,
100 php_property_proxy_t
*proxy
, php_property_proxy_object_t
**ptr TSRMLS_DC
)
102 php_property_proxy_object_t
*o
;
104 o
= ecalloc(1, sizeof(*o
));
105 zend_object_std_init((zend_object
*) o
, ce TSRMLS_CC
);
106 object_properties_init((zend_object
*) o
, ce
);
113 o
->zv
.handle
= zend_objects_store_put(o
, NULL
,
114 php_property_proxy_object_free
, NULL TSRMLS_CC
);
115 o
->zv
.handlers
= &php_property_proxy_object_handlers
;
118 fprintf(stderr
, "#PP %p init\n", o
);
125 /* we do not really care about TS when debugging */
126 static int level
= 1;
127 static const char space
[] = " ";
128 static const char *inoutstr
[] = {"< return",""," > enter"};
129 static void _walk(php_property_proxy_object_t
*obj TSRMLS_DC
)
132 _walk(obj
->parent TSRMLS_CC
);
133 fprintf(stderr
, ".%s", obj
->proxy
->member_str
);
137 static void debug_propro(int inout
, const char *f
, zval
*object
, zval
*offset
,
138 zval
*value TSRMLS_DC
)
140 php_property_proxy_object_t
*obj
;
142 obj
= zend_object_store_get_object(object TSRMLS_CC
);
143 fprintf(stderr
, "#PP %p %s %s %s ", obj
, &space
[sizeof(space
)-level
],
144 inoutstr
[inout
+1], f
);
148 _walk(obj TSRMLS_CC
);
154 char *offset_str
= "[]";
158 convert_to_string_ex(&o
);
159 offset_str
= Z_STRVAL_P(o
);
162 fprintf(stderr
, ".%s", offset_str
);
164 if (o
&& o
!= offset
) {
182 fprintf(stderr
, " = (%s) ", t
[Z_TYPE_P(value
)&0xf]);
183 zend_print_flat_zval_r(value TSRMLS_CC
);
186 fprintf(stderr
, "\n");
189 #define debug_propro(l, f, obj, off, val)
192 static zval
*get_parent_proxied_value(zval
*object TSRMLS_DC
);
193 static zval
*get_proxied_value(zval
*object TSRMLS_DC
);
194 static zval
*read_dimension(zval
*object
, zval
*offset
, int type TSRMLS_DC
);
195 static STATUS
cast_proxied_value(zval
*object
, zval
*return_value
,
197 static void write_dimension(zval
*object
, zval
*offset
, zval
*value TSRMLS_DC
);
198 static void set_proxied_value(zval
**object
, zval
*value TSRMLS_DC
);
200 static zval
*get_parent_proxied_value(zval
*object TSRMLS_DC
)
203 php_property_proxy_object_t
*obj
;
205 obj
= zend_object_store_get_object(object TSRMLS_CC
);
210 MAKE_STD_ZVAL(parent
);
211 parent
->type
= IS_OBJECT
;
212 parent
->value
.obj
= obj
->parent
->zv
;
213 zend_objects_store_add_ref_by_handle(
214 obj
->parent
->zv
.handle TSRMLS_CC
);
215 value
= get_proxied_value(parent TSRMLS_CC
);
216 zval_ptr_dtor(&parent
);
223 static zval
*get_proxied_value(zval
*object TSRMLS_DC
)
225 zval
**hash_value
, *value
= NULL
;
226 php_property_proxy_object_t
*obj
;
229 obj
= zend_object_store_get_object(object TSRMLS_CC
);
230 debug_propro(1, "get", object
, NULL
, NULL TSRMLS_CC
);
234 zval
*parent_value
= get_parent_proxied_value(object TSRMLS_CC
);
236 if (parent_value
&& parent_value
!= obj
->proxy
->container
) {
237 Z_ADDREF_P(parent_value
);
238 zval_ptr_dtor(&obj
->proxy
->container
);
239 obj
->proxy
->container
= parent_value
;
242 switch (Z_TYPE_P(obj
->proxy
->container
)) {
244 value
= zend_read_property(Z_OBJCE_P(obj
->proxy
->container
),
245 obj
->proxy
->container
, obj
->proxy
->member_str
,
246 obj
->proxy
->member_len
, 0 TSRMLS_CC
);
250 rv
= zend_symtable_find(Z_ARRVAL_P(obj
->proxy
->container
),
251 obj
->proxy
->member_str
, obj
->proxy
->member_len
+ 1,
252 (void *) &hash_value
);
261 debug_propro(-1, "get", object
, NULL
, value TSRMLS_CC
);
266 static STATUS
cast_proxied_value(zval
*object
, zval
*return_value
,
271 if ((proxied_value
= get_proxied_value(object TSRMLS_CC
))) {
272 RETVAL_ZVAL(proxied_value
, 1, 0);
273 if (Z_TYPE_P(proxied_value
) != type
) {
274 convert_to_explicit_type(return_value
, type
);
282 static void set_proxied_value(zval
**object
, zval
*value TSRMLS_DC
)
284 php_property_proxy_object_t
*obj
;
286 obj
= zend_object_store_get_object(*object TSRMLS_CC
);
287 debug_propro(1, "set", *object
, NULL
, value TSRMLS_CC
);
291 zval
*parent_value
= get_parent_proxied_value(*object TSRMLS_CC
);
293 if (parent_value
&& parent_value
!= obj
->proxy
->container
) {
294 Z_ADDREF_P(parent_value
);
295 zval_ptr_dtor(&obj
->proxy
->container
);
296 obj
->proxy
->container
= parent_value
;
300 switch (Z_TYPE_P(obj
->proxy
->container
)) {
302 zend_update_property(Z_OBJCE_P(obj
->proxy
->container
),
303 obj
->proxy
->container
, obj
->proxy
->member_str
,
304 obj
->proxy
->member_len
, value TSRMLS_CC
);
309 zend_symtable_update(Z_ARRVAL_P(obj
->proxy
->container
),
310 obj
->proxy
->member_str
, obj
->proxy
->member_len
+ 1,
311 (void *) &value
, sizeof(zval
*), NULL
);
317 MAKE_STD_ZVAL(zparent
);
318 zparent
->type
= IS_OBJECT
;
319 zparent
->value
.obj
= obj
->parent
->zv
;
320 zend_objects_store_add_ref_by_handle(
321 obj
->parent
->zv
.handle TSRMLS_CC
);
322 set_proxied_value(&zparent
, obj
->proxy
->container TSRMLS_CC
);
323 zval_ptr_dtor(&zparent
);
327 debug_propro(-1, "set", *object
, NULL
, NULL TSRMLS_CC
);
330 static zval
*read_dimension(zval
*object
, zval
*offset
, int type TSRMLS_DC
)
336 debug_propro(1, type
== BP_VAR_R
? "dim_read" : "dim_read_ref", object
,
337 offset
, NULL TSRMLS_CC
);
339 proxied_value
= get_proxied_value(object TSRMLS_CC
);
340 convert_to_string_ex(&o
);
342 if (BP_VAR_R
== type
&& proxied_value
) {
343 if (Z_TYPE_P(proxied_value
) == IS_ARRAY
) {
345 STATUS rv
= zend_symtable_find(Z_ARRVAL_P(proxied_value
),
346 Z_STRVAL_P(o
), Z_STRLEN_P(o
), (void *) &hash_value
);
349 Z_ADDREF_PP(hash_value
);
354 php_property_proxy_t
*proxy
;
355 php_property_proxy_object_t
*proxy_obj
;
358 convert_to_array(proxied_value
);
359 Z_ADDREF_P(proxied_value
);
361 MAKE_STD_ZVAL(proxied_value
);
362 array_init(proxied_value
);
363 set_proxied_value(&object
, proxied_value TSRMLS_CC
);
366 proxy
= php_property_proxy_init(proxied_value
, Z_STRVAL_P(o
),
367 Z_STRLEN_P(o
) TSRMLS_CC
);
368 zval_ptr_dtor(&proxied_value
);
369 MAKE_STD_ZVAL(value
);
370 Z_SET_REFCOUNT_P(value
, 0);
371 value
->type
= IS_OBJECT
;
372 value
->value
.obj
= php_property_proxy_object_new_ex(
373 php_property_proxy_get_class_entry(), proxy
,
374 &proxy_obj TSRMLS_CC
);
375 proxy_obj
->parent
= zend_object_store_get_object(object TSRMLS_CC
);
376 zend_objects_store_add_ref_by_handle(
377 proxy_obj
->parent
->zv
.handle TSRMLS_CC
);
379 if (o
&& o
!= offset
) {
383 debug_propro(-1, type
== BP_VAR_R
? "dim_read" : "dim_read_ref", object
,
384 offset
, value TSRMLS_CC
);
389 static int has_dimension(zval
*object
, zval
*offset
, int check_empty TSRMLS_DC
)
394 debug_propro(1, "dim_exists", object
, offset
, NULL TSRMLS_CC
);
396 proxied_value
= get_proxied_value(object TSRMLS_CC
);
397 if (!proxied_value
) {
402 convert_to_string_ex(&o
);
405 if (Z_TYPE_P(proxied_value
) == IS_ARRAY
) {
406 zval
*zentry
= zend_symtable_find(Z_ARRVAL_P(proxied_value
), Z_STR_P(o
));
412 exists
= Z_TYPE_PP(zentry
) != IS_NULL
;
419 if (Z_TYPE_P(proxied_value
) == IS_ARRAY
) {
421 STATUS rv
= zend_symtable_find(Z_ARRVAL_P(proxied_value
), Z_STRVAL_P(o
), Z_STRLEN_P(o
) + 1, (void *) &zentry
);
427 exists
= Z_TYPE_PP(zentry
) != IS_NULL
;
440 debug_propro(-1, "dim_exists", object
, offset
, NULL TSRMLS_CC
);
445 static void write_dimension(zval
*object
, zval
*offset
, zval
*value TSRMLS_DC
)
447 zval
*proxied_value
, *o
= offset
;
449 debug_propro(1, "dim_write", object
, offset
, value TSRMLS_CC
);
451 proxied_value
= get_proxied_value(object TSRMLS_CC
);
454 convert_to_array(proxied_value
);
455 Z_ADDREF_P(proxied_value
);
457 MAKE_STD_ZVAL(proxied_value
);
458 array_init(proxied_value
);
461 if (Z_REFCOUNT_P(value
) > 1) {
462 SEPARATE_ZVAL(&value
);
468 convert_to_string_ex(o
);
469 zend_symtable_update(Z_ARRVAL_P(proxied_value
), Z_STR_P(o
), value
);
471 zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value
), value
);
475 convert_to_string_ex(&o
);
476 zend_symtable_update(Z_ARRVAL_P(proxied_value
), Z_STRVAL_P(o
),
477 Z_STRLEN_P(o
) + 1, (void *) &value
, sizeof(zval
*), NULL
);
479 zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value
), (void *) &value
,
480 sizeof(zval
*), NULL
);
483 if (o
&& o
!= offset
) {
488 set_proxied_value(&object
, proxied_value TSRMLS_CC
);
490 debug_propro(-1, "dim_write", object
, offset
, proxied_value TSRMLS_CC
);
492 zval_ptr_dtor(&proxied_value
);
495 static void unset_dimension(zval
*object
, zval
*offset TSRMLS_DC
)
499 debug_propro(1, "dim_unset", object
, offset
, NULL TSRMLS_CC
);
501 proxied_value
= get_proxied_value(object TSRMLS_CC
);
503 if (proxied_value
&& Z_TYPE_P(proxied_value
) == IS_ARRAY
) {
507 convert_to_string_ex(&o
);
509 rv
= zend_symtable_del(Z_ARRVAL_P(proxied_value
), Z_STR_P(o
));
511 rv
= zend_symtable_del(Z_ARRVAL_P(proxied_value
), Z_STRVAL_P(o
),
515 set_proxied_value(&object
, proxied_value TSRMLS_CC
);
523 debug_propro(-1, "dim_unset", object
, offset
, proxied_value TSRMLS_CC
);
526 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct
, 0, 0, 2)
527 ZEND_ARG_INFO(1, object
)
528 ZEND_ARG_INFO(0, member
)
529 ZEND_ARG_OBJ_INFO(0, parent
, php
\\PropertyProxy
, 1)
531 static PHP_METHOD(propro
, __construct
) {
532 zend_error_handling zeh
;
533 zval
*container
, *parent
= NULL
;
537 zend_replace_error_handling(EH_THROW
, NULL
, &zeh TSRMLS_CC
);
538 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "zs|O!",
539 &container
, &member_str
, &member_len
, &parent
,
540 php_property_proxy_class_entry
)) {
541 php_property_proxy_object_t
*obj
;
543 obj
= zend_object_store_get_object(getThis() TSRMLS_CC
);
544 obj
->proxy
= php_property_proxy_init(container
, member_str
,
545 member_len TSRMLS_CC
);
547 zend_objects_store_add_ref(parent TSRMLS_CC
);
548 obj
->parent
= zend_object_store_get_object(parent TSRMLS_CC
);
551 zend_restore_error_handling(&zeh TSRMLS_CC
);
554 static const zend_function_entry php_property_proxy_method_entry
[] = {
555 PHP_ME(propro
, __construct
, ai_propro_construct
, ZEND_ACC_PUBLIC
)
559 static PHP_MINIT_FUNCTION(propro
)
561 zend_class_entry ce
= {0};
563 INIT_NS_CLASS_ENTRY(ce
, "php", "PropertyProxy",
564 php_property_proxy_method_entry
);
565 php_property_proxy_class_entry
= zend_register_internal_class(&ce TSRMLS_CC
);
566 php_property_proxy_class_entry
->create_object
=
567 php_property_proxy_object_new
;
568 php_property_proxy_class_entry
->ce_flags
|= ZEND_ACC_FINAL_CLASS
;
570 memcpy(&php_property_proxy_object_handlers
, zend_get_std_object_handlers(),
571 sizeof(zend_object_handlers
));
572 php_property_proxy_object_handlers
.set
= set_proxied_value
;
573 php_property_proxy_object_handlers
.get
= get_proxied_value
;
574 php_property_proxy_object_handlers
.cast_object
= cast_proxied_value
;
575 php_property_proxy_object_handlers
.read_dimension
= read_dimension
;
576 php_property_proxy_object_handlers
.write_dimension
= write_dimension
;
577 php_property_proxy_object_handlers
.has_dimension
= has_dimension
;
578 php_property_proxy_object_handlers
.unset_dimension
= unset_dimension
;
583 PHP_MINFO_FUNCTION(propro
)
585 php_info_print_table_start();
586 php_info_print_table_header(2, "Property proxy support", "enabled");
587 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION
);
588 php_info_print_table_end();
591 static const zend_function_entry propro_functions
[] = {
595 zend_module_entry propro_module_entry
= {
596 STANDARD_MODULE_HEADER
,
605 STANDARD_MODULE_PROPERTIES
608 #ifdef COMPILE_DL_PROPRO
609 ZEND_GET_MODULE(propro
)
618 * vim600: noet sw=4 ts=4 fdm=marker
619 * vim<600: noet sw=4 ts=4