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