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