licensing
[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 PHP_PROPRO_API php_property_proxy_t *php_property_proxy_init(zval *container, const char *member_str, size_t member_len TSRMLS_DC)
26 {
27 php_property_proxy_t *proxy = ecalloc(1, sizeof(*proxy));
28
29 Z_ADDREF_P(container);
30 proxy->container = container;
31 proxy->member_str = estrndup(member_str, member_len);
32 proxy->member_len = member_len;
33
34 return proxy;
35 }
36
37 PHP_PROPRO_API void php_property_proxy_free(php_property_proxy_t **proxy)
38 {
39 if (*proxy) {
40 zval_ptr_dtor(&(*proxy)->container);
41 efree((*proxy)->member_str);
42 efree(*proxy);
43 *proxy = NULL;
44 }
45 }
46
47 static zend_class_entry *php_property_proxy_class_entry;
48 static zend_object_handlers php_property_proxy_object_handlers;
49
50 PHP_PROPRO_API zend_class_entry *php_property_proxy_get_class_entry(void)
51 {
52 return php_property_proxy_class_entry;
53 }
54
55 PHP_PROPRO_API zend_object_value php_property_proxy_object_new(zend_class_entry *ce TSRMLS_DC)
56 {
57 return php_property_proxy_object_new_ex(ce, NULL, NULL TSRMLS_CC);
58 }
59
60 static void php_property_proxy_object_free(void *object TSRMLS_DC)
61 {
62 php_property_proxy_object_t *o = object;
63
64 if (o->proxy) {
65 php_property_proxy_free(&o->proxy);
66 }
67 if (o->parent) {
68 zend_objects_store_del_ref_by_handle_ex(o->parent->zv.handle, o->parent->zv.handlers TSRMLS_CC);
69 o->parent = NULL;
70 }
71 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
72 efree(o);
73 }
74
75 PHP_PROPRO_API zend_object_value php_property_proxy_object_new_ex(zend_class_entry *ce, php_property_proxy_t *proxy, php_property_proxy_object_t **ptr TSRMLS_DC)
76 {
77 php_property_proxy_object_t *o;
78
79 o = ecalloc(1, sizeof(*o));
80 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
81 object_properties_init((zend_object *) o, ce);
82
83 if (ptr) {
84 *ptr = o;
85 }
86 o->proxy = proxy;
87
88 o->zv.handle = zend_objects_store_put(o, NULL, php_property_proxy_object_free, NULL TSRMLS_CC);
89 o->zv.handlers = &php_property_proxy_object_handlers;
90
91 return o->zv;
92 }
93
94 static zval *get_proxied_value(zval *object TSRMLS_DC);
95 static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC);
96 static STATUS cast_proxied_value(zval *object, zval *return_value, int type TSRMLS_DC);
97 static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC);
98 static void set_proxied_value(zval **object, zval *value TSRMLS_DC);
99
100 static zval *get_proxied_value(zval *object TSRMLS_DC)
101 {
102 zval **hash_value, *value = NULL;
103 php_property_proxy_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
104
105 if (obj->proxy) {
106 switch (Z_TYPE_P(obj->proxy->container)) {
107 case IS_OBJECT:
108 value = zend_read_property(Z_OBJCE_P(obj->proxy->container), obj->proxy->container, obj->proxy->member_str, obj->proxy->member_len, 0 TSRMLS_CC);
109 break;
110
111 case IS_ARRAY:
112 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(obj->proxy->container), obj->proxy->member_str, obj->proxy->member_len + 1, (void *) &hash_value)) {
113 value = *hash_value;
114 }
115 break;
116 }
117 }
118
119 return value;
120 }
121
122 static STATUS cast_proxied_value(zval *object, zval *return_value, int type TSRMLS_DC)
123 {
124 zval *proxied_value;
125
126 if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
127 RETVAL_ZVAL(proxied_value, 1, 0);
128 if (Z_TYPE_P(proxied_value) != type) {
129 convert_to_explicit_type(return_value, type);
130 }
131 return SUCCESS;
132 }
133
134 return FAILURE;
135 }
136
137 static void set_proxied_value(zval **object, zval *value TSRMLS_DC)
138 {
139 php_property_proxy_object_t *obj = zend_object_store_get_object(*object TSRMLS_CC);
140
141 if (obj->proxy) {
142 switch (Z_TYPE_P(obj->proxy->container)) {
143 case IS_OBJECT:
144 zend_update_property(Z_OBJCE_P(obj->proxy->container), obj->proxy->container, obj->proxy->member_str, obj->proxy->member_len, value TSRMLS_CC);
145 break;
146
147 case IS_ARRAY:
148 Z_ADDREF_P(value);
149 zend_symtable_update(Z_ARRVAL_P(obj->proxy->container), obj->proxy->member_str, obj->proxy->member_len + 1, (void *) &value, sizeof(zval *), NULL);
150 break;
151 }
152
153 if (obj->parent) {
154 zval *zparent;
155 MAKE_STD_ZVAL(zparent);
156 zparent->type = IS_OBJECT;
157 zparent->value.obj = obj->parent->zv;
158 set_proxied_value(&zparent, obj->proxy->container TSRMLS_CC);
159 }
160 }
161 }
162
163 static zval *read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
164 {
165 zval *value = NULL;
166 zval *proxied_value;
167
168 if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
169 zval *o = offset;
170
171 convert_to_string_ex(&o);
172
173 if (Z_TYPE_P(proxied_value) == IS_ARRAY) {
174 zval **hash_value;
175 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o), (void *) &hash_value)) {
176 Z_ADDREF_PP(hash_value);
177 value = *hash_value;
178 }
179 }
180
181 if (!value) {
182 zval **hash_value;
183
184 SEPARATE_ZVAL(&proxied_value);
185 convert_to_array(proxied_value);
186
187 MAKE_STD_ZVAL(value);
188 ZVAL_NULL(value);
189 zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &value, sizeof(zval *), (void *) &hash_value);
190 value = *hash_value;
191 Z_SET_ISREF_P(value);
192
193 set_proxied_value(&object, proxied_value TSRMLS_CC);
194 }
195
196 if (o != offset) {
197 zval_ptr_dtor(&o);
198 }
199 }
200
201 return value;
202 }
203
204 static void write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
205 {
206 zval *proxied_value;
207
208 if ((proxied_value = get_proxied_value(object TSRMLS_CC))) {
209 zval *o = offset;
210
211 SEPARATE_ZVAL(&proxied_value);
212 convert_to_array(proxied_value);
213
214 if (Z_REFCOUNT_P(value) > 1) {
215 SEPARATE_ZVAL(&value);
216 }
217 Z_ADDREF_P(value);
218
219 if (o) {
220 convert_to_string_ex(&o);
221 zend_symtable_update(Z_ARRVAL_P(proxied_value), Z_STRVAL_P(o), Z_STRLEN_P(o) + 1, (void *) &value, sizeof(zval *), NULL);
222 } else {
223 zend_hash_next_index_insert(Z_ARRVAL_P(proxied_value), (void *) &value, sizeof(zval *), NULL);
224 }
225
226 if (o != offset) {
227 zval_ptr_dtor(&o);
228 }
229
230 set_proxied_value(&object, proxied_value TSRMLS_CC);
231 }
232 }
233
234 ZEND_BEGIN_ARG_INFO_EX(ai_propro_construct, 0, 0, 2)
235 ZEND_ARG_INFO(0, object)
236 ZEND_ARG_INFO(0, member)
237 ZEND_ARG_OBJ_INFO(0, parent, php\\PropertyProxy, 1)
238 ZEND_END_ARG_INFO();
239 static PHP_METHOD(propro, __construct) {
240 zend_error_handling zeh;
241 zval *container, *parent = NULL;
242 char *member_str;
243 int member_len;
244
245 zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
246 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs|O!", &container, &member_str, &member_len, &parent, php_property_proxy_class_entry)) {
247 php_property_proxy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
248
249 obj->proxy = php_property_proxy_init(container, member_str, member_len TSRMLS_CC);
250 if (parent) {
251 zend_objects_store_add_ref(parent TSRMLS_CC);
252 obj->parent = zend_object_store_get_object(parent TSRMLS_CC);
253 }
254 }
255 zend_restore_error_handling(&zeh TSRMLS_CC);
256 }
257
258 static const zend_function_entry php_property_proxy_method_entry[] = {
259 PHP_ME(propro, __construct, ai_propro_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
260 {0}
261 };
262
263 static PHP_MINIT_FUNCTION(propro)
264 {
265 zend_class_entry ce = {0};
266
267 INIT_NS_CLASS_ENTRY(ce, "php", "PropertyProxy", php_property_proxy_method_entry);
268 php_property_proxy_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
269 php_property_proxy_class_entry->create_object = php_property_proxy_object_new;
270 php_property_proxy_class_entry->ce_flags |= ZEND_ACC_FINAL_CLASS;
271
272 memcpy(&php_property_proxy_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
273 php_property_proxy_object_handlers.set = set_proxied_value;
274 php_property_proxy_object_handlers.get = get_proxied_value;
275 php_property_proxy_object_handlers.cast_object = cast_proxied_value;
276 php_property_proxy_object_handlers.read_dimension = read_dimension;
277 php_property_proxy_object_handlers.write_dimension = write_dimension;
278
279 return SUCCESS;
280 }
281
282 PHP_MINFO_FUNCTION(propro)
283 {
284 php_info_print_table_start();
285 php_info_print_table_header(2, "Property proxy support", "enabled");
286 php_info_print_table_row(2, "Extension version", PHP_PROPRO_VERSION);
287 php_info_print_table_end();
288 }
289
290 static const zend_function_entry propro_functions[] = {
291 {0}
292 };
293
294 zend_module_entry propro_module_entry = {
295 STANDARD_MODULE_HEADER,
296 "propro",
297 propro_functions,
298 PHP_MINIT(propro),
299 NULL,
300 NULL,
301 NULL,
302 PHP_MINFO(propro),
303 PHP_PROPRO_VERSION,
304 STANDARD_MODULE_PROPERTIES
305 };
306
307 #ifdef COMPILE_DL_PROPRO
308 ZEND_GET_MODULE(propro)
309 #endif
310
311
312 /*
313 * Local variables:
314 * tab-width: 4
315 * c-basic-offset: 4
316 * End:
317 * vim600: noet sw=4 ts=4 fdm=marker
318 * vim<600: noet sw=4 ts=4
319 */