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