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