Line data Source code
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 30 : php_property_proxy_t *php_property_proxy_init(zval *container,
34 : const char *member_str, size_t member_len TSRMLS_DC)
35 : {
36 30 : php_property_proxy_t *proxy = ecalloc(1, sizeof(*proxy));
37 :
38 30 : Z_ADDREF_P(container);
39 30 : proxy->container = container;
40 30 : proxy->member_str = estrndup(member_str, member_len);
41 30 : proxy->member_len = member_len;
42 :
43 30 : return proxy;
44 : }
45 :
46 30 : void php_property_proxy_free(php_property_proxy_t **proxy)
47 : {
48 30 : if (*proxy) {
49 30 : zval_ptr_dtor(&(*proxy)->container);
50 30 : efree((*proxy)->member_str);
51 30 : efree(*proxy);
52 30 : *proxy = NULL;
53 : }
54 30 : }
55 :
56 : static zend_class_entry *php_property_proxy_class_entry;
57 : static zend_object_handlers php_property_proxy_object_handlers;
58 :
59 19 : zend_class_entry *php_property_proxy_get_class_entry(void)
60 : {
61 19 : return php_property_proxy_class_entry;
62 : }
63 :
64 11 : zend_object_value php_property_proxy_object_new(zend_class_entry *ce TSRMLS_DC)
65 : {
66 11 : return php_property_proxy_object_new_ex(ce, NULL, NULL TSRMLS_CC);
67 : }
68 :
69 30 : static void php_property_proxy_object_free(void *object TSRMLS_DC)
70 : {
71 30 : php_property_proxy_object_t *o = object;
72 :
73 : #if DEBUG_PROPRO
74 : fprintf(stderr, "#PP %p free\n", o);
75 : #endif
76 :
77 30 : if (o->proxy) {
78 30 : php_property_proxy_free(&o->proxy);
79 : }
80 30 : if (o->parent) {
81 11 : zend_objects_store_del_ref_by_handle_ex(o->parent->zv.handle,
82 11 : o->parent->zv.handlers TSRMLS_CC);
83 11 : o->parent = NULL;
84 : }
85 30 : zend_object_std_dtor((zend_object *) o TSRMLS_CC);
86 30 : efree(o);
87 30 : }
88 :
89 30 : 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 30 : o = ecalloc(1, sizeof(*o));
95 30 : zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
96 30 : object_properties_init((zend_object *) o, ce);
97 :
98 30 : if (ptr) {
99 11 : *ptr = o;
100 : }
101 30 : o->proxy = proxy;
102 :
103 30 : o->zv.handle = zend_objects_store_put(o, NULL,
104 : php_property_proxy_object_free, NULL TSRMLS_CC);
105 30 : 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 30 : 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 30 : static zval *get_parent_proxied_value(zval *object TSRMLS_DC)
191 : {
192 30 : zval *value = NULL;
193 : php_property_proxy_object_t *obj;
194 :
195 30 : obj = zend_object_store_get_object(object TSRMLS_CC);
196 30 : if (obj->proxy) {
197 30 : if (obj->parent) {
198 : zval *parent;
199 :
200 30 : MAKE_STD_ZVAL(parent);
201 30 : parent->type = IS_OBJECT;
202 30 : parent->value.obj = obj->parent->zv;
203 30 : zend_objects_store_add_ref_by_handle(
204 30 : obj->parent->zv.handle TSRMLS_CC);
205 30 : value = get_proxied_value(parent TSRMLS_CC);
206 30 : zval_ptr_dtor(&parent);
207 : }
208 : }
209 :
210 30 : return value;
211 : }
212 :
213 59 : static zval *get_proxied_value(zval *object TSRMLS_DC)
214 : {
215 59 : zval **hash_value, *value = NULL;
216 : php_property_proxy_object_t *obj;
217 : STATUS rv;
218 :
219 59 : obj = zend_object_store_get_object(object TSRMLS_CC);
220 : debug_propro(1, "get", object, NULL, NULL TSRMLS_CC);
221 :
222 59 : if (obj->proxy) {
223 59 : if (obj->parent) {
224 16 : zval *parent_value = get_parent_proxied_value(object TSRMLS_CC);
225 :
226 16 : if (parent_value && parent_value != obj->proxy->container) {
227 3 : Z_ADDREF_P(parent_value);
228 3 : zval_ptr_dtor(&obj->proxy->container);
229 3 : obj->proxy->container = parent_value;
230 : }
231 : }
232 59 : switch (Z_TYPE_P(obj->proxy->container)) {
233 : case IS_OBJECT:
234 56 : value = zend_read_property(Z_OBJCE_P(obj->proxy->container),
235 28 : obj->proxy->container, obj->proxy->member_str,
236 28 : obj->proxy->member_len, 0 TSRMLS_CC);
237 28 : break;
238 :
239 : case IS_ARRAY:
240 62 : rv = zend_symtable_find(Z_ARRVAL_P(obj->proxy->container),
241 62 : obj->proxy->member_str, obj->proxy->member_len + 1,
242 : (void *) &hash_value);
243 :
244 31 : if (SUCCESS == rv) {
245 25 : value = *hash_value;
246 : }
247 31 : break;
248 : }
249 : }
250 :
251 : debug_propro(-1, "get", object, NULL, value TSRMLS_CC);
252 :
253 59 : return value;
254 : }
255 :
256 1 : static STATUS cast_proxied_value(zval *object, zval *return_value,
257 : int type TSRMLS_DC)
258 : {
259 : zval *proxied_value;
260 :
261 1 : if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
262 1 : RETVAL_ZVAL(proxied_value, 1, 0);
263 1 : if (Z_TYPE_P(proxied_value) != type) {
264 1 : convert_to_explicit_type(return_value, type);
265 : }
266 1 : return SUCCESS;
267 : }
268 :
269 0 : return FAILURE;
270 : }
271 :
272 34 : static void set_proxied_value(zval **object, zval *value TSRMLS_DC)
273 : {
274 : php_property_proxy_object_t *obj;
275 :
276 34 : obj = zend_object_store_get_object(*object TSRMLS_CC);
277 : debug_propro(1, "set", *object, NULL, value TSRMLS_CC);
278 :
279 34 : if (obj->proxy) {
280 34 : if (obj->parent) {
281 14 : zval *parent_value = get_parent_proxied_value(*object TSRMLS_CC);
282 :
283 14 : if (parent_value && parent_value != obj->proxy->container) {
284 5 : Z_ADDREF_P(parent_value);
285 5 : zval_ptr_dtor(&obj->proxy->container);
286 5 : obj->proxy->container = parent_value;
287 : }
288 : }
289 :
290 34 : switch (Z_TYPE_P(obj->proxy->container)) {
291 : case IS_OBJECT:
292 39 : zend_update_property(Z_OBJCE_P(obj->proxy->container),
293 13 : obj->proxy->container, obj->proxy->member_str,
294 13 : obj->proxy->member_len, value TSRMLS_CC);
295 13 : break;
296 :
297 : case IS_ARRAY:
298 21 : Z_ADDREF_P(value);
299 42 : zend_symtable_update(Z_ARRVAL_P(obj->proxy->container),
300 42 : obj->proxy->member_str, obj->proxy->member_len + 1,
301 : (void *) &value, sizeof(zval *), NULL);
302 21 : break;
303 : }
304 :
305 34 : if (obj->parent) {
306 : zval *zparent;
307 14 : MAKE_STD_ZVAL(zparent);
308 14 : zparent->type = IS_OBJECT;
309 14 : zparent->value.obj = obj->parent->zv;
310 14 : zend_objects_store_add_ref_by_handle(
311 14 : obj->parent->zv.handle TSRMLS_CC);
312 14 : set_proxied_value(&zparent, obj->proxy->container TSRMLS_CC);
313 14 : zval_ptr_dtor(&zparent);
314 : }
315 : }
316 :
317 : debug_propro(-1, "set", *object, NULL, NULL TSRMLS_CC);
318 34 : }
319 :
320 11 : static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
321 : {
322 11 : zval *value = NULL;
323 : zval *proxied_value;
324 11 : 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 11 : proxied_value = get_proxied_value(object TSRMLS_CC);
330 11 : convert_to_string_ex(&o);
331 :
332 11 : if (BP_VAR_R == type && proxied_value) {
333 0 : if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
334 : zval **hash_value;
335 0 : STATUS rv = zend_symtable_find(Z_ARRVAL_P(proxied_value),
336 0 : Z_STRVAL_P(o), Z_STRLEN_P(o), (void *) &hash_value);
337 :
338 0 : if (SUCCESS == rv) {
339 0 : Z_ADDREF_PP(hash_value);
340 0 : value = *hash_value;
341 : }
342 : }
343 : } else {
344 : php_property_proxy_t *proxy;
345 : php_property_proxy_object_t *proxy_obj;
346 :
347 11 : if (proxied_value) {
348 9 : convert_to_array(proxied_value);
349 9 : Z_ADDREF_P(proxied_value);
350 : } else {
351 2 : MAKE_STD_ZVAL(proxied_value);
352 2 : array_init(proxied_value);
353 2 : set_proxied_value(&object, proxied_value TSRMLS_CC);
354 : }
355 :
356 11 : proxy = php_property_proxy_init(proxied_value, Z_STRVAL_P(o),
357 11 : Z_STRLEN_P(o) TSRMLS_CC);
358 11 : zval_ptr_dtor(&proxied_value);
359 11 : MAKE_STD_ZVAL(value);
360 11 : Z_SET_REFCOUNT_P(value, 0);
361 11 : value->type = IS_OBJECT;
362 11 : value->value.obj = php_property_proxy_object_new_ex(
363 : php_property_proxy_get_class_entry(), proxy,
364 : &proxy_obj TSRMLS_CC);
365 11 : proxy_obj->parent = zend_object_store_get_object(object TSRMLS_CC);
366 11 : zend_objects_store_add_ref_by_handle(
367 11 : proxy_obj->parent->zv.handle TSRMLS_CC);
368 : }
369 11 : if (o && o != offset) {
370 0 : 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 11 : return value;
377 : }
378 :
379 2 : static int has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC)
380 : {
381 : zval *proxied_value;
382 2 : int exists = 0;
383 :
384 : debug_propro(1, "dim_exists", object, offset, NULL TSRMLS_CC);
385 :
386 2 : proxied_value = get_proxied_value(object TSRMLS_CC);
387 2 : if (!proxied_value) {
388 0 : exists = 0;
389 : } else {
390 2 : zval *o = offset;
391 :
392 2 : convert_to_string_ex(&o);
393 :
394 2 : if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
395 : zval **zentry;
396 2 : STATUS rv = zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &zentry);
397 :
398 2 : if (SUCCESS != rv) {
399 1 : exists = 0;
400 : } else {
401 1 : if (check_empty) {
402 0 : exists = Z_TYPE_PP(zentry) != IS_NULL;
403 : } else {
404 1 : exists = 1;
405 : }
406 : }
407 : }
408 :
409 2 : if (o != offset) {
410 0 : zval_ptr_dtor(&o);
411 : }
412 : }
413 :
414 : debug_propro(-1, "dim_exists", object, offset, NULL TSRMLS_CC);
415 :
416 2 : return exists;
417 : }
418 :
419 14 : static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
420 : {
421 14 : zval *proxied_value, *o = offset;
422 :
423 : debug_propro(1, "dim_write", object, offset, value TSRMLS_CC);
424 :
425 14 : proxied_value = get_proxied_value(object TSRMLS_CC);
426 :
427 14 : if (proxied_value) {
428 10 : convert_to_array(proxied_value);
429 10 : Z_ADDREF_P(proxied_value);
430 : } else {
431 4 : MAKE_STD_ZVAL(proxied_value);
432 4 : array_init(proxied_value);
433 : }
434 :
435 14 : if (Z_REFCOUNT_P(value) > 1) {
436 0 : SEPARATE_ZVAL(&value);
437 : }
438 14 : Z_ADDREF_P(value);
439 :
440 14 : if (o) {
441 6 : convert_to_string_ex(&o);
442 6 : zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o),
443 6 : Z_STRLEN_P(o) + 1, (void *) &value, sizeof(zval *), NULL);
444 : } else {
445 8 : zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value), (void *) &value,
446 : sizeof(zval *), NULL);
447 : }
448 :
449 14 : if (o && o != offset) {
450 0 : zval_ptr_dtor(&o);
451 : }
452 :
453 14 : set_proxied_value(&object, proxied_value TSRMLS_CC);
454 :
455 : debug_propro(-1, "dim_write", object, offset, proxied_value TSRMLS_CC);
456 :
457 14 : zval_ptr_dtor(&proxied_value);
458 14 : }
459 :
460 1 : 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 1 : proxied_value = get_proxied_value(object TSRMLS_CC);
467 :
468 1 : if (proxied_value && Z_TYPE_P(proxied_value) == IS_ARRAY) {
469 1 : zval *o = offset;
470 : STATUS rv;
471 :
472 1 : convert_to_string_ex(&o);
473 1 : rv = zend_symtable_del(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o),
474 1 : Z_STRLEN_P(o) + 1);
475 :
476 1 : if (SUCCESS == rv) {
477 1 : set_proxied_value(&object, proxied_value TSRMLS_CC);
478 : }
479 :
480 1 : if (o != offset) {
481 0 : zval_ptr_dtor(&o);
482 : }
483 : }
484 :
485 : debug_propro(-1, "dim_unset", object, offset, proxied_value TSRMLS_CC);
486 1 : }
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 11 : static PHP_METHOD(propro, __construct) {
494 : zend_error_handling zeh;
495 11 : zval *container, *parent = NULL;
496 : char *member_str;
497 : int member_len;
498 :
499 11 : zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
500 11 : 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 11 : obj = zend_object_store_get_object(getThis() TSRMLS_CC);
506 11 : obj->proxy = php_property_proxy_init(container, member_str,
507 : member_len TSRMLS_CC);
508 11 : if (parent) {
509 0 : zend_objects_store_add_ref(parent TSRMLS_CC);
510 0 : obj->parent = zend_object_store_get_object(parent TSRMLS_CC);
511 : }
512 : }
513 11 : zend_restore_error_handling(&zeh TSRMLS_CC);
514 11 : }
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 373 : static PHP_MINIT_FUNCTION(propro)
522 : {
523 373 : zend_class_entry ce = {0};
524 :
525 373 : INIT_NS_CLASS_ENTRY(ce, "php", "PropertyProxy",
526 : php_property_proxy_method_entry);
527 373 : php_property_proxy_class_entry = zend_register_internal_class_ex(&ce, NULL,
528 : NULL TSRMLS_CC);
529 373 : php_property_proxy_class_entry->create_object =
530 : php_property_proxy_object_new;
531 373 : php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL_CLASS;
532 :
533 373 : memcpy(&php_property_proxy_object_handlers, zend_get_std_object_handlers(),
534 : sizeof(zend_object_handlers));
535 373 : php_property_proxy_object_handlers.set = set_proxied_value;
536 373 : php_property_proxy_object_handlers.get = get_proxied_value;
537 373 : php_property_proxy_object_handlers.cast_object = cast_proxied_value;
538 373 : php_property_proxy_object_handlers.read_dimension = read_dimension;
539 373 : php_property_proxy_object_handlers.write_dimension = write_dimension;
540 373 : php_property_proxy_object_handlers.has_dimension = has_dimension;
541 373 : php_property_proxy_object_handlers.unset_dimension = unset_dimension;
542 :
543 373 : return SUCCESS;
544 : }
545 :
546 1 : PHP_MINFO_FUNCTION(propro)
547 : {
548 1 : php_info_print_table_start();
549 1 : php_info_print_table_header(2, "Property proxy support", "enabled");
550 1 : php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION);
551 1 : php_info_print_table_end();
552 1 : }
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 : */
|