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