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