interruption
[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 typedef int STATUS;
24
25 #define DEBUG_PROPRO 0
26
27 /* legacy */
28 #ifdef PHPNG
29 # undef zval_ptr_dtor
30 # define zval_ptr_dtor(zpp) _zval_ptr_dtor(*(zpp) ZEND_FILE_LINE_CC)
31 #endif
32
33 #if PHP_VERSION_ID < 50400
34 # define object_properties_init(o, ce) \
35 zend_hash_copy(((zend_object *) o)->properties, &(ce->default_properties), \
36 (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*))
37 #endif
38
39 php_property_proxy_t *php_property_proxy_init(zval *container,
40 const char *member_str, size_t member_len TSRMLS_DC)
41 {
42 php_property_proxy_t *proxy = ecalloc(1, sizeof(*proxy));
43
44 Z_ADDREF_P(container);
45 proxy->container = container;
46 proxy->member_str = estrndup(member_str, member_len);
47 proxy->member_len = member_len;
48
49 return proxy;
50 }
51
52 void php_property_proxy_free(php_property_proxy_t **proxy)
53 {
54 if (*proxy) {
55 zval_ptr_dtor(&(*proxy)->container);
56 efree((*proxy)->member_str);
57 efree(*proxy);
58 *proxy = NULL;
59 }
60 }
61
62 static zend_class_entry *php_property_proxy_class_entry;
63 static zend_object_handlers php_property_proxy_object_handlers;
64
65 zend_class_entry *php_property_proxy_get_class_entry(void)
66 {
67 return php_property_proxy_class_entry;
68 }
69
70 zend_object_value php_property_proxy_object_new(zend_class_entry *ce TSRMLS_DC)
71 {
72 return php_property_proxy_object_new_ex(ce, NULL, NULL TSRMLS_CC);
73 }
74
75 static void php_property_proxy_object_free(void *object TSRMLS_DC)
76 {
77 php_property_proxy_object_t *o = object;
78
79 #if DEBUG_PROPRO
80 fprintf(stderr, "#PP %p free\n", o);
81 #endif
82
83 if (o->proxy) {
84 php_property_proxy_free(&o->proxy);
85 }
86 if (o->parent) {
87 #ifdef PHPNG
88 zend_objects_store_del(&o->parent->zv);
89 #else
90 zend_objects_store_del_ref_by_handle_ex(o->parent->zv.handle,
91 o->parent->zv.handlers TSRMLS_CC);
92 #endif
93 o->parent = NULL;
94 }
95 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
96 efree(o);
97 }
98
99 zend_object_value php_property_proxy_object_new_ex(zend_class_entry *ce,
100 php_property_proxy_t *proxy, php_property_proxy_object_t **ptr TSRMLS_DC)
101 {
102 php_property_proxy_object_t *o;
103
104 o = ecalloc(1, sizeof(*o));
105 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
106 object_properties_init((zend_object *) o, ce);
107
108 if (ptr) {
109 *ptr = o;
110 }
111 o->proxy = proxy;
112
113 o->zv.handle = zend_objects_store_put(o, NULL,
114 php_property_proxy_object_free, NULL TSRMLS_CC);
115 o->zv.handlers = &php_property_proxy_object_handlers;
116
117 #if DEBUG_PROPRO
118 fprintf(stderr, "#PP %p init\n", o);
119 #endif
120
121 return o->zv;
122 }
123
124 #if DEBUG_PROPRO
125 /* we do not really care about TS when debugging */
126 static int level = 1;
127 static const char space[] = " ";
128 static const char *inoutstr[] = {"< return",""," > enter"};
129 static void _walk(php_property_proxy_object_t *obj TSRMLS_DC)
130 {
131 if (obj) {
132 _walk(obj->parent TSRMLS_CC);
133 fprintf(stderr, ".%s", obj->proxy->member_str);
134 }
135 }
136
137 static void debug_propro(int inout, const char *f, zval *object, zval *offset,
138 zval *value TSRMLS_DC)
139 {
140 php_property_proxy_object_t *obj;
141
142 obj = zend_object_store_get_object(object TSRMLS_CC);
143 fprintf(stderr, "#PP %p %s %s %s ", obj, &space[sizeof(space)-level],
144 inoutstr[inout+1], f);
145
146 level += inout;
147
148 _walk(obj TSRMLS_CC);
149
150 if (*f++=='d'
151 && *f++=='i'
152 && *f++=='m'
153 ) {
154 char *offset_str = "[]";
155 zval *o = offset;
156
157 if (o) {
158 convert_to_string_ex(&o);
159 offset_str = Z_STRVAL_P(o);
160 }
161
162 fprintf(stderr, ".%s", offset_str);
163
164 if (o && o != offset) {
165 zval_ptr_dtor(&o);
166 }
167 }
168 if (value) {
169 const char *t[] = {
170 "NULL",
171 "int",
172 "float",
173 "bool",
174 "Array",
175 "Object",
176 "string",
177 "resource",
178 "const",
179 "const Array",
180 "callable"
181 };
182 fprintf(stderr, " = (%s) ", t[Z_TYPE_P(value)&0xf]);
183 zend_print_flat_zval_r(value TSRMLS_CC);
184 }
185
186 fprintf(stderr, "\n");
187 }
188 #else
189 #define debug_propro(l, f, obj, off, val)
190 #endif
191
192 static zval *get_parent_proxied_value(zval *object TSRMLS_DC);
193 static zval *get_proxied_value(zval *object TSRMLS_DC);
194 static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
195 static STATUS cast_proxied_value(zval *object, zval *return_value,
196 int type TSRMLS_DC);
197 static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC);
198 static void set_proxied_value(zval **object, zval *value TSRMLS_DC);
199
200 static zval *get_parent_proxied_value(zval *object TSRMLS_DC)
201 {
202 zval *value = NULL;
203 php_property_proxy_object_t *obj;
204
205 obj = zend_object_store_get_object(object TSRMLS_CC);
206 if (obj->proxy) {
207 if (obj->parent) {
208 zval *parent;
209
210 MAKE_STD_ZVAL(parent);
211 parent->type = IS_OBJECT;
212 parent->value.obj = obj->parent->zv;
213 zend_objects_store_add_ref_by_handle(
214 obj->parent->zv.handle TSRMLS_CC);
215 value = get_proxied_value(parent TSRMLS_CC);
216 zval_ptr_dtor(&parent);
217 }
218 }
219
220 return value;
221 }
222
223 static zval *get_proxied_value(zval *object TSRMLS_DC)
224 {
225 zval **hash_value, *value = NULL;
226 php_property_proxy_object_t *obj;
227 STATUS rv;
228
229 obj = zend_object_store_get_object(object TSRMLS_CC);
230 debug_propro(1, "get", object, NULL, NULL TSRMLS_CC);
231
232 if (obj->proxy) {
233 if (obj->parent) {
234 zval *parent_value = get_parent_proxied_value(object TSRMLS_CC);
235
236 if (parent_value && parent_value != obj->proxy->container) {
237 Z_ADDREF_P(parent_value);
238 zval_ptr_dtor(&obj->proxy->container);
239 obj->proxy->container = parent_value;
240 }
241 }
242 switch (Z_TYPE_P(obj->proxy->container)) {
243 case IS_OBJECT:
244 value = zend_read_property(Z_OBJCE_P(obj->proxy->container),
245 obj->proxy->container, obj->proxy->member_str,
246 obj->proxy->member_len, 0 TSRMLS_CC);
247 break;
248
249 case IS_ARRAY:
250 rv = zend_symtable_find(Z_ARRVAL_P(obj->proxy->container),
251 obj->proxy->member_str, obj->proxy->member_len + 1,
252 (void *) &hash_value);
253
254 if (SUCCESS == rv) {
255 value = *hash_value;
256 }
257 break;
258 }
259 }
260
261 debug_propro(-1, "get", object, NULL, value TSRMLS_CC);
262
263 return value;
264 }
265
266 static STATUS cast_proxied_value(zval *object, zval *return_value,
267 int type TSRMLS_DC)
268 {
269 zval *proxied_value;
270
271 if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
272 RETVAL_ZVAL(proxied_value, 1, 0);
273 if (Z_TYPE_P(proxied_value) != type) {
274 convert_to_explicit_type(return_value, type);
275 }
276 return SUCCESS;
277 }
278
279 return FAILURE;
280 }
281
282 static void set_proxied_value(zval **object, zval *value TSRMLS_DC)
283 {
284 php_property_proxy_object_t *obj;
285
286 obj = zend_object_store_get_object(*object TSRMLS_CC);
287 debug_propro(1, "set", *object, NULL, value TSRMLS_CC);
288
289 if (obj->proxy) {
290 if (obj->parent) {
291 zval *parent_value = get_parent_proxied_value(*object TSRMLS_CC);
292
293 if (parent_value && parent_value != obj->proxy->container) {
294 Z_ADDREF_P(parent_value);
295 zval_ptr_dtor(&obj->proxy->container);
296 obj->proxy->container = parent_value;
297 }
298 }
299
300 switch (Z_TYPE_P(obj->proxy->container)) {
301 case IS_OBJECT:
302 zend_update_property(Z_OBJCE_P(obj->proxy->container),
303 obj->proxy->container, obj->proxy->member_str,
304 obj->proxy->member_len, value TSRMLS_CC);
305 break;
306
307 case IS_ARRAY:
308 Z_ADDREF_P(value);
309 zend_symtable_update(Z_ARRVAL_P(obj->proxy->container),
310 obj->proxy->member_str, obj->proxy->member_len + 1,
311 (void *) &value, sizeof(zval *), NULL);
312 break;
313 }
314
315 if (obj->parent) {
316 zval *zparent;
317 MAKE_STD_ZVAL(zparent);
318 zparent->type = IS_OBJECT;
319 zparent->value.obj = obj->parent->zv;
320 zend_objects_store_add_ref_by_handle(
321 obj->parent->zv.handle TSRMLS_CC);
322 set_proxied_value(&zparent, obj->proxy->container TSRMLS_CC);
323 zval_ptr_dtor(&zparent);
324 }
325 }
326
327 debug_propro(-1, "set", *object, NULL, NULL TSRMLS_CC);
328 }
329
330 static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
331 {
332 zval *value = NULL;
333 zval *proxied_value;
334 zval *o = offset;
335
336 debug_propro(1, type == BP_VAR_R ? "dim_read" : "dim_read_ref", object,
337 offset, NULL TSRMLS_CC);
338
339 proxied_value = get_proxied_value(object TSRMLS_CC);
340 convert_to_string_ex(&o);
341
342 if (BP_VAR_R == type && proxied_value) {
343 if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
344 zval **hash_value;
345 STATUS rv = zend_symtable_find(Z_ARRVAL_P(proxied_value),
346 Z_STRVAL_P(o), Z_STRLEN_P(o), (void *) &hash_value);
347
348 if (SUCCESS == rv) {
349 Z_ADDREF_PP(hash_value);
350 value = *hash_value;
351 }
352 }
353 } else {
354 php_property_proxy_t *proxy;
355 php_property_proxy_object_t *proxy_obj;
356
357 if (proxied_value) {
358 convert_to_array(proxied_value);
359 Z_ADDREF_P(proxied_value);
360 } else {
361 MAKE_STD_ZVAL(proxied_value);
362 array_init(proxied_value);
363 set_proxied_value(&object, proxied_value TSRMLS_CC);
364 }
365
366 proxy = php_property_proxy_init(proxied_value, Z_STRVAL_P(o),
367 Z_STRLEN_P(o) TSRMLS_CC);
368 zval_ptr_dtor(&proxied_value);
369 MAKE_STD_ZVAL(value);
370 Z_SET_REFCOUNT_P(value, 0);
371 value->type = IS_OBJECT;
372 value->value.obj = php_property_proxy_object_new_ex(
373 php_property_proxy_get_class_entry(), proxy,
374 &proxy_obj TSRMLS_CC);
375 proxy_obj->parent = zend_object_store_get_object(object TSRMLS_CC);
376 zend_objects_store_add_ref_by_handle(
377 proxy_obj->parent->zv.handle TSRMLS_CC);
378 }
379 if (o && o != offset) {
380 zval_ptr_dtor(&o);
381 }
382
383 debug_propro(-1, type == BP_VAR_R ? "dim_read" : "dim_read_ref", object,
384 offset, value TSRMLS_CC);
385
386 return value;
387 }
388
389 static int has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC)
390 {
391 zval *proxied_value;
392 int exists = 0;
393
394 debug_propro(1, "dim_exists", object, offset, NULL TSRMLS_CC);
395
396 proxied_value = get_proxied_value(object TSRMLS_CC);
397 if (!proxied_value) {
398 exists = 0;
399 } else {
400 zval *o = offset;
401
402 convert_to_string_ex(&o);
403
404 #ifdef PHPNG
405 if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
406 zval *zentry = zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STR_P(o));
407
408 if (!zentry) {
409 exists = 0;
410 } else {
411 if (check_empty) {
412 exists = Z_TYPE_PP(zentry) != IS_NULL;
413 } else {
414 exists = 1;
415 }
416 }
417 }
418 #else
419 if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
420 zval **zentry;
421 STATUS rv = zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &zentry);
422
423 if (SUCCESS != rv) {
424 exists = 0;
425 } else {
426 if (check_empty) {
427 exists = Z_TYPE_PP(zentry) != IS_NULL;
428 } else {
429 exists = 1;
430 }
431 }
432 }
433
434 if (o != offset) {
435 zval_ptr_dtor(&o);
436 }
437 #endif
438 }
439
440 debug_propro(-1, "dim_exists", object, offset, NULL TSRMLS_CC);
441
442 return exists;
443 }
444
445 static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
446 {
447 zval *proxied_value, *o = offset;
448
449 debug_propro(1, "dim_write", object, offset, value TSRMLS_CC);
450
451 proxied_value = get_proxied_value(object TSRMLS_CC);
452
453 if (proxied_value) {
454 convert_to_array(proxied_value);
455 Z_ADDREF_P(proxied_value);
456 } else {
457 MAKE_STD_ZVAL(proxied_value);
458 array_init(proxied_value);
459 }
460
461 if (Z_REFCOUNT_P(value) > 1) {
462 SEPARATE_ZVAL(&value);
463 }
464 Z_ADDREF_P(value);
465
466 #ifdef PHPNG
467 if (o) {
468 convert_to_string_ex(o);
469 zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STR_P(o), value);
470 } else {
471 zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value), value);
472 }
473 #else
474 if (o) {
475 convert_to_string_ex(&o);
476 zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o),
477 Z_STRLEN_P(o) + 1, (void *) &value, sizeof(zval *), NULL);
478 } else {
479 zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value), (void *) &value,
480 sizeof(zval *), NULL);
481 }
482
483 if (o && o != offset) {
484 zval_ptr_dtor(&o);
485 }
486 #endif
487
488 set_proxied_value(&object, proxied_value TSRMLS_CC);
489
490 debug_propro(-1, "dim_write", object, offset, proxied_value TSRMLS_CC);
491
492 zval_ptr_dtor(&proxied_value);
493 }
494
495 static void unset_dimension(zval *object, zval *offset TSRMLS_DC)
496 {
497 zval *proxied_value;
498
499 debug_propro(1, "dim_unset", object, offset, NULL TSRMLS_CC);
500
501 proxied_value = get_proxied_value(object TSRMLS_CC);
502
503 if (proxied_value && Z_TYPE_P(proxied_value) == IS_ARRAY) {
504 zval *o = offset;
505 STATUS rv;
506
507 convert_to_string_ex(&o);
508 #ifdef PHPNG
509 rv = zend_symtable_del(Z_ARRVAL_P(proxied_value), Z_STR_P(o));
510 #else
511 rv = zend_symtable_del(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o),
512 Z_STRLEN_P(o) + 1);
513 #endif
514 if (SUCCESS == rv) {
515 set_proxied_value(&object, proxied_value TSRMLS_CC);
516 }
517
518 if (o != offset) {
519 zval_ptr_dtor(&o);
520 }
521 }
522
523 debug_propro(-1, "dim_unset", object, offset, proxied_value TSRMLS_CC);
524 }
525
526 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct, 0, 0, 2)
527 ZEND_ARG_INFO(1, object)
528 ZEND_ARG_INFO(0, member)
529 ZEND_ARG_OBJ_INFO(0, parent, php\\PropertyProxy, 1)
530 ZEND_END_ARG_INFO();
531 static PHP_METHOD(propro, __construct) {
532 zend_error_handling zeh;
533 zval *container, *parent = NULL;
534 char *member_str;
535 int member_len;
536
537 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
538 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|O!",
539 &container, &member_str, &member_len, &parent,
540 php_property_proxy_class_entry)) {
541 php_property_proxy_object_t *obj;
542
543 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
544 obj->proxy = php_property_proxy_init(container, member_str,
545 member_len TSRMLS_CC);
546 if (parent) {
547 zend_objects_store_add_ref(parent TSRMLS_CC);
548 obj->parent = zend_object_store_get_object(parent TSRMLS_CC);
549 }
550 }
551 zend_restore_error_handling(&zeh TSRMLS_CC);
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 TSRMLS_CC);
566 php_property_proxy_class_entry->create_object =
567 php_property_proxy_object_new;
568 php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL_CLASS;
569
570 memcpy(&php_property_proxy_object_handlers, zend_get_std_object_handlers(),
571 sizeof(zend_object_handlers));
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 */