/** * php-asio/common.hpp * * @author CismonX */ #pragma once #include "p3.hpp" #include #include // Compatible with PHP 7.3 #if PHP_VERSION_ID < 70300 #define GC_ADDREF(p) ++GC_REFCOUNT(p) #define GC_DELREF(p) --GC_REFCOUNT(p) #endif #if PHP_VERSION_ID < 70115 || (PHP_VERSION_ID > 70200 && PHP_VERSION_ID < 70203) #if PHP_VERSION_ID < 70100 #define _zend_wrong_parameters_count_error(throw, ...) zend_wrong_paramers_count_error(__VA_ARGS__) #elif PHP_VERSION_ID < 70200 #define _zend_wrong_parameters_count_error(throw, ...) zend_wrong_parameters_count_error(__VA_ARGS__) #else #define _zend_wrong_parameters_count_error(...) zend_wrong_parameters_count_error(__VA_ARGS__) #endif // See https://externals.io/message/101364 for details. #undef ZEND_PARSE_PARAMETERS_START_EX #define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \ const int _flags = (flags); \ int _min_num_args = (min_num_args); \ int _max_num_args = (max_num_args); \ int _num_args = EX_NUM_ARGS(); \ int _i; \ zval *_real_arg, *_arg = NULL; \ zend_expected_type _expected_type = Z_EXPECTED_LONG; \ char *_error = NULL; \ zend_bool _dummy; \ zend_bool _optional = 0; \ int error_code = ZPP_ERROR_OK; \ ((void)_i); \ ((void)_real_arg); \ ((void)_arg); \ ((void)_expected_type); \ ((void)_error); \ ((void)_dummy); \ ((void)_optional); \ do { \ if (UNEXPECTED(_num_args < _min_num_args) || \ (UNEXPECTED(_num_args > _max_num_args) && \ EXPECTED(_max_num_args >= 0))) { \ if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \ _zend_wrong_parameters_count_error(_flags & ZEND_PARSE_PARAMS_THROW, \ _num_args, _min_num_args, _max_num_args); \ } \ error_code = ZPP_ERROR_FAILURE; \ break; \ } \ _i = 0; \ _real_arg = ZEND_CALL_ARG(execute_data, 0); #endif #define PHP_ASIO_OBJ_ALLOC(obj, type, arg) \ auto obj = p3::alloc_object(type::class_entry, [this](type* ptr) { \ new(ptr) type(arg); \ }) #define PHP_ASIO_OBJ_DTOR(obj) GC_DELREF(p3::to_zend_object(obj)) #define ZVAL_ALLOC(name) name = static_cast(emalloc(sizeof(zval))) #define ZVAL_PTR_INIT(name) auto ZVAL_ALLOC(name) #define ZVAL_INIT(name) zval name = {{ 0 }} #define RETVAL_EC(ec) RETVAL_LONG(static_cast((ec).value())) #define PHP_ASIO_ERROR(type, msg) php_error_docref(nullptr, type, msg) #define PHP_ASIO_CE_DECLARE() \ static zend_class_entry* class_entry; \ static zend_object_handlers handlers #define PHP_ASIO_CE_DEFINE(type) \ zend_class_entry* type::class_entry; \ zend_object_handlers type::handlers // Handlers with one argument is treated as ones with two arguments. #define NOARG int #define ASYNC_HANDLER_SINGLE_ARG \ std::function(boost::bind( \ &future::resolve, NOARG>, \ future, boost::asio::placeholders::error, 0)) #define ASYNC_HANDLER_DOUBLE_ARG(obj_type) \ std::function(boost::bind( \ &future::resolve, obj_type>, \ future, boost::asio::placeholders::error, _2)) // If you don't need coroutines, you can turn it off for better performance. #ifdef ENABLE_COROUTINE #define CORO_REGISTER(value) future::coroutine(value) #define FUTURE_INIT() \ zend_object* obj; \ auto future = future::add(this, obj); #define FUTURE_RETURN() RETVAL_OBJ(obj) #define INIT_RETVAL() \ ZVAL_PTR_INIT(retval); \ ZVAL_NULL(retval) #define PASS_RETVAL retval #else #define CORO_REGISTER(value) #define FUTURE_INIT() auto future = future::add(this) #define FUTURE_RETURN() #define INIT_RETVAL() ZVAL_INIT(retval) #define PASS_RETVAL &retval #endif // ENABLE_COROUTINE #define CORO_RETURN_NULL() \ ZVAL_PTR_INIT(retval); \ ZVAL_NULL(retval); \ return retval #define CORO_RETURN(type, value) \ ZVAL_PTR_INIT(retval); \ type(retval, value); \ return retval // If you don't need multi-threading support for I/O objects, you can disable Strand for better performance. #if defined(ENABLE_STRAND) && !defined(ZTS) #undef ENABLE_STRAND #endif #ifdef ENABLE_STRAND #define STRAND_UNWRAP() \ callback = future->handle_strand(callback); \ if (future->get_strand()) \ ZVAL_COPY_VALUE(cb, callback); \ else #define STRAND_RESOLVE(arg) future->get_strand() ? future->get_strand()->wrap(arg) : arg #else #define STRAND_UNWRAP() #define STRAND_RESOLVE(arg) arg #endif // ENABLE_STRAND #define PHP_ASIO_INVOKE_CALLBACK_START(argc) \ const auto _argc = argc; \ if (callback && zend_is_callable(callback, 0, nullptr)) { \ zval arguments[_argc] = {{{ 0 }}}; \ ZVAL_OBJ(&arguments[0], p3::to_zend_object(this)); #define PHP_ASIO_INVOKE_CALLBACK() \ ZVAL_LONG(&arguments[_argc - 2], static_cast(error.value())); \ if (argument) \ ZVAL_COPY(&arguments[_argc - 1], argument); \ else \ ZVAL_NULL(&arguments[_argc - 1]); \ INIT_RETVAL(); \ call_user_function(CG(function_table), nullptr, callback, PASS_RETVAL, _argc, arguments) #define PHP_ASIO_INVOKE_CALLBACK_END() \ CORO_REGISTER(retval); \ zval_ptr_dtor(callback); \ efree(callback); \ } \ if (argument) { \ zval_ptr_dtor(argument); \ efree(argument); \ } #define PHP_ASIO_INC_HANDLER_COUNT() \ if (handler_count_inc() == 1) \ GC_ADDREF(p3::to_zend_object(this)) #define PHP_ASIO_DEC_HANDLER_COUNT() \ if (handler_count_dec() == 0) \ PHP_ASIO_OBJ_DTOR(this) // To ensure the callback and the extra arg is still alive when async operation resolves, // We shall allocate new memory on the heap. #define PHP_ASIO_FUTURE_INIT() \ PHP_ASIO_INC_HANDLER_COUNT(); \ FUTURE_INIT(); \ zval* cb = nullptr; \ if (callback) { \ ZVAL_ALLOC(cb); \ STRAND_UNWRAP() \ ZVAL_COPY(cb, callback); \ } \ zval* args = nullptr; \ if (argument) { \ ZVAL_ALLOC(args); \ ZVAL_COPY(args, argument); \ } using boost::asio::ip::tcp; using boost::asio::ip::udp; using unix = boost::asio::local::stream_protocol; using udg = boost::asio::local::datagram_protocol;