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