--crashes; ++leaks;
[m6w6/ext-propro] / src / php_propro_api.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_api.h"
22
23 #define DEBUG_PROPRO 0
24
25 static inline php_property_proxy_object_t *get_propro(zval *object);
26 static inline zval *get_proxied_value(zval *object, zval *return_value);
27 static inline void set_proxied_value(zval *object, zval *value);
28
29 static zval *read_dimension(zval *object, zval *offset, int type, zval *return_value);
30 static ZEND_RESULT_CODE cast_obj(zval *object, zval *return_value, int type);
31 static void write_dimension(zval *object, zval *offset, zval *input_value);
32
33 #if DEBUG_PROPRO
34 /* we do not really care about TS when debugging */
35 static int level = 1;
36 static const char *inoutstr[] = {"< return","= "," > enter "};
37 static const char *types[] = {
38 "UNDEF",
39 "NULL",
40 "FALSE",
41 "TRUE",
42 "int",
43 "float",
44 "string",
45 "Array",
46 "Object",
47 "resource",
48 "reference",
49 "constant",
50 "constant AST",
51 "_BOOL",
52 "callable",
53 "indirect",
54 "---",
55 "pointer"
56 };
57
58 static int _walk(php_property_proxy_object_t *obj)
59 {
60 int p = 0;
61
62 if (obj) {
63 if (!Z_ISUNDEF(obj->parent)) {
64 p += _walk(get_propro(&obj->parent));
65 }
66 if (obj->proxy) {
67 p += fprintf(stderr, ".%s", obj->proxy->member->val);
68 }
69 }
70
71 return p;
72 }
73
74 static void debug_propro(int inout, const char *f,
75 php_property_proxy_object_t *obj,
76 php_property_proxy_t *proxy,
77 zval *offset, zval *value)
78 {
79 int width;
80
81 if (!proxy && obj) {
82 proxy = obj->proxy;
83 }
84
85 fprintf(stderr, "#PP %14p %*c %s %s\t", proxy, level, ' ', inoutstr[inout + 1], f);
86
87 level += inout;
88
89 if (proxy) {
90 fprintf(stderr, " container= %-10p <%12s rc=%d ",
91 Z_REFCOUNTED(proxy->container) ? Z_COUNTED(proxy->container) : NULL,
92 types[Z_TYPE(proxy->container)],
93 Z_REFCOUNTED(proxy->container) ? Z_REFCOUNT(proxy->container) : 0);
94 if (Z_ISREF(proxy->container)) {
95 zval *ref = Z_REFVAL(proxy->container);
96 fprintf(stderr, " %-12s %p rc=% 2d",
97 types[Z_TYPE_P(ref)],
98 ref->value.counted,
99 Z_REFCOUNTED_P(ref) ? Z_REFCOUNT_P(ref) : -1);
100 }
101 fprintf(stderr, "> ");
102 }
103
104 width = _walk(obj);
105
106 if (*f++=='d'
107 && *f++=='i'
108 && *f++=='m'
109 ) {
110 char *offset_str = "[]";
111 zval *o = offset;
112
113 if (o) {
114 convert_to_string_ex(o);
115 offset_str = Z_STRVAL_P(o);
116 }
117
118 width += fprintf(stderr, ".%s", offset_str);
119
120 if (o && o != offset) {
121 zval_ptr_dtor(o);
122 }
123 }
124 if (value) {
125 fprintf(stderr, "%.*s", 32-width, " ");
126 if (Z_ISUNDEF_P(value)) {
127 fprintf(stderr, " =UNDEF ");
128 } else {
129 fprintf(stderr, " = %-10p <%12s rc=%d %3s> ",
130 Z_REFCOUNTED_P(value) ? Z_COUNTED_P(value) : NULL,
131 types[Z_TYPE_P(value)&0xf],
132 Z_REFCOUNTED_P(value) ? Z_REFCOUNT_P(value) : 0,
133 Z_ISREF_P(value) ? "ref" : "");
134 if (!Z_ISUNDEF_P(value) && Z_TYPE_P(value) != IS_INDIRECT) {
135 zend_print_flat_zval_r(value TSRMLS_CC);
136 }
137 }
138 }
139
140 fprintf(stderr, "\n");
141 }
142 #else
143 #define debug_propro(l, f, obj, proxy, off, val)
144 #endif
145
146 php_property_proxy_t *php_property_proxy_init(zval *container, zend_string *member)
147 {
148 php_property_proxy_t *proxy = ecalloc(1, sizeof(*proxy));
149 #if DEBUG_PROPRO
150 zval offset;
151
152 ZVAL_STR_COPY(&offset, member);
153 #endif
154
155 debug_propro(1, "init", NULL, proxy, &offset, NULL);
156
157 ZVAL_COPY(&proxy->container, container);
158 proxy->member = zend_string_copy(member);
159
160 debug_propro(-1, "init", NULL, proxy, &offset, NULL);
161
162 #if DEBUG_PROPRO
163 zval_dtor(&offset);
164 #endif
165
166 return proxy;
167 }
168
169 void php_property_proxy_free(php_property_proxy_t **proxy)
170 {
171 #if DEBUG_PROPRO
172 zval offset;
173
174 ZVAL_STR_COPY(&offset, (*proxy)->member);
175 debug_propro(1, "dtor", NULL, *proxy, &offset, NULL);
176 #endif
177
178 if (*proxy) {
179 zval_ptr_dtor(&(*proxy)->container);
180
181 ZVAL_UNDEF(&(*proxy)->container);
182 zend_string_release((*proxy)->member);
183 (*proxy)->member = NULL;
184 efree(*proxy);
185 *proxy = NULL;
186 }
187
188 #if DEBUG_PROPRO
189 debug_propro(-1, "dtor", NULL, NULL, &offset, NULL);
190
191 zval_dtor(&offset);
192 #endif
193 }
194
195 static zend_class_entry *php_property_proxy_class_entry;
196 static zend_object_handlers php_property_proxy_object_handlers;
197
198 zend_class_entry *php_property_proxy_get_class_entry(void)
199 {
200 return php_property_proxy_class_entry;
201 }
202
203 php_property_proxy_object_t *php_property_proxy_object_new_ex(
204 zend_class_entry *ce, php_property_proxy_t *proxy)
205 {
206 php_property_proxy_object_t *o;
207
208 if (!ce) {
209 ce = php_property_proxy_class_entry;
210 }
211
212 o = ecalloc(1, sizeof(*o) + sizeof(zval) * (ce->default_properties_count - 1));
213 zend_object_std_init(&o->zo, ce);
214 object_properties_init(&o->zo, ce);
215
216 o->proxy = proxy;
217 o->zo.handlers = &php_property_proxy_object_handlers;
218
219 return o;
220 }
221
222 zend_object *php_property_proxy_object_new(zend_class_entry *ce)
223 {
224 return &php_property_proxy_object_new_ex(ce, NULL)->zo;
225 }
226
227 static void destroy_obj(zend_object *object)
228 {
229 php_property_proxy_object_t *o = PHP_PROPRO_PTR(object);
230
231 if (o->proxy) {
232 php_property_proxy_free(&o->proxy);
233 }
234 if (!Z_ISUNDEF(o->parent)) {
235 zval_ptr_dtor(&o->parent);
236 ZVAL_UNDEF(&o->parent);
237 }
238 zend_object_std_dtor(object);
239 }
240
241 static ZEND_RESULT_CODE cast_obj(zval *object, zval *return_value, int type)
242 {
243 zval tmp;
244
245 ZVAL_UNDEF(&tmp);
246 RETVAL_ZVAL(get_proxied_value(object, &tmp), 1, 0);
247
248 debug_propro(0, "cast", get_propro(object), NULL, NULL, return_value);
249
250 if (!Z_ISUNDEF_P(return_value)) {
251 ZVAL_DEREF(return_value);
252 convert_to_explicit_type_ex(return_value, type);
253 return SUCCESS;
254 }
255
256 return FAILURE;
257 }
258
259 static zval *get_obj(zval *object, zval *return_value)
260 {
261 zval tmp;
262
263 ZVAL_UNDEF(&tmp);
264 RETVAL_ZVAL(get_proxied_value(object, &tmp), 1, 0);
265 return return_value;
266 }
267
268 static void set_obj(zval *object, zval *value) {
269 set_proxied_value(object, value);
270 }
271
272 static inline php_property_proxy_object_t *get_propro(zval *object)
273 {
274 ZEND_ASSERT(Z_TYPE_P(object) == IS_OBJECT);
275 return PHP_PROPRO_PTR(Z_OBJ_P(object));
276 }
277
278 static inline zval *get_container(zval *object)
279 {
280 php_property_proxy_object_t *obj = get_propro(object);
281
282 if (!Z_ISUNDEF(obj->parent)) {
283 zval *parent_value, tmp;
284
285 ZVAL_UNDEF(&tmp);
286 parent_value = get_proxied_value(&obj->parent, &tmp);
287 ZVAL_DEREF(parent_value);
288 switch (Z_TYPE_P(parent_value)) {
289 case IS_OBJECT:
290 case IS_ARRAY:
291 zend_assign_to_variable(&obj->proxy->container, parent_value, IS_CV);
292 break;
293 default:
294 break;
295 }
296 }
297
298 return &obj->proxy->container;
299 }
300
301 static inline void set_container(zval *object, zval *container)
302 {
303 php_property_proxy_object_t *obj = get_propro(object);
304 zend_assign_to_variable(&obj->proxy->container, container, IS_CV);
305
306 if (!Z_ISUNDEF(obj->parent)) {
307 set_proxied_value(&obj->parent, &obj->proxy->container);
308 }
309 }
310
311 static inline zval *get_container_value(zval *container, zend_string *member, zval *return_value)
312 {
313 zval *found_value = NULL, prop_tmp;
314
315 ZVAL_DEREF(container);
316 switch (Z_TYPE_P(container)) {
317 case IS_OBJECT:
318 found_value = zend_read_property(Z_OBJCE_P(container), container,
319 member->val, member->len, 0, &prop_tmp);
320
321 break;
322
323 case IS_ARRAY:
324 found_value = zend_symtable_find(Z_ARRVAL_P(container), member);
325 break;
326 }
327
328 if (found_value) {
329 RETVAL_ZVAL(found_value, 0, 0);
330 }
331
332 return return_value;
333 }
334
335 static inline void set_container_value(zval *container, zend_string *member, zval *value)
336 {
337 ZVAL_DEREF(container);
338 switch (Z_TYPE_P(container)) {
339 case IS_OBJECT:
340 zend_update_property(Z_OBJCE_P(container), container,
341 member->val, member->len, value);
342 break;
343
344 case IS_UNDEF:
345 array_init(container);
346 /* no break */
347 default:
348 SEPARATE_ZVAL(container);
349 convert_to_array(container);
350 /* no break */
351 case IS_ARRAY:
352 SEPARATE_ARRAY(container);
353 Z_TRY_ADDREF_P(value);
354 if (member) {
355 zend_symtable_update(Z_ARRVAL_P(container), member, value);
356 } else {
357 zend_hash_next_index_insert(Z_ARRVAL_P(container), value);
358 }
359 break;
360 }
361 }
362
363
364 static zval *get_proxied_value(zval *object, zval *return_value)
365 {
366 php_property_proxy_object_t *obj = get_propro(object);
367
368 debug_propro(1, "get", obj, NULL, NULL, NULL);
369
370 if (obj->proxy) {
371 zval *container = get_container(object);
372
373 return_value = get_container_value(container, obj->proxy->member, return_value);
374 }
375
376 debug_propro(-1, "get", obj, NULL, NULL, return_value);
377
378 return return_value;
379 }
380
381 static void set_proxied_value(zval *object, zval *value)
382 {
383 php_property_proxy_object_t *obj = get_propro(object);
384
385 debug_propro(1, "set", obj, NULL, NULL, value);
386
387 if (obj->proxy) {
388 zval *container = get_container(object);
389
390 set_container_value(container, obj->proxy->member, value);
391 set_container(object, container);
392
393 debug_propro(0, "set", obj, NULL, NULL, value);
394 }
395
396 debug_propro(-1, "set", obj, NULL, NULL, value);
397 }
398
399 static zval *read_dimension(zval *object, zval *offset, int type, zval *return_value)
400 {
401 zval *value, tmp;
402 zend_string *member = offset ? zval_get_string(offset) : NULL;
403
404 debug_propro(1, type == BP_VAR_R ? "dim_r" : "dim_R",
405 get_propro(object), NULL, offset, NULL);
406
407 ZVAL_UNDEF(&tmp);
408 value = get_proxied_value(object, &tmp);
409
410 if (type == BP_VAR_R) {
411 ZEND_ASSERT(member);
412
413 if (!Z_ISUNDEF_P(value)) {
414 zval tmp;
415
416 ZVAL_UNDEF(&tmp);
417 RETVAL_ZVAL(get_container_value(value, member, &tmp), 1, 0);
418 }
419 } else {
420 php_property_proxy_t *proxy;
421 php_property_proxy_object_t *proxy_obj;
422 zval *array = value;
423 zend_bool created = Z_ISUNDEF_P(value);
424
425 ZVAL_DEREF(array);
426 if (Z_REFCOUNTED_P(array) && Z_REFCOUNT_P(array) > 1) {
427 created = 1;
428 SEPARATE_ZVAL_NOREF(array);
429 }
430 if (Z_TYPE_P(array) != IS_ARRAY) {
431 created = 1;
432 if (Z_ISUNDEF_P(array)) {
433 array_init(array);
434 } else {
435 convert_to_array(array);
436 }
437 }
438
439 if (!member) {
440 member = zend_long_to_str(zend_hash_next_free_element(
441 Z_ARRVAL_P(array)));
442 }
443
444 proxy = php_property_proxy_init(value, member);
445 proxy_obj = php_property_proxy_object_new_ex(NULL, proxy);
446 ZVAL_COPY(&proxy_obj->parent, object);
447 RETVAL_OBJ(&proxy_obj->zo);
448
449 if (created) {
450 Z_DELREF_P(value);
451 }
452
453 debug_propro(0, created ? "dim_R pp C" : "dim_R pp", get_propro(object), NULL, offset, return_value);
454 }
455
456 if (member) {
457 zend_string_release(member);
458 }
459
460 debug_propro(-1, type == BP_VAR_R ? "dim_r" : "dim_R",
461 get_propro(object), NULL, offset, return_value);
462
463 return return_value;
464 }
465
466 static int has_dimension(zval *object, zval *offset, int check_empty)
467 {
468 zval *value, tmp;
469 int exists = 0;
470
471 debug_propro(1, "dim_e", get_propro(object), NULL, offset, NULL);
472
473 ZVAL_UNDEF(&tmp);
474 value = get_proxied_value(object, &tmp);
475
476 exists = 0;
477 if (!Z_ISUNDEF_P(value)) {
478 zend_string *zs = zval_get_string(offset);
479
480 ZVAL_DEREF(value);
481 if (Z_TYPE_P(value) == IS_ARRAY) {
482 zval *zentry = zend_symtable_find(Z_ARRVAL_P(value), zs);
483
484 if (!zentry) {
485 exists = 0;
486 } else {
487 if (check_empty) {
488 exists = !Z_ISNULL_P(zentry);
489 } else {
490 exists = 1;
491 }
492 }
493 }
494
495 zend_string_release(zs);
496 }
497
498 debug_propro(-1, "dim_e", get_propro(object), NULL, offset, NULL);
499
500 return exists;
501 }
502
503 static void write_dimension(zval *object, zval *offset, zval *input_value)
504 {
505 zval *array, tmp;
506 zend_string *zs = NULL;
507
508 debug_propro(1, "dim_w", get_propro(object), NULL, offset, input_value);
509
510 ZVAL_UNDEF(&tmp);
511 array = get_proxied_value(object, &tmp);
512
513 if (offset) {
514 zs = zval_get_string(offset);
515 }
516 set_container_value(array, zs, input_value);
517 if (zs) {
518 zend_string_release(zs);
519 }
520
521 set_proxied_value(object, array);
522
523 debug_propro(-1, "dim_w", get_propro(object), NULL, offset, input_value);
524 }
525
526 static void unset_dimension(zval *object, zval *offset)
527 {
528 zval *array, *value, tmp;
529
530 debug_propro(1, "dim_u", get_propro(object), NULL, offset, NULL);
531
532 ZVAL_UNDEF(&tmp);
533 value = get_proxied_value(object, &tmp);
534 array = value;
535 ZVAL_DEREF(array);
536
537 if (Z_TYPE_P(array) == IS_ARRAY) {
538 zend_string *o = zval_get_string(offset);
539
540 SEPARATE_ARRAY(array);
541 zend_symtable_del(Z_ARRVAL_P(array), o);
542
543 set_proxied_value(object, value);
544
545 zend_string_release(o);
546 }
547
548 debug_propro(-1, "dim_u", get_propro(object), NULL, offset, NULL);
549 }
550
551 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct, 0, 0, 2)
552 ZEND_ARG_INFO(1, object)
553 ZEND_ARG_INFO(0, member)
554 ZEND_ARG_OBJ_INFO(0, parent, php\\PropertyProxy, 1)
555 ZEND_END_ARG_INFO();
556 static PHP_METHOD(propro, __construct) {
557 zend_error_handling zeh;
558 zval *reference, *container, *parent = NULL;
559 zend_string *member;
560
561 zend_replace_error_handling(EH_THROW, NULL, &zeh);
562 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "zS|O!",
563 &reference, &member, &parent,
564 php_property_proxy_class_entry)) {
565 php_property_proxy_object_t *obj;
566
567 container = reference;
568 ZVAL_DEREF(container);
569 switch (Z_TYPE_P(container)) {
570 case IS_ARRAY:
571 SEPARATE_ARRAY(container);
572 break;
573 case IS_OBJECT:
574 break;
575 default:
576 SEPARATE_ZVAL(container);
577 convert_to_array(container);
578 break;
579 }
580
581 obj = get_propro(getThis());
582 obj->proxy = php_property_proxy_init(reference, member);
583
584 if (parent) {
585 ZVAL_COPY(&obj->parent, parent);
586 }
587 }
588 zend_restore_error_handling(&zeh);
589 }
590
591 static const zend_function_entry php_property_proxy_method_entry[] = {
592 PHP_ME(propro, __construct, ai_propro_construct, ZEND_ACC_PUBLIC)
593 {0}
594 };
595
596 static PHP_MINIT_FUNCTION(propro)
597 {
598 zend_class_entry ce = {0};
599
600 INIT_NS_CLASS_ENTRY(ce, "php", "PropertyProxy",
601 php_property_proxy_method_entry);
602 php_property_proxy_class_entry = zend_register_internal_class(&ce);
603 php_property_proxy_class_entry->create_object = php_property_proxy_object_new;
604 php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL;
605
606 memcpy(&php_property_proxy_object_handlers, zend_get_std_object_handlers(),
607 sizeof(zend_object_handlers));
608 php_property_proxy_object_handlers.offset = XtOffsetOf(php_property_proxy_object_t, zo);
609 php_property_proxy_object_handlers.free_obj = destroy_obj;
610 php_property_proxy_object_handlers.set = set_obj;
611 php_property_proxy_object_handlers.get = get_obj;
612 php_property_proxy_object_handlers.cast_object = cast_obj;
613 php_property_proxy_object_handlers.read_dimension = read_dimension;
614 php_property_proxy_object_handlers.write_dimension = write_dimension;
615 php_property_proxy_object_handlers.has_dimension = has_dimension;
616 php_property_proxy_object_handlers.unset_dimension = unset_dimension;
617
618 return SUCCESS;
619 }
620
621 PHP_MINFO_FUNCTION(propro)
622 {
623 php_info_print_table_start();
624 php_info_print_table_header(2, "Property proxy support", "enabled");
625 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION);
626 php_info_print_table_end();
627 }
628
629 static const zend_function_entry propro_functions[] = {
630 {0}
631 };
632
633 zend_module_entry propro_module_entry = {
634 STANDARD_MODULE_HEADER,
635 "propro",
636 propro_functions,
637 PHP_MINIT(propro),
638 NULL,
639 NULL,
640 NULL,
641 PHP_MINFO(propro),
642 PHP_PROPRO_VERSION,
643 STANDARD_MODULE_PROPERTIES
644 };
645
646 #ifdef COMPILE_DL_PROPRO
647 ZEND_GET_MODULE(propro)
648 #endif
649
650
651 /*
652 * Local variables:
653 * tab-width: 4
654 * c-basic-offset: 4
655 * End:
656 * vim600: noet sw=4 ts=4 fdm=marker
657 * vim<600: noet sw=4 ts=4
658 */