3 * This file is part of Pimple.
5 * Copyright (c) 2014 Fabien Potencier
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:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
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
32 #include "ext/standard/info.h"
33 #include "php_pimple.h"
34 #include "pimple_compat.h"
35 #include "zend_interfaces.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"
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;
50 #define FETCH_DIM_HANDLERS_VARS pimple_object *pimple_obj = NULL; \
52 pimple_obj = (pimple_object *)zend_object_store_get_object(object TSRMLS_CC); \
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 */ \
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; \
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; \
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; \
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; \
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; \
88 zend_call_function(&fci, &retval->fcc TSRMLS_CC); \
90 if (EG(exception)) { \
91 return EG(uninitialized_zval_ptr); \
95 ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0)
96 ZEND_ARG_ARRAY_INFO(0, value, 0)
99 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetset, 0, 0, 2)
100 ZEND_ARG_INFO(0, offset)
101 ZEND_ARG_INFO(0, value)
104 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetget, 0, 0, 1)
105 ZEND_ARG_INFO(0, offset)
108 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetexists, 0, 0, 1)
109 ZEND_ARG_INFO(0, offset)
112 ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetunset, 0, 0, 1)
113 ZEND_ARG_INFO(0, offset)
116 ZEND_BEGIN_ARG_INFO_EX(arginfo_factory, 0, 0, 1)
117 ZEND_ARG_INFO(0, callable)
120 ZEND_BEGIN_ARG_INFO_EX(arginfo_protect, 0, 0, 1)
121 ZEND_ARG_INFO(0, callable)
124 ZEND_BEGIN_ARG_INFO_EX(arginfo_raw, 0, 0, 1)
128 ZEND_BEGIN_ARG_INFO_EX(arginfo_extend, 0, 0, 2)
130 ZEND_ARG_INFO(0, callable)
133 ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 0)
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)
141 ZEND_BEGIN_ARG_INFO_EX(arginfo_serviceprovider_register, 0, 0, 1)
142 ZEND_ARG_OBJ_INFO(0, pimple, Pimple\\Container, 0)
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)
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)
161 static const zend_function_entry pimple_serviceprovider_iface_ce_functions[] = {
162 PHP_ABSTRACT_ME(ServiceProviderInterface, register, arginfo_serviceprovider_register)
166 static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC)
168 zend_object_std_dtor(&obj->zobj TSRMLS_CC);
170 zval_ptr_dtor(&obj->factory);
173 zval_ptr_dtor(&obj->callable);
178 static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC)
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);
187 static void pimple_free_bucket(pimple_bucket_value *bucket)
190 zval_ptr_dtor(&bucket->raw);
194 static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC)
196 zend_object_value retval;
197 pimple_closure_object *pimple_closure_obj = NULL;
199 pimple_closure_obj = ecalloc(1, sizeof(pimple_closure_object));
200 ZEND_OBJ_INIT(&pimple_closure_obj->zobj, ce);
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);
209 static zend_function *pimple_closure_get_constructor(zval *obj TSRMLS_DC)
211 zend_error(E_ERROR, "Pimple\\ContainerClosure is an internal class and cannot be instantiated");
216 static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC)
219 *ce_ptr = Z_OBJCE_P(obj);
220 *fptr_ptr = (zend_function *)&pimple_closure_invoker_function;
225 static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC)
227 zend_object_value retval;
228 pimple_object *pimple_obj = NULL;
229 zend_function *function = NULL;
231 pimple_obj = emalloc(sizeof(pimple_object));
232 ZEND_OBJ_INIT(&pimple_obj->zobj, ce);
234 PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS
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);
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);
246 static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC)
248 FETCH_DIM_HANDLERS_VARS
250 pimple_bucket_value pimple_value = {0}, *found_value = NULL;
253 pimple_zval_to_pimpleval(value, &pimple_value TSRMLS_CC);
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);
261 switch (Z_TYPE_P(offset)) {
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));
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);
279 if (Z_TYPE_P(offset) == IS_DOUBLE) {
280 index = (ulong)Z_DVAL_P(offset);
282 index = Z_LVAL_P(offset);
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);
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);
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);
301 pimple_free_bucket(&pimple_value);
302 zend_error(E_WARNING, "Unsupported offset type");
306 static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC)
308 FETCH_DIM_HANDLERS_VARS
310 switch (Z_TYPE_P(offset)) {
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);
319 if (Z_TYPE_P(offset) == IS_DOUBLE) {
320 index = (ulong)Z_DVAL_P(offset);
322 index = Z_LVAL_P(offset);
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);
329 zend_error(E_WARNING, "Unsupported offset type");
333 static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC)
335 FETCH_DIM_HANDLERS_VARS
337 pimple_bucket_value *retval = NULL;
339 switch (Z_TYPE_P(offset)) {
341 if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == SUCCESS) {
342 switch (check_empty) {
344 return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;) */
347 return zend_is_true(retval->value);
355 if (Z_TYPE_P(offset) == IS_DOUBLE) {
356 index = (ulong)Z_DVAL_P(offset);
358 index = Z_LVAL_P(offset);
360 if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == SUCCESS) {
361 switch (check_empty) {
363 return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;)*/
366 return zend_is_true(retval->value);
372 zend_error(E_WARNING, "Unsupported offset type");
377 static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC)
379 FETCH_DIM_HANDLERS_VARS
381 pimple_bucket_value *retval = NULL;
382 zend_fcall_info fci = {0};
383 zval *retval_ptr_ptr = NULL;
385 switch (Z_TYPE_P(offset)) {
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);
395 if (Z_TYPE_P(offset) == IS_DOUBLE) {
396 index = (ulong)Z_DVAL_P(offset);
398 index = Z_LVAL_P(offset);
400 if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == FAILURE) {
401 return EG(uninitialized_zval_ptr);
404 case IS_NULL: /* $p[][3] = 'foo' first dim access */
405 return EG(uninitialized_zval_ptr);
408 zend_error(E_WARNING, "Unsupported offset type");
409 return EG(uninitialized_zval_ptr);
412 if(retval->type == PIMPLE_IS_PARAM) {
413 return retval->value;
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;
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 */
424 Z_DELREF_P(retval_ptr_ptr); /* fetch dim addr will increment refcount */
425 return retval_ptr_ptr;
428 if (retval->initialized == 1) {
429 /* Service has already been called, return its cached value */
430 return retval->value;
433 ALLOC_INIT_ZVAL(retval->raw);
434 MAKE_COPY_ZVAL(&retval->value, retval->raw);
438 retval->initialized = 1;
439 zval_ptr_dtor(&retval->value);
440 retval->value = retval_ptr_ptr;
442 return retval->value;
445 static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC)
447 if (Z_TYPE_P(_zval) != IS_OBJECT) {
451 if (_pimple_bucket_value->fcc.called_scope) {
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;
463 static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC)
465 _pimple_bucket_value->value = _zval;
467 if (Z_TYPE_P(_zval) != IS_OBJECT) {
468 return PIMPLE_IS_PARAM;
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);
476 return PIMPLE_IS_SERVICE;
479 static void pimple_bucket_dtor(pimple_bucket_value *bucket)
481 zval_ptr_dtor(&bucket->value);
482 pimple_free_bucket(bucket);
485 PHP_METHOD(Pimple, protect)
487 zval *protected = NULL;
488 pimple_object *pobj = NULL;
489 pimple_bucket_value bucket = {0};
491 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &protected) == FAILURE) {
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);
501 pimple_zval_to_pimpleval(protected, &bucket TSRMLS_CC);
502 pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
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);
508 pimple_free_bucket(&bucket);
513 PHP_METHOD(Pimple, raw)
516 pimple_object *pobj = NULL;
517 pimple_bucket_value *value = NULL;
520 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
524 pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
526 switch (Z_TYPE_P(offset)) {
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));
536 if (Z_TYPE_P(offset) == IS_DOUBLE) {
537 index = (ulong)Z_DVAL_P(offset);
539 index = Z_LVAL_P(offset);
541 if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) {
547 zend_error(E_WARNING, "Unsupported offset type");
551 RETVAL_ZVAL(value->raw, 1, 0);
553 RETVAL_ZVAL(value->value, 1, 0);
557 PHP_METHOD(Pimple, extend)
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;
565 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &callable) == FAILURE) {
569 pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
571 switch (Z_TYPE_P(offset)) {
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));
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));
585 if (Z_TYPE_P(offset) == IS_DOUBLE) {
586 index = (ulong)Z_DVAL_P(offset);
588 index = Z_LVAL_P(offset);
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);
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);
601 zend_error(E_WARNING, "Unsupported offset type");
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);
609 pimple_free_bucket(&bucket);
611 ALLOC_INIT_ZVAL(pimple_closure_obj);
612 object_init_ex(pimple_closure_obj, pimple_closure_ce);
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);
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);
627 pimple_object_write_dimension(getThis(), offset, pimple_closure_obj TSRMLS_CC);
629 RETVAL_ZVAL(pimple_closure_obj, 1, 1);
632 PHP_METHOD(Pimple, keys)
635 pimple_object *pobj = NULL;
638 char *str_index = NULL;
642 if (zend_parse_parameters_none() == FAILURE) {
646 pobj = zend_object_store_get_object(getThis() TSRMLS_CC);
647 array_init_size(return_value, zend_hash_num_elements(&pobj->values));
649 zend_hash_internal_pointer_reset_ex(&pobj->values, &pos);
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);
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);
663 zend_hash_move_forward_ex(&pobj->values, &pos);
667 PHP_METHOD(Pimple, factory)
669 zval *factory = NULL;
670 pimple_object *pobj = NULL;
671 pimple_bucket_value bucket = {0};
673 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &factory) == FAILURE) {
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);
683 pimple_zval_to_pimpleval(factory, &bucket TSRMLS_CC);
684 pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC);
686 if (zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) {
688 RETURN_ZVAL(factory, 1 , 0);
690 pimple_free_bucket(&bucket);
696 PHP_METHOD(Pimple, offsetSet)
698 zval *offset = NULL, *value = NULL;
700 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &value) == FAILURE) {
704 pimple_object_write_dimension(getThis(), offset, value TSRMLS_CC);
707 PHP_METHOD(Pimple, offsetGet)
709 zval *offset = NULL, *retval = NULL;
711 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
715 retval = pimple_object_read_dimension(getThis(), offset, 0 TSRMLS_CC);
717 RETVAL_ZVAL(retval, 1, 0);
720 PHP_METHOD(Pimple, offsetUnset)
724 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
728 pimple_object_unset_dimension(getThis(), offset TSRMLS_CC);
731 PHP_METHOD(Pimple, offsetExists)
735 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) {
739 RETVAL_BOOL(pimple_object_has_dimension(getThis(), offset, 1 TSRMLS_CC));
742 PHP_METHOD(Pimple, register)
749 HashTable *array = NULL;
752 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|h", &provider, pimple_serviceprovider_ce, &array) == FAILURE) {
756 RETVAL_ZVAL(getThis(), 1, 0);
758 zend_call_method_with_1_params(&provider, Z_OBJCE_P(provider), NULL, "register", &retval, getThis());
761 zval_ptr_dtor(&retval);
768 zend_hash_internal_pointer_reset_ex(array, &pos);
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);
777 PHP_METHOD(Pimple, __construct)
779 zval *values = NULL, **pData = NULL, offset;
781 char *str_index = NULL;
782 zend_uint str_length;
785 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &values) == FAILURE || !values) {
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);
794 if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(values), &pos) == HASH_KEY_IS_LONG) {
795 ZVAL_LONG(&offset, num_index);
797 ZVAL_STRINGL(&offset, str_index, (str_length - 1), 0);
799 pimple_object_write_dimension(getThis(), &offset, *pData TSRMLS_CC);
800 zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos);
805 * This is PHP code snippet handling extend()s calls :
807 $extended = function ($c) use ($callable, $factory) {
808 return $callable($factory($c), $c);
812 PHP_METHOD(PimpleClosure, invoker)
814 pimple_closure_object *pcobj = NULL;
815 zval *arg = NULL, *retval = NULL, *newretval = NULL;
816 zend_fcall_info fci = {0};
819 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
823 pcobj = zend_object_store_get_object(getThis() TSRMLS_CC);
825 fci.function_name = pcobj->factory;
827 zend_fcall_info_argp(&fci TSRMLS_CC, 1, args);
828 fci.retval_ptr_ptr = &retval;
829 fci.size = sizeof(fci);
831 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) {
833 return; /* Should here return default zval */
837 memset(&fci, 0, sizeof(fci));
838 fci.size = sizeof(fci);
840 fci.function_name = pcobj->callable;
843 zend_fcall_info_argp(&fci TSRMLS_CC, 2, args);
844 fci.retval_ptr_ptr = &newretval;
846 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) {
848 zval_ptr_dtor(&retval);
853 zval_ptr_dtor(&retval);
855 RETVAL_ZVAL(newretval, 1 ,1);
858 PHP_MINIT_FUNCTION(pimple)
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);
865 tmp_pimple_ce.create_object = pimple_object_create;
866 tmp_pimple_closure_ce.create_object = pimple_closure_object_create;
868 pimple_ce = zend_register_internal_class(&tmp_pimple_ce TSRMLS_CC);
869 zend_class_implements(pimple_ce TSRMLS_CC, 1, zend_ce_arrayaccess);
871 pimple_closure_ce = zend_register_internal_class(&tmp_pimple_closure_ce TSRMLS_CC);
872 pimple_closure_ce->ce_flags |= ZEND_ACC_FINAL_CLASS;
874 pimple_serviceprovider_ce = zend_register_internal_interface(&tmp_pimple_serviceprovider_iface_ce TSRMLS_CC);
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;
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;
892 PHP_MINFO_FUNCTION(pimple)
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();
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);
904 php_info_print_box_end();
907 zend_module_entry pimple_module_entry = {
908 STANDARD_MODULE_HEADER,
917 STANDARD_MODULE_PROPERTIES
920 #ifdef COMPILE_DL_PIMPLE
921 ZEND_GET_MODULE(pimple)