+#include "Zend/zend_interfaces.h"
+#include "ext/spl/spl_iterators.h"
+
+zend_long psi_zval_count(zval *zvalue)
+{
+ /* mimic PHP count() */
+ zend_long count;
+ zval retval;
+
+ switch (Z_TYPE_P(zvalue)) {
+ default:
+ count = 1;
+ break;
+ case IS_NULL:
+ count = 0;
+ break;
+ case IS_ARRAY:
+ count = zend_array_count(Z_ARRVAL_P(zvalue));
+ break;
+ case IS_OBJECT:
+ count = 1;
+ if (Z_OBJ_HT_P(zvalue)->count_elements) {
+ if (SUCCESS == Z_OBJ_HT_P(zvalue)->count_elements(zvalue, &count)) {
+ break;
+ }
+ }
+
+ if (instanceof_function(Z_OBJCE_P(zvalue), spl_ce_Countable)) {
+ zend_call_method_with_0_params(zvalue, NULL, NULL, "count", &retval);
+ if (Z_TYPE(retval) != IS_UNDEF) {
+ count = zval_get_long(&retval);
+ zval_ptr_dtor(&retval);
+ }
+ }
+ break;
+ }
+
+ return count;
+}
+
+int psi_internal_type(struct psi_impl_type *type)
+{
+ switch (type->type) {
+ case PSI_T_BOOL:
+ return _IS_BOOL;
+ case PSI_T_INT:
+ return IS_LONG;
+ case PSI_T_FLOAT:
+ case PSI_T_DOUBLE:
+ return IS_DOUBLE;
+ case PSI_T_STRING:
+ return IS_STRING;
+ case PSI_T_ARRAY:
+ return IS_ARRAY;
+ default:
+ return 0;
+ }
+}
+
+zend_internal_arg_info *psi_internal_arginfo(struct psi_impl *impl)
+{
+ size_t i = 0, argc = psi_plist_count(impl->func->args);
+ zend_internal_arg_info *aip;
+ zend_internal_function_info *fi;
+ struct psi_impl_arg *iarg;
+
+ aip = pecalloc(argc + 1 + !!impl->func->vararg, sizeof(*aip), 1);
+
+ fi = (zend_internal_function_info *) &aip[0];
+#ifdef ZEND_TYPE_ENCODE
+ fi->type = ZEND_TYPE_ENCODE(psi_internal_type(impl->func->return_type), 1);
+#else
+ fi->allow_null = 1;
+ fi->type_hint = psi_internal_type(impl->func->return_type);
+#endif
+ fi->required_num_args = psi_impl_num_min_args(impl);
+ fi->return_reference = impl->func->return_reference;
+
+ if (impl->func->vararg) {
+ struct psi_impl_arg *vararg = impl->func->vararg;
+ zend_internal_arg_info *ai = &aip[argc];
+
+ ai->name = vararg->var->name->val;
+#ifdef ZEND_TYPE_ENCODE
+ ai->type = ZEND_TYPE_ENCODE(psi_internal_type(vararg->type), 1);
+#else
+ ai->allow_null = 1;
+ ai->type_hint = psi_internal_type(vararg->type);
+#endif
+ if (vararg->var->reference) {
+ ai->pass_by_reference = 1;
+ }
+ ai->is_variadic = 1;
+ }
+
+ while (psi_plist_get(impl->func->args, i++, &iarg)) {
+ zend_internal_arg_info *ai = &aip[i];
+
+ ai->name = iarg->var->name->val;
+#ifdef ZEND_TYPE_ENCODE
+ ai->type = ZEND_TYPE_ENCODE(psi_internal_type(iarg->type), 1);
+#else
+ ai->allow_null = 1;
+ ai->type_hint = psi_internal_type(iarg->type);
+#endif
+ if (iarg->var->reference) {
+ ai->pass_by_reference = 1;
+ }
+ }
+
+ return aip;
+}
+
+/*
+ * return void(dvar)
+ */
+void psi_set_void(zval *return_value, struct psi_set_exp *set, impl_val *ret_val, struct psi_call_frame *frame)