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