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