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