debugging aids; has_dimension and unset_dimension
[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 #define DEBUG_PROPRO 0
26
27 #if PHP_VERSION_ID < 50400
28 # define object_properties_init(o, ce) zend_hash_copy(((zend_object *) o)->properties, &(ce->default_properties), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*))
29 #endif
30
31 PHP_PROPRO_API php_property_proxy_t *php_property_proxy_init(zval *container, const char *member_str, size_t member_len TSRMLS_DC)
32 {
33 php_property_proxy_t *proxy = ecalloc(1, sizeof(*proxy));
34
35 Z_ADDREF_P(container);
36 proxy->container = container;
37 proxy->member_str = estrndup(member_str, member_len);
38 proxy->member_len = member_len;
39
40 return proxy;
41 }
42
43 PHP_PROPRO_API void php_property_proxy_free(php_property_proxy_t **proxy)
44 {
45 if (*proxy) {
46 zval_ptr_dtor(&(*proxy)->container);
47 efree((*proxy)->member_str);
48 efree(*proxy);
49 *proxy = NULL;
50 }
51 }
52
53 static zend_class_entry *php_property_proxy_class_entry;
54 static zend_object_handlers php_property_proxy_object_handlers;
55
56 PHP_PROPRO_API zend_class_entry *php_property_proxy_get_class_entry(void)
57 {
58 return php_property_proxy_class_entry;
59 }
60
61 PHP_PROPRO_API zend_object_value php_property_proxy_object_new(zend_class_entry *ce TSRMLS_DC)
62 {
63 return php_property_proxy_object_new_ex(ce, NULL, NULL TSRMLS_CC);
64 }
65
66 static void php_property_proxy_object_free(void *object TSRMLS_DC)
67 {
68 php_property_proxy_object_t *o = object;
69
70 #if DEBUG_PROPRO
71 fprintf(stderr, "#PP %p free\n", o);
72 #endif
73
74 if (o->proxy) {
75 php_property_proxy_free(&o->proxy);
76 }
77 if (o->parent) {
78 zend_objects_store_del_ref_by_handle_ex(o->parent->zv.handle, o->parent->zv.handlers TSRMLS_CC);
79 o->parent = NULL;
80 }
81 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
82 efree(o);
83 }
84
85 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)
86 {
87 php_property_proxy_object_t *o;
88
89 o = ecalloc(1, sizeof(*o));
90 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
91 object_properties_init((zend_object *) o, ce);
92
93 if (ptr) {
94 *ptr = o;
95 }
96 o->proxy = proxy;
97
98 o->zv.handle = zend_objects_store_put(o, NULL, php_property_proxy_object_free, NULL TSRMLS_CC);
99 o->zv.handlers = &php_property_proxy_object_handlers;
100
101 #if DEBUG_PROPRO
102 fprintf(stderr, "#PP %p init\n", o);
103 #endif
104
105 return o->zv;
106 }
107
108 #if DEBUG_PROPRO
109 /* we do not really care about TS when debugging */
110 static int level = 1;
111 static const char space[] = " ";
112 static const char *inoutstr[] = {"< return",""," > enter"};
113 static void _walk(php_property_proxy_object_t *obj TSRMLS_DC)
114 {
115 if (obj) {
116 _walk(obj->parent TSRMLS_CC);
117 fprintf(stderr, ".%s", obj->proxy->member_str);
118 }
119 }
120
121 static void debug_propro(int inout, const char *f, zval *object, zval *offset, zval *value TSRMLS_DC)
122 {
123 php_property_proxy_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
124
125 fprintf(stderr, "#PP %p %s %s %s ", obj, &space[sizeof(space)-level], inoutstr[inout+1], f);
126
127 level += inout;
128
129 _walk(obj TSRMLS_CC);
130
131 if (*f++=='d'
132 && *f++=='i'
133 && *f++=='m'
134 ) {
135 char *offset_str = "[]";
136 zval *o = offset;
137
138 if (o) {
139 convert_to_string_ex(&o);
140 offset_str = Z_STRVAL_P(o);
141 }
142
143 fprintf(stderr, ".%s", offset_str);
144
145 if (o && o != offset) {
146 zval_ptr_dtor(&o);
147 }
148 }
149 if (value) {
150 const char *t[] = {
151 "NULL",
152 "int",
153 "float",
154 "bool",
155 "Array",
156 "Object",
157 "string",
158 "resource",
159 "const",
160 "const Array",
161 "callable"
162 };
163 fprintf(stderr, " = (%s) ", t[Z_TYPE_P(value)&0xf]);
164 zend_print_flat_zval_r(value TSRMLS_CC);
165 }
166
167 fprintf(stderr, "\n");
168 }
169 #else
170 #define debug_propro(l, f, obj, off, val)
171 #endif
172
173 static zval *get_parent_proxied_value(zval *object TSRMLS_DC);
174 static zval *get_proxied_value(zval *object TSRMLS_DC);
175 static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
176 static STATUS cast_proxied_value(zval *object, zval *return_value, int type TSRMLS_DC);
177 static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC);
178 static void set_proxied_value(zval **object, zval *value TSRMLS_DC);
179
180 static zval *get_parent_proxied_value(zval *object TSRMLS_DC)
181 {
182 zval *value = NULL;
183 php_property_proxy_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
184
185 if (obj->proxy) {
186 if (obj->parent) {
187 zval *parent;
188
189 MAKE_STD_ZVAL(parent);
190 parent->type = IS_OBJECT;
191 parent->value.obj = obj->parent->zv;
192 zend_objects_store_add_ref_by_handle(obj->parent->zv.handle TSRMLS_CC);
193 value = get_proxied_value(parent TSRMLS_CC);
194 zval_ptr_dtor(&parent);
195 }
196 }
197
198 return value;
199 }
200
201 static zval *get_proxied_value(zval *object TSRMLS_DC)
202 {
203 zval **hash_value, *value = NULL;
204 php_property_proxy_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
205
206 debug_propro(1, "get", object, NULL, NULL TSRMLS_CC);
207
208 if (obj->proxy) {
209 if (obj->parent) {
210 zval *parent_value = get_parent_proxied_value(object TSRMLS_CC);
211
212 if (parent_value && parent_value != obj->proxy->container) {
213 Z_ADDREF_P(parent_value);
214 zval_ptr_dtor(&obj->proxy->container);
215 obj->proxy->container = parent_value;
216 }
217 }
218 switch (Z_TYPE_P(obj->proxy->container)) {
219 case IS_OBJECT:
220 value = zend_read_property(Z_OBJCE_P(obj->proxy->container), obj->proxy->container, obj->proxy->member_str, obj->proxy->member_len, 0 TSRMLS_CC);
221 break;
222
223 case IS_ARRAY:
224 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(obj->proxy->container), obj->proxy->member_str, obj->proxy->member_len + 1, (void *) &hash_value)) {
225 value = *hash_value;
226 }
227 break;
228 }
229 }
230
231 debug_propro(-1, "get", object, NULL, value TSRMLS_CC);
232
233 return value;
234 }
235
236 static STATUS cast_proxied_value(zval *object, zval *return_value, int type TSRMLS_DC)
237 {
238 zval *proxied_value;
239
240 if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
241 RETVAL_ZVAL(proxied_value, 1, 0);
242 if (Z_TYPE_P(proxied_value) != type) {
243 convert_to_explicit_type(return_value, type);
244 }
245 return SUCCESS;
246 }
247
248 return FAILURE;
249 }
250
251 static void set_proxied_value(zval **object, zval *value TSRMLS_DC)
252 {
253 php_property_proxy_object_t *obj = zend_object_store_get_object(*object TSRMLS_CC);
254
255 debug_propro(1, "set", *object, NULL, value TSRMLS_CC);
256
257 if (obj->proxy) {
258 if (obj->parent) {
259 zval *parent_value = get_parent_proxied_value(*object TSRMLS_CC);
260
261 if (parent_value && parent_value != obj->proxy->container) {
262 Z_ADDREF_P(parent_value);
263 zval_ptr_dtor(&obj->proxy->container);
264 obj->proxy->container = parent_value;
265 }
266 }
267
268 switch (Z_TYPE_P(obj->proxy->container)) {
269 case IS_OBJECT:
270 zend_update_property(Z_OBJCE_P(obj->proxy->container), obj->proxy->container, obj->proxy->member_str, obj->proxy->member_len, value TSRMLS_CC);
271 break;
272
273 case IS_ARRAY:
274 Z_ADDREF_P(value);
275 zend_symtable_update(Z_ARRVAL_P(obj->proxy->container), obj->proxy->member_str, obj->proxy->member_len + 1, (void *) &value, sizeof(zval *), NULL);
276 break;
277 }
278
279 if (obj->parent) {
280 zval *zparent;
281 MAKE_STD_ZVAL(zparent);
282 zparent->type = IS_OBJECT;
283 zparent->value.obj = obj->parent->zv;
284 zend_objects_store_add_ref_by_handle(obj->parent->zv.handle TSRMLS_CC);
285 set_proxied_value(&zparent, obj->proxy->container TSRMLS_CC);
286 zval_ptr_dtor(&zparent);
287 }
288 }
289
290 debug_propro(-1, "set", *object, NULL, NULL TSRMLS_CC);
291 }
292
293 static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
294 {
295 zval *value = NULL;
296 zval *proxied_value;
297 zval *o = offset;
298
299 debug_propro(1, type == BP_VAR_R ? "dim_read" : "dim_read_ref", object, offset, NULL TSRMLS_CC);
300
301 proxied_value = get_proxied_value(object TSRMLS_CC);
302 convert_to_string_ex(&o);
303
304 if (BP_VAR_R == type && proxied_value) {
305 if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
306 zval **hash_value;
307 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o), (void *) &hash_value)) {
308 Z_ADDREF_PP(hash_value);
309 value = *hash_value;
310 }
311 }
312 } else {
313 php_property_proxy_t *proxy;
314 php_property_proxy_object_t *proxy_obj;
315
316 if (proxied_value) {
317 convert_to_array(proxied_value);
318 Z_ADDREF_P(proxied_value);
319 } else {
320 MAKE_STD_ZVAL(proxied_value);
321 array_init(proxied_value);
322 set_proxied_value(&object, proxied_value TSRMLS_CC);
323 }
324
325 //add_assoc_null_ex(proxied_value, Z_STRVAL_P(o), Z_STRLEN_P(o) + 1);
326
327 proxy = php_property_proxy_init(proxied_value, Z_STRVAL_P(o), Z_STRLEN_P(o) TSRMLS_CC);
328 zval_ptr_dtor(&proxied_value);
329 MAKE_STD_ZVAL(value);
330 Z_SET_REFCOUNT_P(value, 0);
331 value->type = IS_OBJECT;
332 value->value.obj = php_property_proxy_object_new_ex(php_property_proxy_get_class_entry(), proxy, &proxy_obj TSRMLS_CC);
333 proxy_obj->parent = zend_object_store_get_object(object TSRMLS_CC);
334 zend_objects_store_add_ref_by_handle(proxy_obj->parent->zv.handle TSRMLS_CC);
335 }
336 if (o && o != offset) {
337 zval_ptr_dtor(&o);
338 }
339
340 debug_propro(-1, type == BP_VAR_R ? "dim_read" : "dim_read_ref", object, offset, value TSRMLS_CC);
341
342 return value;
343 }
344
345 static int has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC)
346 {
347 zval *proxied_value;
348 int exists = 0;
349
350 debug_propro(1, "dim_exists", object, offset, NULL TSRMLS_CC);
351
352 proxied_value = get_proxied_value(object TSRMLS_CC);
353 if (!proxied_value) {
354 exists = 0;
355 } else {
356 zval *o = offset;
357
358 convert_to_string_ex(&o);
359
360 if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
361 zval **zentry;
362
363 if (SUCCESS != zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &zentry)) {
364 exists = 0;
365 } else {
366 if (check_empty) {
367 exists = Z_TYPE_PP(zentry) != IS_NULL;
368 } else {
369 exists = 1;
370 }
371 }
372 }
373
374 if (o != offset) {
375 zval_ptr_dtor(&o);
376 }
377 }
378
379 debug_propro(-1, "dim_exists", object, offset, NULL TSRMLS_CC);
380
381 return exists;
382 }
383
384 static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
385 {
386 zval *proxied_value, *o = offset;
387
388 debug_propro(1, "dim_write", object, offset, value TSRMLS_CC);
389
390 proxied_value = get_proxied_value(object TSRMLS_CC);
391
392 if (proxied_value) {
393 convert_to_array(proxied_value);
394 Z_ADDREF_P(proxied_value);
395 } else {
396 MAKE_STD_ZVAL(proxied_value);
397 array_init(proxied_value);
398 }
399
400 if (Z_REFCOUNT_P(value) > 1) {
401 SEPARATE_ZVAL(&value);
402 }
403 Z_ADDREF_P(value);
404
405 if (o) {
406 convert_to_string_ex(&o);
407 zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &value, sizeof(zval *), NULL);
408 } else {
409 zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value), (void *) &value, sizeof(zval *), NULL);
410 }
411
412 if (o && o != offset) {
413 zval_ptr_dtor(&o);
414 }
415
416 set_proxied_value(&object, proxied_value TSRMLS_CC);
417
418 debug_propro(-1, "dim_write", object, offset, proxied_value TSRMLS_CC);
419
420 zval_ptr_dtor(&proxied_value);
421 }
422
423 static void unset_dimension(zval *object, zval *offset TSRMLS_DC)
424 {
425 zval *proxied_value;
426
427 debug_propro(1, "dim_unset", object, offset, NULL TSRMLS_CC);
428
429 proxied_value = get_proxied_value(object TSRMLS_CC);
430
431 if (proxied_value && Z_TYPE_P(proxied_value) == IS_ARRAY) {
432 zval *o = offset;
433
434 convert_to_string_ex(&o);
435
436 if (SUCCESS == zend_symtable_del(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1)) {
437 set_proxied_value(&object, proxied_value TSRMLS_CC);
438 }
439
440 if (o != offset) {
441 zval_ptr_dtor(&o);
442 }
443 }
444
445 debug_propro(-1, "dim_unset", object, offset, proxied_value TSRMLS_CC);
446 }
447
448 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct, 0, 0, 2)
449 ZEND_ARG_INFO(1, object)
450 ZEND_ARG_INFO(0, member)
451 ZEND_ARG_OBJ_INFO(0, parent, php\\PropertyProxy, 1)
452 ZEND_END_ARG_INFO();
453 static PHP_METHOD(propro, __construct) {
454 zend_error_handling zeh;
455 zval *container, *parent = NULL;
456 char *member_str;
457 int member_len;
458
459 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
460 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|O!", &container, &member_str, &member_len, &parent, php_property_proxy_class_entry)) {
461 php_property_proxy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
462
463 obj->proxy = php_property_proxy_init(container, member_str, member_len TSRMLS_CC);
464 if (parent) {
465 zend_objects_store_add_ref(parent TSRMLS_CC);
466 obj->parent = zend_object_store_get_object(parent TSRMLS_CC);
467 }
468 }
469 zend_restore_error_handling(&zeh TSRMLS_CC);
470 }
471
472 static const zend_function_entry php_property_proxy_method_entry[] = {
473 PHP_ME(propro, __construct, ai_propro_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
474 {0}
475 };
476
477 static PHP_MINIT_FUNCTION(propro)
478 {
479 zend_class_entry ce = {0};
480
481 INIT_NS_CLASS_ENTRY(ce, "php", "PropertyProxy", php_property_proxy_method_entry);
482 php_property_proxy_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
483 php_property_proxy_class_entry->create_object = php_property_proxy_object_new;
484 php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL_CLASS;
485
486 memcpy(&php_property_proxy_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
487 php_property_proxy_object_handlers.set = set_proxied_value;
488 php_property_proxy_object_handlers.get = get_proxied_value;
489 php_property_proxy_object_handlers.cast_object = cast_proxied_value;
490 php_property_proxy_object_handlers.read_dimension = read_dimension;
491 php_property_proxy_object_handlers.write_dimension = write_dimension;
492 php_property_proxy_object_handlers.has_dimension = has_dimension;
493 php_property_proxy_object_handlers.unset_dimension = unset_dimension;
494
495 return SUCCESS;
496 }
497
498 PHP_MINFO_FUNCTION(propro)
499 {
500 php_info_print_table_start();
501 php_info_print_table_header(2, "Property proxy support", "enabled");
502 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION);
503 php_info_print_table_end();
504 }
505
506 static const zend_function_entry propro_functions[] = {
507 {0}
508 };
509
510 zend_module_entry propro_module_entry = {
511 STANDARD_MODULE_HEADER,
512 "propro",
513 propro_functions,
514 PHP_MINIT(propro),
515 NULL,
516 NULL,
517 NULL,
518 PHP_MINFO(propro),
519 PHP_PROPRO_VERSION,
520 STANDARD_MODULE_PROPERTIES
521 };
522
523 #ifdef COMPILE_DL_PROPRO
524 ZEND_GET_MODULE(propro)
525 #endif
526
527
528 /*
529 * Local variables:
530 * tab-width: 4
531 * c-basic-offset: 4
532 * End:
533 * vim600: noet sw=4 ts=4 fdm=marker
534 * vim<600: noet sw=4 ts=4
535 */