// // php-armadillo/php_arma.hh // // @Author CismonX // #ifndef PHP_ARMA_HH #define PHP_ARMA_HH #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #define PHP_ARMA_VERSION "0.0.1" #if PHP_VERSION_ID >= 70400 #define ZEND_ACC_CTOR 0 #endif /// Helper macros for method entry. #define PHP_ARMA_START_ME(cls, ...) \ zend_function_entry cls<__VA_ARGS__>::me[] = { #define PHP_ARMA_END_ME() \ PHP_FE_END \ } #define PHP_ARMA_ME(func, flags) \ ZEND_FENTRY(func, zif_##func, nullptr, flags) #define PHP_ARMA_METHOD(cls, func, ...) \ void ZEND_FASTCALL cls<__VA_ARGS__>::zif_##func(INTERNAL_FUNCTION_PARAMETERS) /// Helper macros for class entry and object handlers #define PHP_ARMA_CE_HANDLRES_DECLARE() \ static inline zend_class_entry *ce; \ static inline zend_object_handlers handlers #define PHP_ARMA_CE(cls, ...) \ cls<__VA_ARGS__>::ce #define PHP_ARMA_HANDLERS(cls, ...) \ &cls<__VA_ARGS__>::handlers #define Z_OBJNAME_P(zval_p) \ ZSTR_VAL(Z_OBJCE_P(zval_p)->name) #define PHP_ARMA_COMMON_DECLARE() \ struct php_name; \ static zend_function_entry me[] #define PHP_ARMA_NAME_DECLARE(cls, name, ...) \ template <> \ struct cls<__VA_ARGS__>::php_name \ { \ static constexpr const char val[] = name; \ } /// Helper macros for parameter parsing. #ifndef Z_PARAM_DOUBLE_DEREF #define Z_PARAM_DOUBLE_DEREF(dest) \ Z_PARAM_DOUBLE_EX2(dest, _dummy, 0, 1, 0) #endif // !Z_PARAM_DOUBLE_DEREF #ifdef PHP_ARMA_OPERATORS /// Helper macros for handling operator overloading. #define PHP_ARMA_OPERATOR_BEGIN(parent_ce) \ { \ if (instanceof_function(ce, parent_ce)) { #define PHP_ARMA_OPERATOR_END() \ return false; \ } \ } #endif // PHP_ARMA_OPERATORS namespace php_arma { extern zend_module_entry module_entry; /// Helpers for compile-time string concatenation for better module startup performance. namespace str { template using seq = std::integer_sequence; template using seq_impl = std::make_integer_sequence; constexpr auto get_len(const char *str) { auto i = 0; for (; *str != 0; ++i, ++str); return i; } template struct concat_impl; template struct concat_impl, Str2, seq> { static constexpr const char value[] { Str1[Len1]..., Str2[Len2]..., 0 }; }; template constexpr auto concat { concat_impl, Str2, seq_impl>::value }; constexpr const char namespace_prefix[] = "Arma\\"; constexpr const char internal_prefix[] = "Internal\\"; template constexpr auto with_arma_prefix { concat }; template constexpr auto with_internal_prefix { concat }; } /// Helper functions for initializing class entry and object handlers. template zend_always_inline zend_class_entry *ce_init(const zend_function_entry *methods, F init_func) { zend_class_entry tmp_ce; constexpr auto name = str::with_arma_prefix; constexpr auto name_len = str::get_len(name); INIT_CLASS_ENTRY_EX(tmp_ce, name, name_len, methods); return init_func(&tmp_ce); } template zend_always_inline zend_class_entry *ce_init(const zend_function_entry *methods, F init_func, Ts... interfaces) { auto ce = ce_init(methods, init_func); zend_class_implements(ce, sizeof...(Ts), interfaces...); return ce; } template zend_always_inline zend_class_entry *interface_register() { // Although methods are declared in interfaces as you see in the stubs, // nothing is declared in the internal interface implementation. return ce_init>(nullptr, zend_register_internal_class); } template zend_always_inline zend_class_entry *interface_register(Ts... parents) { return ce_init>(nullptr, zend_register_internal_class, parents...); } template zend_always_inline zend_class_entry *class_register(zend_class_entry *parent, const zend_function_entry *methods) { auto init_func = std::bind(zend_register_internal_class_ex, std::placeholders::_1, parent); return ce_init(methods, init_func); } template zend_always_inline zend_class_entry *abstract_class_register() { auto ce = ce_init(nullptr, zend_register_internal_class); ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; return ce; } template zend_always_inline zend_class_entry *abstract_class_register(Ts... interfaces) { auto ce = ce_init(nullptr, zend_register_internal_class, interfaces...); ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; return ce; } zend_always_inline void object_handlers_init(zend_object_handlers *handlers) { memcpy(handlers, &std_object_handlers, sizeof(zend_object_handlers)); } template zend_always_inline zend_object *to_zend_object(T *obj) { return reinterpret_cast(obj + 1); } template zend_always_inline T *to_native_object(zend_object *zobj) { return reinterpret_cast(zobj) - 1; } /// Helper functions for handling objects. template zend_always_inline zend_object *object_create(zend_class_entry *ce, F init) { auto obj = reinterpret_cast(emalloc(sizeof(T) + sizeof(zend_object) - sizeof(zval))); auto zobj = to_zend_object(obj); zobj->handlers = init(obj); zend_object_std_init(zobj, ce); return zobj; } zend_always_inline zend_object *object_create(zend_class_entry *ce, const zend_object_handlers *handlers) { auto zobj = reinterpret_cast(emalloc( sizeof(zend_object) + zend_object_properties_size(ce))); zobj->handlers = handlers; zend_object_std_init(zobj, ce); object_properties_init(zobj, ce); return zobj; } zend_always_inline void object_set_property(zend_object *obj, size_t idx, zval *val) { zval_ptr_dtor(OBJ_PROP_NUM(obj, idx)); ZVAL_COPY(OBJ_PROP_NUM(obj, idx), val); } zend_always_inline zend_object *object_non_constructible(zend_class_entry *ce) { zend_throw_exception_ex(zend_ce_error, -3, "Class %s is not explicitly constructible.", ZSTR_VAL(ce->name)); return zend_objects_new(ce); } /// Helper functions for handling scalar types. template struct false_type : std::false_type {}; template zend_always_inline constexpr void bad_scalar_type() { static_assert(false_type::value, "Type should be zend_long, double or cx_double"); } template zend_always_inline bool zval_is_scalar(zval *zv) { bad_scalar_type(); } template <> zend_always_inline bool zval_is_scalar(zval *zv) { return Z_TYPE_P(zv) == IS_DOUBLE; } template <> zend_always_inline bool zval_is_scalar(zval *zv) { return Z_TYPE_P(zv) == IS_LONG; } template zend_always_inline const char *scalar_type_name() { bad_scalar_type(); } template <> zend_always_inline const char *scalar_type_name() { return zend_get_type_by_const(IS_DOUBLE); } template <> zend_always_inline const char *scalar_type_name() { return zend_get_type_by_const(IS_LONG); } template zend_always_inline T zval_get_scalar(zval *zv) { bad_scalar_type(); } template <> zend_always_inline double zval_get_scalar(zval *zv) { return Z_DVAL_P(zv); } template <> zend_always_inline zend_long zval_get_scalar(zval *zv) { return Z_LVAL_P(zv); } zend_always_inline void zval_set_scalar(zval *zv, double val) { ZVAL_DOUBLE(zv, val); } zend_always_inline void zval_set_scalar(zval *zv, zend_long val) { ZVAL_LONG(zv, val); } zend_always_inline const char *zval_get_type_name(zval *zv) { if (Z_TYPE_P(zv) == IS_OBJECT) { return ZSTR_VAL(Z_OBJCE_P(zv)->name); } else { return zend_get_type_by_const(Z_TYPE_P(zv)); } } /// Helper functions for throwing exceptions when errors occur. zend_always_inline void ex_bad_type(const char *expected, const char *got) { zend_throw_exception_ex(zend_ce_type_error, -1, "Bad type, expected %s, %s given.", expected, got); } template zend_always_inline bool zval_check_scalar(zval *zv) { if (UNEXPECTED(!zval_is_scalar(zv))) { ex_bad_type(scalar_type_name(), zval_get_type_name(zv)); return false; } return true; } } #endif // !PHP_ARMA_HH