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