add parent recursion support; package.xml
[m6w6/ext-propro] / php_propro.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: propro |
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 +--------------------------------------------------------------------+
11 */
12
13
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #include <php.h>
19 #include <ext/standard/info.h>
20
21 #include "php_propro.h"
22
23 typedef int STATUS;
24
25 PHP_PROPRO_API php_property_proxy_t *php_property_proxy_init(zval *container, const char *member_str, size_t member_len TSRMLS_DC)
26 {
27 php_property_proxy_t *proxy = ecalloc(1, sizeof(*proxy));
28
29 Z_ADDREF_P(container);
30 proxy->container = container;
31 proxy->member_str = estrndup(member_str, member_len);
32 proxy->member_len = member_len;
33
34 return proxy;
35 }
36
37 PHP_PROPRO_API void php_property_proxy_free(php_property_proxy_t **proxy)
38 {
39 if (*proxy) {
40 zval_ptr_dtor(&(*proxy)->container);
41 efree((*proxy)->member_str);
42 efree(*proxy);
43 *proxy = NULL;
44 }
45 }
46
47 static zend_class_entry *php_property_proxy_class_entry;
48 static zend_object_handlers php_property_proxy_object_handlers;
49
50 PHP_PROPRO_API zend_class_entry *php_property_proxy_get_class_entry(void)
51 {
52 return php_property_proxy_class_entry;
53 }
54
55 PHP_PROPRO_API zend_object_value php_property_proxy_object_new(zend_class_entry *ce TSRMLS_DC)
56 {
57 return php_property_proxy_object_new_ex(ce, NULL, NULL TSRMLS_CC);
58 }
59
60 static void php_property_proxy_object_free(void *object TSRMLS_DC)
61 {
62 php_property_proxy_object_t *o = object;
63
64 if (o->proxy) {
65 php_property_proxy_free(&o->proxy);
66 }
67 if (o->parent) {
68 zend_objects_store_del_ref_by_handle_ex(o->parent->zv.handle, o->parent->zv.handlers TSRMLS_CC);
69 o->parent = NULL;
70 }
71 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
72 efree(o);
73 }
74
75 PHP_PROPRO_API zend_object_value php_property_proxy_object_new_ex(zend_class_entry *ce, php_property_proxy_t *proxy, php_property_proxy_object_t **ptr TSRMLS_DC)
76 {
77 php_property_proxy_object_t *o;
78
79 o = ecalloc(1, sizeof(*o));
80 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
81 object_properties_init((zend_object *) o, ce);
82
83 if (ptr) {
84 *ptr = o;
85 }
86 o->proxy = proxy;
87
88 o->zv.handle = zend_objects_store_put(o, NULL, php_property_proxy_object_free, NULL TSRMLS_CC);
89 o->zv.handlers = &php_property_proxy_object_handlers;
90
91 return o->zv;
92 }
93
94 static zval *get_parent_proxied_value(zval *object TSRMLS_DC);
95 static zval *get_proxied_value(zval *object TSRMLS_DC);
96 static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
97 static STATUS cast_proxied_value(zval *object, zval *return_value, int type TSRMLS_DC);
98 static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC);
99 static void set_proxied_value(zval **object, zval *value TSRMLS_DC);
100
101 static zval *get_parent_proxied_value(zval *object TSRMLS_DC)
102 {
103 zval *value = NULL;
104 php_property_proxy_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
105
106 if (obj->proxy) {
107 if (obj->parent) {
108 zval *parent;
109
110 MAKE_STD_ZVAL(parent);
111 parent->type = IS_OBJECT;
112 parent->value.obj = obj->parent->zv;
113 zend_objects_store_add_ref_by_handle(obj->parent->zv.handle TSRMLS_CC);
114 value = get_proxied_value(parent TSRMLS_CC);
115 zval_ptr_dtor(&parent);
116 }
117 }
118
119 return value;
120 }
121
122 static zval *get_proxied_value(zval *object TSRMLS_DC)
123 {
124 zval **hash_value, *value = NULL;
125 php_property_proxy_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
126
127 if (obj->proxy) {
128 if (obj->parent) {
129 zval *parent_value = get_parent_proxied_value(object TSRMLS_CC);
130
131 Z_ADDREF_P(parent_value);
132 zval_ptr_dtor(&obj->proxy->container);
133 obj->proxy->container = parent_value;
134 }
135
136 switch (Z_TYPE_P(obj->proxy->container)) {
137 case IS_OBJECT:
138 value = zend_read_property(Z_OBJCE_P(obj->proxy->container), obj->proxy->container, obj->proxy->member_str, obj->proxy->member_len, 0 TSRMLS_CC);
139 break;
140
141 case IS_ARRAY:
142 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(obj->proxy->container), obj->proxy->member_str, obj->proxy->member_len + 1, (void *) &hash_value)) {
143 value = *hash_value;
144 }
145 break;
146 }
147 }
148
149 return value;
150 }
151
152 static STATUS cast_proxied_value(zval *object, zval *return_value, int type TSRMLS_DC)
153 {
154 zval *proxied_value;
155
156 if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
157 RETVAL_ZVAL(proxied_value, 1, 0);
158 if (Z_TYPE_P(proxied_value) != type) {
159 convert_to_explicit_type(return_value, type);
160 }
161 return SUCCESS;
162 }
163
164 return FAILURE;
165 }
166
167 static void set_proxied_value(zval **object, zval *value TSRMLS_DC)
168 {
169 php_property_proxy_object_t *obj = zend_object_store_get_object(*object TSRMLS_CC);
170
171 if (obj->proxy) {
172 if (obj->parent) {
173 zval *parent_value = get_parent_proxied_value(*object TSRMLS_CC);
174
175 Z_ADDREF_P(parent_value);
176 zval_ptr_dtor(&obj->proxy->container);
177 obj->proxy->container = parent_value;
178 }
179
180 switch (Z_TYPE_P(obj->proxy->container)) {
181 case IS_OBJECT:
182 zend_update_property(Z_OBJCE_P(obj->proxy->container), obj->proxy->container, obj->proxy->member_str, obj->proxy->member_len, value TSRMLS_CC);
183 break;
184
185 case IS_ARRAY:
186 Z_ADDREF_P(value);
187 zend_symtable_update(Z_ARRVAL_P(obj->proxy->container), obj->proxy->member_str, obj->proxy->member_len + 1, (void *) &value, sizeof(zval *), NULL);
188 break;
189 }
190
191 if (obj->parent) {
192 zval *zparent;
193 MAKE_STD_ZVAL(zparent);
194 zparent->type = IS_OBJECT;
195 zparent->value.obj = obj->parent->zv;
196 zend_objects_store_add_ref_by_handle(obj->parent->zv.handle TSRMLS_CC);
197 set_proxied_value(&zparent, obj->proxy->container TSRMLS_CC);
198 zval_ptr_dtor(&zparent);
199 }
200 }
201 }
202
203 static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
204 {
205 zval *value = NULL;
206 zval *proxied_value = get_proxied_value(object TSRMLS_CC);
207 zval *o = offset;
208
209 convert_to_string_ex(&o);
210
211 if (BP_VAR_R == type && proxied_value) {
212 if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
213 zval **hash_value;
214 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o), (void *) &hash_value)) {
215 Z_ADDREF_PP(hash_value);
216 value = *hash_value;
217 }
218 }
219 } else {
220 php_property_proxy_t *proxy;
221 php_property_proxy_object_t *proxy_obj;
222
223 if (proxied_value) {
224 convert_to_array(proxied_value);
225 Z_ADDREF_P(proxied_value);
226 } else {
227 MAKE_STD_ZVAL(proxied_value);
228 array_init(proxied_value);
229 set_proxied_value(&object, proxied_value TSRMLS_CC);
230 }
231
232 add_assoc_null_ex(proxied_value, Z_STRVAL_P(o), Z_STRLEN_P(o) + 1);
233
234 proxy = php_property_proxy_init(proxied_value, Z_STRVAL_P(o), Z_STRLEN_P(o) TSRMLS_CC);
235 zval_ptr_dtor(&proxied_value);
236 MAKE_STD_ZVAL(value);
237 Z_SET_REFCOUNT_P(value, 0);
238 value->type = IS_OBJECT;
239 value->value.obj = php_property_proxy_object_new_ex(php_property_proxy_get_class_entry(), proxy, &proxy_obj TSRMLS_CC);
240 proxy_obj->parent = zend_object_store_get_object(object TSRMLS_CC);
241 zend_objects_store_add_ref_by_handle(proxy_obj->parent->zv.handle TSRMLS_CC);
242 }
243 if (o && o != offset) {
244 zval_ptr_dtor(&o);
245 }
246
247 return value;
248 }
249
250 static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
251 {
252 zval *proxied_value, *o = offset;
253 php_property_proxy_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
254
255 if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
256 convert_to_array(proxied_value);
257 Z_ADDREF_P(proxied_value);
258 } else {
259 MAKE_STD_ZVAL(proxied_value);
260 array_init(proxied_value);
261 }
262
263 if (Z_REFCOUNT_P(value) > 1) {
264 SEPARATE_ZVAL(&value);
265 }
266 Z_ADDREF_P(value);
267
268 if (o) {
269 convert_to_string_ex(&o);
270 zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &value, sizeof(zval *), NULL);
271 } else {
272 zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value), (void *) &value, sizeof(zval *), NULL);
273 }
274
275 if (o && o != offset) {
276 zval_ptr_dtor(&o);
277 }
278
279 set_proxied_value(&object, proxied_value TSRMLS_CC);
280 zval_ptr_dtor(&proxied_value);
281 }
282
283 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct, 0, 0, 2)
284 ZEND_ARG_INFO(0, object)
285 ZEND_ARG_INFO(0, member)
286 ZEND_ARG_OBJ_INFO(0, parent, php\\PropertyProxy, 1)
287 ZEND_END_ARG_INFO();
288 static PHP_METHOD(propro, __construct) {
289 zend_error_handling zeh;
290 zval *container, *parent = NULL;
291 char *member_str;
292 int member_len;
293
294 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
295 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|O!", &container, &member_str, &member_len, &parent, php_property_proxy_class_entry)) {
296 php_property_proxy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
297
298 obj->proxy = php_property_proxy_init(container, member_str, member_len TSRMLS_CC);
299 if (parent) {
300 zend_objects_store_add_ref(parent TSRMLS_CC);
301 obj->parent = zend_object_store_get_object(parent TSRMLS_CC);
302 }
303 }
304 zend_restore_error_handling(&zeh TSRMLS_CC);
305 }
306
307 static const zend_function_entry php_property_proxy_method_entry[] = {
308 PHP_ME(propro, __construct, ai_propro_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
309 {0}
310 };
311
312 static PHP_MINIT_FUNCTION(propro)
313 {
314 zend_class_entry ce = {0};
315
316 INIT_NS_CLASS_ENTRY(ce, "php", "PropertyProxy", php_property_proxy_method_entry);
317 php_property_proxy_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
318 php_property_proxy_class_entry->create_object = php_property_proxy_object_new;
319 php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL_CLASS;
320
321 memcpy(&php_property_proxy_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
322 php_property_proxy_object_handlers.set = set_proxied_value;
323 php_property_proxy_object_handlers.get = get_proxied_value;
324 php_property_proxy_object_handlers.cast_object = cast_proxied_value;
325 php_property_proxy_object_handlers.read_dimension = read_dimension;
326 php_property_proxy_object_handlers.write_dimension = write_dimension;
327
328 return SUCCESS;
329 }
330
331 PHP_MINFO_FUNCTION(propro)
332 {
333 php_info_print_table_start();
334 php_info_print_table_header(2, "Property proxy support", "enabled");
335 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION);
336 php_info_print_table_end();
337 }
338
339 static const zend_function_entry propro_functions[] = {
340 {0}
341 };
342
343 zend_module_entry propro_module_entry = {
344 STANDARD_MODULE_HEADER,
345 "propro",
346 propro_functions,
347 PHP_MINIT(propro),
348 NULL,
349 NULL,
350 NULL,
351 PHP_MINFO(propro),
352 PHP_PROPRO_VERSION,
353 STANDARD_MODULE_PROPERTIES
354 };
355
356 #ifdef COMPILE_DL_PROPRO
357 ZEND_GET_MODULE(propro)
358 #endif
359
360
361 /*
362 * Local variables:
363 * tab-width: 4
364 * c-basic-offset: 4
365 * End:
366 * vim600: noet sw=4 ts=4 fdm=marker
367 * vim<600: noet sw=4 ts=4
368 */