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