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