]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - vendor/pimple/pimple/ext/pimple/pimple.c
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / vendor / pimple / pimple / ext / pimple / pimple.c
1
2 /*
3  * This file is part of Pimple.
4  *
5  * Copyright (c) 2014 Fabien Potencier
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is furnished
12  * to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "php.h"
31 #include "php_ini.h"
32 #include "ext/standard/info.h"
33 #include "php_pimple.h"
34 #include "pimple_compat.h"
35 #include "zend_interfaces.h"
36 #include "zend.h"
37 #include "Zend/zend_closures.h"
38 #include "ext/spl/spl_exceptions.h"
39 #include "Zend/zend_exceptions.h"
40 #include "main/php_output.h"
41 #include "SAPI.h"
42
43 static zend_class_entry *pimple_ce;
44 static zend_object_handlers pimple_object_handlers;
45 static zend_class_entry *pimple_closure_ce;
46 static zend_class_entry *pimple_serviceprovider_ce;
47 static zend_object_handlers pimple_closure_object_handlers;
48 static zend_internal_function pimple_closure_invoker_function;
49
50 #define FETCH_DIM_HANDLERS_VARS         pimple_object *pimple_obj = NULL; \
51                                                                         ulong index; \
52                                                                         pimple_obj = (pimple_object *)zend_object_store_get_object(object TSRMLS_CC); \
53
54 #define PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS        do { \
55         if (ce != pimple_ce) { \
56                 zend_hash_find(&ce->function_table, ZEND_STRS("offsetget"), (void **)&function); \
57                 if (function->common.scope != ce) { /* if the function is not defined in this actual class */ \
58                         pimple_object_handlers.read_dimension = pimple_object_read_dimension; /* then overwrite the handler to use custom one */ \
59                 } \
60                 zend_hash_find(&ce->function_table, ZEND_STRS("offsetset"), (void **)&function); \
61                 if (function->common.scope != ce) { \
62                         pimple_object_handlers.write_dimension = pimple_object_write_dimension; \
63                 } \
64                 zend_hash_find(&ce->function_table, ZEND_STRS("offsetexists"), (void **)&function); \
65                 if (function->common.scope != ce) { \
66                         pimple_object_handlers.has_dimension = pimple_object_has_dimension; \
67                 } \
68                 zend_hash_find(&ce->function_table, ZEND_STRS("offsetunset"), (void **)&function); \
69                 if (function->common.scope != ce) { \
70                         pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \
71                 } \
72         } else { \
73                 pimple_object_handlers.read_dimension = pimple_object_read_dimension; \
74                 pimple_object_handlers.write_dimension = pimple_object_write_dimension; \
75                 pimple_object_handlers.has_dimension = pimple_object_has_dimension; \
76                 pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \
77         }\
78                                                                                         } while(0);
79
80 #define PIMPLE_CALL_CB  do { \
81                         zend_fcall_info_argn(&fci TSRMLS_CC, 1, &object); \
82                         fci.size           = sizeof(fci); \
83                         fci.object_ptr     = retval->fcc.object_ptr; \
84                         fci.function_name  = retval->value; \
85                         fci.no_separation  = 1; \
86                         fci.retval_ptr_ptr = &retval_ptr_ptr; \
87 \
88                         zend_call_function(&fci, &retval->fcc TSRMLS_CC); \
89                         efree(fci.params); \
90                         if (EG(exception)) { \
91                                 return EG(uninitialized_zval_ptr); \
92                         } \
93                                                 } while(0);
94
95 ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0)
96 ZEND_ARG_ARRAY_INFO(0, value, 0)
97 ZEND_END_ARG_INFO()
98
99 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetset, 0, 0, 2)
100 ZEND_ARG_INFO(0, offset)
101 ZEND_ARG_INFO(0, value)
102 ZEND_END_ARG_INFO()
103
104 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetget, 0, 0, 1)
105 ZEND_ARG_INFO(0, offset)
106 ZEND_END_ARG_INFO()
107
108 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetexists, 0, 0, 1)
109 ZEND_ARG_INFO(0, offset)
110 ZEND_END_ARG_INFO()
111
112 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetunset, 0, 0, 1)
113 ZEND_ARG_INFO(0, offset)
114 ZEND_END_ARG_INFO()
115
116 ZEND_BEGIN_ARG_INFO_EX(arginfo_factory, 0, 0, 1)
117 ZEND_ARG_INFO(0, callable)
118 ZEND_END_ARG_INFO()
119
120 ZEND_BEGIN_ARG_INFO_EX(arginfo_protect, 0, 0, 1)
121 ZEND_ARG_INFO(0, callable)
122 ZEND_END_ARG_INFO()
123
124 ZEND_BEGIN_ARG_INFO_EX(arginfo_raw, 0, 0, 1)
125 ZEND_ARG_INFO(0, id)
126 ZEND_END_ARG_INFO()
127
128 ZEND_BEGIN_ARG_INFO_EX(arginfo_extend, 0, 0, 2)
129 ZEND_ARG_INFO(0, id)
130 ZEND_ARG_INFO(0, callable)
131 ZEND_END_ARG_INFO()
132
133 ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 0)
134 ZEND_END_ARG_INFO()
135
136 ZEND_BEGIN_ARG_INFO_EX(arginfo_register, 0, 0, 1)
137 ZEND_ARG_OBJ_INFO(0, provider, Pimple\\ServiceProviderInterface, 0)
138 ZEND_ARG_ARRAY_INFO(0, values, 1)
139 ZEND_END_ARG_INFO()
140
141 ZEND_BEGIN_ARG_INFO_EX(arginfo_serviceprovider_register, 0, 0, 1)
142 ZEND_ARG_OBJ_INFO(0, pimple, Pimple\\Container, 0)
143 ZEND_END_ARG_INFO()
144
145 static const zend_function_entry pimple_ce_functions[] = {
146         PHP_ME(Pimple, __construct,     arginfo___construct, ZEND_ACC_PUBLIC)
147         PHP_ME(Pimple, factory,         arginfo_factory,         ZEND_ACC_PUBLIC)
148         PHP_ME(Pimple, protect,         arginfo_protect,         ZEND_ACC_PUBLIC)
149         PHP_ME(Pimple, raw,             arginfo_raw,             ZEND_ACC_PUBLIC)
150         PHP_ME(Pimple, extend,          arginfo_extend,          ZEND_ACC_PUBLIC)
151         PHP_ME(Pimple, keys,            arginfo_keys,            ZEND_ACC_PUBLIC)
152         PHP_ME(Pimple, register,                arginfo_register,                ZEND_ACC_PUBLIC)
153
154         PHP_ME(Pimple, offsetSet,       arginfo_offsetset,       ZEND_ACC_PUBLIC)
155         PHP_ME(Pimple, offsetGet,       arginfo_offsetget,       ZEND_ACC_PUBLIC)
156         PHP_ME(Pimple, offsetExists,    arginfo_offsetexists,    ZEND_ACC_PUBLIC)
157         PHP_ME(Pimple, offsetUnset,     arginfo_offsetunset,     ZEND_ACC_PUBLIC)
158         PHP_FE_END
159 };
160
161 static const zend_function_entry pimple_serviceprovider_iface_ce_functions[] = {
162         PHP_ABSTRACT_ME(ServiceProviderInterface, register, arginfo_serviceprovider_register)
163         PHP_FE_END
164 };
165
166 static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC)
167 {
168         zend_object_std_dtor(&obj->zobj TSRMLS_CC);
169         if (obj->factory) {
170                 zval_ptr_dtor(&obj->factory);
171         }
172         if (obj->callable) {
173                 zval_ptr_dtor(&obj->callable);
174         }
175         efree(obj);
176 }
177
178 static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC)
179 {
180         zend_hash_destroy(&obj->factories);
181         zend_hash_destroy(&obj->protected);
182         zend_hash_destroy(&obj->values);
183         zend_object_std_dtor(&obj->zobj TSRMLS_CC);
184         efree(obj);
185 }
186
187 static void pimple_free_bucket(pimple_bucket_value *bucket)
188 {
189         if (bucket->raw) {
190                 zval_ptr_dtor(&bucket->raw);
191         }
192 }
193
194 static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC)
195 {
196         zend_object_value retval;
197         pimple_closure_object *pimple_closure_obj = NULL;
198
199         pimple_closure_obj = ecalloc(1, sizeof(pimple_closure_object));
200         ZEND_OBJ_INIT(&pimple_closure_obj->zobj, ce);
201
202         pimple_closure_object_handlers.get_constructor = pimple_closure_get_constructor;
203         retval.handlers = &pimple_closure_object_handlers;
204         retval.handle   = zend_objects_store_put(pimple_closure_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_closure_free_object_storage, NULL TSRMLS_CC);
205
206         return retval;
207 }
208
209 static zend_function *pimple_closure_get_constructor(zval *obj TSRMLS_DC)
210 {
211         zend_error(E_ERROR, "Pimple\\ContainerClosure is an internal class and cannot be instantiated");
212
213         return NULL;
214 }
215
216 static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC)
217 {
218         *zobj_ptr = obj;
219         *ce_ptr   = Z_OBJCE_P(obj);
220         *fptr_ptr = (zend_function *)&pimple_closure_invoker_function;
221
222         return SUCCESS;
223 }
224
225 static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC)
226 {
227         zend_object_value retval;
228         pimple_object *pimple_obj  = NULL;
229         zend_function *function    = NULL;
230
231         pimple_obj = emalloc(sizeof(pimple_object));
232         ZEND_OBJ_INIT(&pimple_obj->zobj, ce);
233
234         PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS
235
236         retval.handlers = &pimple_object_handlers;
237         retval.handle   = zend_objects_store_put(pimple_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_free_object_storage, NULL TSRMLS_CC);
238
239         zend_hash_init(&pimple_obj->factories, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0);
240         zend_hash_init(&pimple_obj->protected, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0);
241         zend_hash_init(&pimple_obj->values, PIMPLE_DEFAULT_ZVAL_VALUES_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0);
242
243         return retval;
244 }
245
246 static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
247 {
248         FETCH_DIM_HANDLERS_VARS
249
250         pimple_bucket_value pimple_value = {0}, *found_value = NULL;
251         ulong hash;
252
253         pimple_zval_to_pimpleval(value, &pimple_value TSRMLS_CC);
254
255         if (!offset) {/* $p[] = 'foo' when not overloaded */
256                 zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL);
257                 Z_ADDREF_P(value);
258                 return;
259         }
260
261         switch (Z_TYPE_P(offset)) {
262         case IS_STRING:
263                 hash = zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
264                 zend_hash_quick_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void **)&found_value);
265                 if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) {
266                         pimple_free_bucket(&pimple_value);
267                         zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Cannot override frozen service \"%s\".", Z_STRVAL_P(offset));
268                         return;
269                 }
270                 if (zend_hash_quick_update(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) {
271                         pimple_free_bucket(&pimple_value);
272                         return;
273                 }
274                 Z_ADDREF_P(value);
275         break;
276         case IS_DOUBLE:
277         case IS_BOOL:
278         case IS_LONG:
279                 if (Z_TYPE_P(offset) == IS_DOUBLE) {
280                         index = (ulong)Z_DVAL_P(offset);
281                 } else {
282                         index = Z_LVAL_P(offset);
283                 }
284                 zend_hash_index_find(&pimple_obj->values, index, (void **)&found_value);
285                 if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) {
286                         pimple_free_bucket(&pimple_value);
287                         zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Cannot override frozen service \"%ld\".", index);
288                         return;
289                 }
290                 if (zend_hash_index_update(&pimple_obj->values, index, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) {
291                         pimple_free_bucket(&pimple_value);
292                         return;
293                 }
294                 Z_ADDREF_P(value);
295         break;
296         case IS_NULL: /* $p[] = 'foo' when overloaded */
297                 zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL);
298                 Z_ADDREF_P(value);
299         break;
300         default:
301                 pimple_free_bucket(&pimple_value);
302                 zend_error(E_WARNING, "Unsupported offset type");
303         }
304 }
305
306 static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC)
307 {
308         FETCH_DIM_HANDLERS_VARS
309
310         switch (Z_TYPE_P(offset)) {
311         case IS_STRING:
312                 zend_symtable_del(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
313                 zend_symtable_del(&pimple_obj->factories, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
314                 zend_symtable_del(&pimple_obj->protected, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
315         break;
316         case IS_DOUBLE:
317         case IS_BOOL:
318         case IS_LONG:
319                 if (Z_TYPE_P(offset) == IS_DOUBLE) {
320                         index = (ulong)Z_DVAL_P(offset);
321                 } else {
322                         index = Z_LVAL_P(offset);
323                 }
324                 zend_hash_index_del(&pimple_obj->values, index);
325                 zend_hash_index_del(&pimple_obj->factories, index);
326                 zend_hash_index_del(&pimple_obj->protected, index);
327         break;
328         default:
329                 zend_error(E_WARNING, "Unsupported offset type");
330         }
331 }
332
333 static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC)
334 {
335         FETCH_DIM_HANDLERS_VARS
336
337         pimple_bucket_value *retval = NULL;
338
339         switch (Z_TYPE_P(offset)) {
340         case IS_STRING:
341                 if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == SUCCESS) {
342                         switch (check_empty) {
343                         case 0: /* isset */
344                                 return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;) */
345                         case 1: /* empty */
346                         default:
347                                 return zend_is_true(retval->value);
348                         }
349                 }
350                 return 0;
351         break;
352         case IS_DOUBLE:
353         case IS_BOOL:
354         case IS_LONG:
355                 if (Z_TYPE_P(offset) == IS_DOUBLE) {
356                         index = (ulong)Z_DVAL_P(offset);
357                 } else {
358                         index = Z_LVAL_P(offset);
359                 }
360                 if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == SUCCESS) {
361                         switch (check_empty) {
362                                 case 0: /* isset */
363                                         return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;)*/
364                                 case 1: /* empty */
365                                 default:
366                                         return zend_is_true(retval->value);
367                         }
368                 }
369                 return 0;
370         break;
371         default:
372                 zend_error(E_WARNING, "Unsupported offset type");
373                 return 0;
374         }
375 }
376
377 static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
378 {
379         FETCH_DIM_HANDLERS_VARS
380
381         pimple_bucket_value *retval = NULL;
382         zend_fcall_info fci         = {0};
383         zval *retval_ptr_ptr        = NULL;
384
385         switch (Z_TYPE_P(offset)) {
386         case IS_STRING:
387                 if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == FAILURE) {
388                         zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset));
389                         return EG(uninitialized_zval_ptr);
390                 }
391         break;
392         case IS_DOUBLE:
393         case IS_BOOL:
394         case IS_LONG:
395                 if (Z_TYPE_P(offset) == IS_DOUBLE) {
396                         index = (ulong)Z_DVAL_P(offset);
397                 } else {
398                         index = Z_LVAL_P(offset);
399                 }
400                 if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == FAILURE) {
401                         return EG(uninitialized_zval_ptr);
402                 }
403         break;
404         case IS_NULL: /* $p[][3] = 'foo' first dim access */
405                 return EG(uninitialized_zval_ptr);
406         break;
407         default:
408                 zend_error(E_WARNING, "Unsupported offset type");
409                 return EG(uninitialized_zval_ptr);
410         }
411
412         if(retval->type == PIMPLE_IS_PARAM) {
413                 return retval->value;
414         }
415
416         if (zend_hash_index_exists(&pimple_obj->protected, retval->handle_num)) {
417                 /* Service is protected, return the value every time */
418                 return retval->value;
419         }
420
421         if (zend_hash_index_exists(&pimple_obj->factories, retval->handle_num)) {
422                 /* Service is a factory, call it everytime and never cache its result */
423                 PIMPLE_CALL_CB
424                 Z_DELREF_P(retval_ptr_ptr); /* fetch dim addr will increment refcount */
425                 return retval_ptr_ptr;
426         }
427
428         if (retval->initialized == 1) {
429                 /* Service has already been called, return its cached value */
430                 return retval->value;
431         }
432
433         ALLOC_INIT_ZVAL(retval->raw);
434         MAKE_COPY_ZVAL(&retval->value, retval->raw);
435
436         PIMPLE_CALL_CB
437
438         retval->initialized = 1;
439         zval_ptr_dtor(&retval->value);
440         retval->value = retval_ptr_ptr;
441
442         return retval->value;
443 }
444
445 static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC)
446 {
447         if (Z_TYPE_P(_zval) != IS_OBJECT) {
448                 return FAILURE;
449         }
450
451         if (_pimple_bucket_value->fcc.called_scope) {
452                 return SUCCESS;
453         }
454
455         if (Z_OBJ_HANDLER_P(_zval, get_closure) && Z_OBJ_HANDLER_P(_zval, get_closure)(_zval, &_pimple_bucket_value->fcc.calling_scope, &_pimple_bucket_value->fcc.function_handler, &_pimple_bucket_value->fcc.object_ptr TSRMLS_CC) == SUCCESS) {
456                 _pimple_bucket_value->fcc.called_scope = _pimple_bucket_value->fcc.calling_scope;
457                 return SUCCESS;
458         } else {
459                 return FAILURE;
460         }
461 }
462
463 static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC)
464 {
465         _pimple_bucket_value->value = _zval;
466
467         if (Z_TYPE_P(_zval) != IS_OBJECT) {
468                 return PIMPLE_IS_PARAM;
469         }
470
471         if (pimple_zval_is_valid_callback(_zval, _pimple_bucket_value TSRMLS_CC) == SUCCESS) {
472                 _pimple_bucket_value->type       = PIMPLE_IS_SERVICE;
473                 _pimple_bucket_value->handle_num = Z_OBJ_HANDLE_P(_zval);
474         }
475
476         return PIMPLE_IS_SERVICE;
477 }
478
479 static void pimple_bucket_dtor(pimple_bucket_value *bucket)
480 {
481         zval_ptr_dtor(&bucket->value);
482         pimple_free_bucket(bucket);
483 }
484
485 PHP_METHOD(Pimple, protect)
486 {
487         zval *protected     = NULL;
488         pimple_object *pobj = NULL;
489         pimple_bucket_value bucket = {0};
490
491         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &protected) == FAILURE) {
492                 return;
493         }
494
495         if (pimple_zval_is_valid_callback(protected, &bucket TSRMLS_CC) == FAILURE) {
496                 pimple_free_bucket(&bucket);
497                 zend_throw_exception(spl_ce_InvalidArgumentException, "Callable is not a Closure or invokable object.", 0 TSRMLS_CC);
498                 return;
499         }
500
501         pimple_zval_to_pimpleval(protected, &bucket TSRMLS_CC);
502         pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
503
504         if (zend_hash_index_update(&pobj->protected, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) {
505                 Z_ADDREF_P(protected);
506                 RETURN_ZVAL(protected, 1 , 0);
507         } else {
508                 pimple_free_bucket(&bucket);
509         }
510         RETURN_FALSE;
511 }
512
513 PHP_METHOD(Pimple, raw)
514 {
515         zval *offset = NULL;
516         pimple_object *pobj        = NULL;
517         pimple_bucket_value *value = NULL;
518         ulong index;
519
520         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
521                 return;
522         }
523
524         pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
525
526         switch (Z_TYPE_P(offset)) {
527                 case IS_STRING:
528                         if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) {
529                                 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset));
530                                 RETURN_NULL();
531                         }
532                 break;
533                 case IS_DOUBLE:
534                 case IS_BOOL:
535                 case IS_LONG:
536                         if (Z_TYPE_P(offset) == IS_DOUBLE) {
537                                 index = (ulong)Z_DVAL_P(offset);
538                         } else {
539                                 index = Z_LVAL_P(offset);
540                         }
541                         if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) {
542                                 RETURN_NULL();
543                         }
544                 break;
545                 case IS_NULL:
546                 default:
547                         zend_error(E_WARNING, "Unsupported offset type");
548         }
549
550         if (value->raw) {
551                 RETVAL_ZVAL(value->raw, 1, 0);
552         } else {
553                 RETVAL_ZVAL(value->value, 1, 0);
554         }
555 }
556
557 PHP_METHOD(Pimple, extend)
558 {
559         zval *offset = NULL, *callable = NULL, *pimple_closure_obj = NULL;
560         pimple_bucket_value bucket = {0}, *value = NULL;
561         pimple_object *pobj          = NULL;
562         pimple_closure_object *pcobj = NULL;
563         ulong index;
564
565         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &callable) == FAILURE) {
566                 return;
567         }
568
569         pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
570
571         switch (Z_TYPE_P(offset)) {
572                 case IS_STRING:
573                         if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) {
574                                 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" is not defined.", Z_STRVAL_P(offset));
575                                 RETURN_NULL();
576                         }
577                         if (value->type != PIMPLE_IS_SERVICE) {
578                                 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%s\" does not contain an object definition.", Z_STRVAL_P(offset));
579                                 RETURN_NULL();
580                         }
581                 break;
582                 case IS_DOUBLE:
583                 case IS_BOOL:
584                 case IS_LONG:
585                         if (Z_TYPE_P(offset) == IS_DOUBLE) {
586                                 index = (ulong)Z_DVAL_P(offset);
587                         } else {
588                                 index = Z_LVAL_P(offset);
589                         }
590                         if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) {
591                                 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%ld\" is not defined.", index);
592                                 RETURN_NULL();
593                         }
594                         if (value->type != PIMPLE_IS_SERVICE) {
595                                 zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Identifier \"%ld\" does not contain an object definition.", index);
596                                 RETURN_NULL();
597                         }
598                 break;
599                 case IS_NULL:
600                 default:
601                         zend_error(E_WARNING, "Unsupported offset type");
602         }
603
604         if (pimple_zval_is_valid_callback(callable, &bucket TSRMLS_CC) == FAILURE) {
605                 pimple_free_bucket(&bucket);
606                 zend_throw_exception(spl_ce_InvalidArgumentException, "Extension service definition is not a Closure or invokable object.", 0 TSRMLS_CC);
607                 RETURN_NULL();
608         }
609         pimple_free_bucket(&bucket);
610
611         ALLOC_INIT_ZVAL(pimple_closure_obj);
612         object_init_ex(pimple_closure_obj, pimple_closure_ce);
613
614         pcobj = zend_object_store_get_object(pimple_closure_obj TSRMLS_CC);
615         pcobj->callable = callable;
616         pcobj->factory  = value->value;
617         Z_ADDREF_P(callable);
618         Z_ADDREF_P(value->value);
619
620         if (zend_hash_index_exists(&pobj->factories, value->handle_num)) {
621                 pimple_zval_to_pimpleval(pimple_closure_obj, &bucket TSRMLS_CC);
622                 zend_hash_index_del(&pobj->factories, value->handle_num);
623                 zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL);
624                 Z_ADDREF_P(pimple_closure_obj);
625         }
626
627         pimple_object_write_dimension(getThis(), offset, pimple_closure_obj TSRMLS_CC);
628
629         RETVAL_ZVAL(pimple_closure_obj, 1, 1);
630 }
631
632 PHP_METHOD(Pimple, keys)
633 {
634         HashPosition pos;
635         pimple_object *pobj = NULL;
636         zval **value        = NULL;
637         zval *endval        = NULL;
638         char *str_index     = NULL;
639         int str_len;
640         ulong num_index;
641
642         if (zend_parse_parameters_none() == FAILURE) {
643                 return;
644         }
645
646         pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
647         array_init_size(return_value, zend_hash_num_elements(&pobj->values));
648
649         zend_hash_internal_pointer_reset_ex(&pobj->values, &pos);
650
651         while(zend_hash_get_current_data_ex(&pobj->values, (void **)&value, &pos) == SUCCESS) {
652                 MAKE_STD_ZVAL(endval);
653                 switch (zend_hash_get_current_key_ex(&pobj->values, &str_index, (uint *)&str_len, &num_index, 0, &pos)) {
654                         case HASH_KEY_IS_STRING:
655                                 ZVAL_STRINGL(endval, str_index, str_len - 1, 1);
656                                 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL);
657                         break;
658                         case HASH_KEY_IS_LONG:
659                                 ZVAL_LONG(endval, num_index);
660                                 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL);
661                         break;
662                 }
663         zend_hash_move_forward_ex(&pobj->values, &pos);
664         }
665 }
666
667 PHP_METHOD(Pimple, factory)
668 {
669         zval *factory       = NULL;
670         pimple_object *pobj = NULL;
671         pimple_bucket_value bucket = {0};
672
673         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &factory) == FAILURE) {
674                 return;
675         }
676
677         if (pimple_zval_is_valid_callback(factory, &bucket TSRMLS_CC) == FAILURE) {
678                 pimple_free_bucket(&bucket);
679                 zend_throw_exception(spl_ce_InvalidArgumentException, "Service definition is not a Closure or invokable object.", 0 TSRMLS_CC);
680                 return;
681         }
682
683         pimple_zval_to_pimpleval(factory, &bucket TSRMLS_CC);
684         pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
685
686         if (zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) {
687                 Z_ADDREF_P(factory);
688                 RETURN_ZVAL(factory, 1 , 0);
689         } else {
690                 pimple_free_bucket(&bucket);
691         }
692
693         RETURN_FALSE;
694 }
695
696 PHP_METHOD(Pimple, offsetSet)
697 {
698         zval *offset = NULL, *value = NULL;
699
700         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &value) == FAILURE) {
701                 return;
702         }
703
704         pimple_object_write_dimension(getThis(), offset, value TSRMLS_CC);
705 }
706
707 PHP_METHOD(Pimple, offsetGet)
708 {
709         zval *offset = NULL, *retval = NULL;
710
711         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
712                 return;
713         }
714
715         retval = pimple_object_read_dimension(getThis(), offset, 0 TSRMLS_CC);
716
717         RETVAL_ZVAL(retval, 1, 0);
718 }
719
720 PHP_METHOD(Pimple, offsetUnset)
721 {
722         zval *offset = NULL;
723
724         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
725                 return;
726         }
727
728         pimple_object_unset_dimension(getThis(), offset TSRMLS_CC);
729 }
730
731 PHP_METHOD(Pimple, offsetExists)
732 {
733         zval *offset = NULL;
734
735         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
736                 return;
737         }
738
739         RETVAL_BOOL(pimple_object_has_dimension(getThis(), offset, 1 TSRMLS_CC));
740 }
741
742 PHP_METHOD(Pimple, register)
743 {
744         zval *provider;
745         zval **data;
746         zval *retval = NULL;
747         zval key;
748
749         HashTable *array = NULL;
750         HashPosition pos;
751
752         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|h", &provider, pimple_serviceprovider_ce, &array) == FAILURE) {
753                 return;
754         }
755
756         RETVAL_ZVAL(getThis(), 1, 0);
757
758         zend_call_method_with_1_params(&provider, Z_OBJCE_P(provider), NULL, "register", &retval, getThis());
759
760         if (retval) {
761                 zval_ptr_dtor(&retval);
762         }
763
764         if (!array) {
765                 return;
766         }
767
768         zend_hash_internal_pointer_reset_ex(array, &pos);
769
770         while(zend_hash_get_current_data_ex(array, (void **)&data, &pos) == SUCCESS) {
771                 zend_hash_get_current_key_zval_ex(array, &key, &pos);
772                 pimple_object_write_dimension(getThis(), &key, *data TSRMLS_CC);
773                 zend_hash_move_forward_ex(array, &pos);
774         }
775 }
776
777 PHP_METHOD(Pimple, __construct)
778 {
779         zval *values = NULL, **pData = NULL, offset;
780         HashPosition pos;
781         char *str_index = NULL;
782         zend_uint str_length;
783         ulong num_index;
784
785         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &values) == FAILURE || !values) {
786                 return;
787         }
788
789         zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos);
790         while (zend_hash_has_more_elements_ex(Z_ARRVAL_P(values), &pos) == SUCCESS) {
791                         zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&pData, &pos);
792                         zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_index, &str_length, &num_index, 0, &pos);
793                         INIT_ZVAL(offset);
794                         if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(values), &pos) == HASH_KEY_IS_LONG) {
795                                 ZVAL_LONG(&offset, num_index);
796                         } else {
797                                 ZVAL_STRINGL(&offset, str_index, (str_length - 1), 0);
798                         }
799                 pimple_object_write_dimension(getThis(), &offset, *pData TSRMLS_CC);
800                 zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos);
801         }
802 }
803
804 /*
805  * This is PHP code snippet handling extend()s calls :
806
807   $extended = function ($c) use ($callable, $factory) {
808       return $callable($factory($c), $c);
809   };
810
811  */
812 PHP_METHOD(PimpleClosure, invoker)
813 {
814         pimple_closure_object *pcobj = NULL;
815         zval *arg = NULL, *retval = NULL, *newretval = NULL;
816         zend_fcall_info fci        = {0};
817         zval **args[2];
818
819         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
820                 return;
821         }
822
823         pcobj = zend_object_store_get_object(getThis() TSRMLS_CC);
824
825         fci.function_name = pcobj->factory;
826         args[0] = &arg;
827         zend_fcall_info_argp(&fci TSRMLS_CC, 1, args);
828         fci.retval_ptr_ptr = &retval;
829         fci.size = sizeof(fci);
830
831         if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) {
832                 efree(fci.params);
833                 return; /* Should here return default zval */
834         }
835
836         efree(fci.params);
837         memset(&fci, 0, sizeof(fci));
838         fci.size = sizeof(fci);
839
840         fci.function_name = pcobj->callable;
841         args[0] = &retval;
842         args[1] = &arg;
843         zend_fcall_info_argp(&fci TSRMLS_CC, 2, args);
844         fci.retval_ptr_ptr = &newretval;
845
846         if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) {
847                 efree(fci.params);
848                 zval_ptr_dtor(&retval);
849                 return;
850         }
851
852         efree(fci.params);
853         zval_ptr_dtor(&retval);
854
855         RETVAL_ZVAL(newretval, 1 ,1);
856 }
857
858 PHP_MINIT_FUNCTION(pimple)
859 {
860         zend_class_entry tmp_pimple_ce, tmp_pimple_closure_ce, tmp_pimple_serviceprovider_iface_ce;
861         INIT_NS_CLASS_ENTRY(tmp_pimple_ce, PIMPLE_NS, "Container", pimple_ce_functions);
862         INIT_NS_CLASS_ENTRY(tmp_pimple_closure_ce, PIMPLE_NS, "ContainerClosure", NULL);
863         INIT_NS_CLASS_ENTRY(tmp_pimple_serviceprovider_iface_ce, PIMPLE_NS, "ServiceProviderInterface", pimple_serviceprovider_iface_ce_functions);
864
865         tmp_pimple_ce.create_object         = pimple_object_create;
866         tmp_pimple_closure_ce.create_object = pimple_closure_object_create;
867
868         pimple_ce = zend_register_internal_class(&tmp_pimple_ce TSRMLS_CC);
869         zend_class_implements(pimple_ce TSRMLS_CC, 1, zend_ce_arrayaccess);
870
871         pimple_closure_ce = zend_register_internal_class(&tmp_pimple_closure_ce TSRMLS_CC);
872         pimple_closure_ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
873
874         pimple_serviceprovider_ce = zend_register_internal_interface(&tmp_pimple_serviceprovider_iface_ce TSRMLS_CC);
875
876         memcpy(&pimple_closure_object_handlers, zend_get_std_object_handlers(), sizeof(*zend_get_std_object_handlers()));
877         pimple_object_handlers                     = std_object_handlers;
878         pimple_closure_object_handlers.get_closure = pimple_closure_get_closure;
879
880         pimple_closure_invoker_function.function_name     = "Pimple closure internal invoker";
881         pimple_closure_invoker_function.fn_flags         |= ZEND_ACC_CLOSURE;
882         pimple_closure_invoker_function.handler           = ZEND_MN(PimpleClosure_invoker);
883         pimple_closure_invoker_function.num_args          = 1;
884         pimple_closure_invoker_function.required_num_args = 1;
885         pimple_closure_invoker_function.scope             = pimple_closure_ce;
886         pimple_closure_invoker_function.type              = ZEND_INTERNAL_FUNCTION;
887         pimple_closure_invoker_function.module            = &pimple_module_entry;
888
889         return SUCCESS;
890 }
891
892 PHP_MINFO_FUNCTION(pimple)
893 {
894         php_info_print_table_start();
895         php_info_print_table_header(2, "SensioLabs Pimple C support", "enabled");
896         php_info_print_table_row(2, "Pimple supported version", PIMPLE_VERSION);
897         php_info_print_table_end();
898
899         php_info_print_box_start(0);
900         php_write((void *)ZEND_STRL("SensioLabs Pimple C support developed by Julien Pauli") TSRMLS_CC);
901         if (!sapi_module.phpinfo_as_text) {
902                 php_write((void *)ZEND_STRL(sensiolabs_logo) TSRMLS_CC);
903         }
904         php_info_print_box_end();
905 }
906
907 zend_module_entry pimple_module_entry = {
908         STANDARD_MODULE_HEADER,
909         "pimple",
910         NULL,
911         PHP_MINIT(pimple),
912         NULL,
913         NULL,
914         NULL,
915         PHP_MINFO(pimple),
916         PIMPLE_VERSION,
917         STANDARD_MODULE_PROPERTIES
918 };
919
920 #ifdef COMPILE_DL_PIMPLE
921 ZEND_GET_MODULE(pimple)
922 #endif