master
CismonX 2018-04-01 16:35:05 +08:00
commit 9f3a47020d
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
66 changed files with 5272 additions and 0 deletions

26
.travis.yml Normal file
View File

@ -0,0 +1,26 @@
sudo: required
dist: trusty
group: edge
language: php
php:
- 7.0
- 7.1
- 7.2
- nightly
before_install:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
- sudo apt-get install -qq g++-6
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 90
- sudo apt-get install libboost1.55-all-dev
script:
- phpize
- ./configure --enable-asio-strand
- make
after_success:
- make test

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017-2018 CismonX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

28
README.md Normal file
View File

@ -0,0 +1,28 @@
# php-asio
[![Travis-CI](https://travis-ci.org/CismonX/php-asio.svg?branch=master)](https://travis-ci.org/CismonX/php-asio)
[![MIT license](https://img.shields.io/badge/licence-MIT-blue.svg)](https://opensource.org/licenses/MIT)
## 1. About
This extension is a PHP wrapper for the Boost.Asio library, which provides a high-performance event-driven model for network I/O.
Now php-asio is **in development**, do not use it in production.
Bug reports and pull requests are welcome.
## 2. Documentation
See the [Wiki](https://github.com/CismonX/php-asio/wiki) page for documentation.
Signatures of classes and functions can be found within [stubs/](stubs/) directory, with PHPDoc.
There are also some test cases in the [tests/](tests/) directory.
## 3. TODO list
* Fix memory leak. (Priority: **high**)
* Multi-threading support. (Priority: **medium**)
* Add support for serial ports. (Priority: **low**)
* Add socket `onReadable()` `onWritable()` `onError()` methods (with Boost version 1.66 and above). (Priority: **low**)
* Add support for Windows. (Priority: **low**)

43
config.m4 Normal file
View File

@ -0,0 +1,43 @@
PHP_ARG_ENABLE(asio, for asio support,
[ --enable-asio Enable asio support ])
PHP_ARG_ENABLE(asio-coroutine, for coroutine support,
[ --disable-asio-coroutine Disable coroutine support ], yes, no)
PHP_ARG_ENABLE(asio-strand, for strand support,
[ --enable-asio-strand Enable strand support ], no, no)
PHP_ARG_ENABLE(asio-null-buffers, for null buffer support,
[ --enable-asio-null-buffers Enable null buffers ], no, no)
if test "$PHP_ASIO" != "no"; then
PHP_REQUIRE_CXX()
PHP_ASIO_SRC="src/php_asio.cpp \
src/service.cpp \
src/wrapped_handler.cpp \
src/future.cpp \
src/strand.cpp \
src/timer.cpp \
src/signal.cpp \
src/resolver.cpp \
src/socket.cpp \
src/acceptor.cpp \
src/stream_descriptor.cpp"
PHP_NEW_EXTENSION(asio, $PHP_ASIO_SRC, $ext_shared, cli, -std=c++14, yes)
if test "$PHP_ASIO_COROUTINE" != "no"; then
AC_DEFINE(ENABLE_COROUTINE, 1, [ ])
fi
if test "$PHP_ASIO_STRAND" != "no"; then
AC_DEFINE(ENABLE_STRAND, 1, [ ])
fi
if test "$PHP_ASIO_NULL_BUFFERS" != "no"; then
AC_DEFINE(ENABLE_NULL_BUFFERS, 1, [ ])
fi
PHP_ADD_LIBRARY(boost_system, 1, ASIO_SHARED_LIBADD)
PHP_ADD_LIBRARY(boost_filesystem, 1, ASIO_SHARED_LIBADD)
PHP_SUBST(ASIO_SHARED_LIBADD)
fi

171
src/acceptor.cpp Normal file
View File

@ -0,0 +1,171 @@
/**
* php-asio/acceptor.cpp
*
* @author CismonX<admin@cismon.net>
*/
#include "acceptor.hpp"
#include "future.hpp"
#include "io.hpp"
namespace asio
{
template <typename Protocol>
zval* acceptor<Protocol>::handler(const boost::system::error_code& error,
socket<Protocol>* const socket, zval* callback, zval* argument)
{
PHP_ASIO_INVOKE_CALLBACK_START(4)
ZVAL_OBJ(&arguments[1], p3::to_zend_object(socket));
PHP_ASIO_INVOKE_CALLBACK();
PHP_ASIO_INVOKE_CALLBACK_END();
CORO_RETURN(ZVAL_OBJ, p3::to_zend_object(socket));
}
/* {{{ proto int TcpAcceptor::accept(bool inet6);
* Open socket acceptor. */
template <>
P3_METHOD(tcp_acceptor, open)
{
zend_bool inet6;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_BOOL(inet6)
ZEND_PARSE_PARAMETERS_END();
boost::system::error_code ec;
acceptor_.open(inet6 ? tcp::v6() : tcp::v4(), ec);
RETVAL_EC(ec)
}
/* }}} */
/* {{{ proto int UnixAcceptor::accept(void);
* Open socket acceptor. */
template <>
P3_METHOD(unix_acceptor, open)
{
boost::system::error_code ec;
acceptor_.open(unix(), ec);
RETVAL_EC(ec)
}
/* }}} */
/* {{{ proto int TcpAcceptor::assign(bool inet6, int|resource fd);
* Assign an existing native socket to the acceptor. */
template <>
P3_METHOD(tcp_acceptor, assign)
{
PHP_ASIO_INET_ASSIGN(acceptor_, tcp);
}
/* }}} */
/* {{{ proto int UnixAcceptor::assign(int|resource fd);
* Assign an existing native socket to the acceptor. */
template <>
P3_METHOD(unix_acceptor, assign)
{
PHP_ASIO_LOCAL_ASSIGN(acceptor_, unix);
}
/* }}} */
/* {{{ proto int TcpAcceptor::bind(string address, int port);
* Bind the acceptor to the specified local endpoint. */
template <>
P3_METHOD(tcp_acceptor, bind)
{
zend_string* address;
zend_long port_num;
ZEND_PARSE_PARAMETERS_START(2, 2);
Z_PARAM_STR(address)
Z_PARAM_LONG(port_num)
ZEND_PARSE_PARAMETERS_END();
boost::system::error_code ec;
acceptor_.bind({ boost::asio::ip::address::from_string(ZSTR_VAL(address)),
static_cast<unsigned short>(port_num) }, ec);
RETVAL_EC(ec);
}
/* }}} */
/* {{{ proto int TcpAcceptor::bind(string path);
* Bind the acceptor to the specified local endpoint. */
template <>
P3_METHOD(unix_acceptor, bind)
{
zend_string* socket_path;
ZEND_PARSE_PARAMETERS_START(1, 1);
Z_PARAM_STR(socket_path)
ZEND_PARSE_PARAMETERS_END();
namespace fs = boost::filesystem;
if (fs::status(ZSTR_VAL(socket_path)).type() == fs::socket_file)
fs::remove(ZSTR_VAL(socket_path));
boost::system::error_code ec;
acceptor_.bind({ ZSTR_VAL(socket_path) }, ec);
RETVAL_EC(ec);
}
/* }}} */
/* {{{ proto int Acceptor::listen([int backlog]);
* Put the acceptor into the state where it may accept new connections. */
template <typename Protocol>
P3_METHOD(acceptor<Protocol>, listen)
{
zend_long backlog = 0;
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(backlog)
ZEND_PARSE_PARAMETERS_END();
boost::system::error_code ec;
acceptor_.listen(backlog ? static_cast<int>(backlog) :
Protocol::socket::max_connections, ec);
RETVAL_EC(ec);
}
/* }}} */
/* {{{ proto int Acceptor::accept([callable callback], [mixed argument]);
* Asynchronously accept a new connection into a socket. */
template <typename Protocol>
P3_METHOD(acceptor<Protocol>, accept)
{
zval* callback = nullptr;
zval* argument = nullptr;
ZEND_PARSE_PARAMETERS_START(0, 2)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(callback)
Z_PARAM_ZVAL(argument)
ZEND_PARSE_PARAMETERS_END();
PHP_ASIO_OBJ_ALLOC(accepted_socket, socket<Protocol>, io_service_);
PHP_ASIO_FUTURE_INIT();
auto asio_socket = p3::to_object<socket<Protocol>>(accepted_socket);
future->template on_resolve<NOARG>(boost::bind(
&acceptor::handler, this, _1, asio_socket, cb, args));
acceptor_.async_accept(asio_socket->get_socket(), STRAND_RESOLVE(ASYNC_HANDLER_SINGLE_ARG));
FUTURE_RETURN();
}
/* }}} */
/* {{{ proto int Acceptor::cancel(void);
* Cancel all asynchronous operations on this acceptor. */
template <typename Protocol>
P3_METHOD(acceptor<Protocol>, cancel)
{
boost::system::error_code ec;
RETVAL_EC(acceptor_.cancel(ec));
}
/* }}} */
/* {{{ proto int Acceptor::close(void);
* Stop the acceptor. */
template <typename Protocol>
P3_METHOD(acceptor<Protocol>, close)
{
boost::system::error_code ec;
RETVAL_EC(acceptor_.close(ec));
}
/* }}} */
template <typename Protocol>
zend_class_entry* acceptor<Protocol>::class_entry;
template <typename Protocol>
zend_object_handlers acceptor<Protocol>::handlers;
template class acceptor<tcp>;
template class acceptor<unix>;
}

52
src/acceptor.hpp Normal file
View File

@ -0,0 +1,52 @@
/**
* php-asio/acceptor.hpp
*
* @author CismonX<admin@cismon.net>
*/
#pragma once
#include "common.hpp"
#include "base.hpp"
#include "socket.hpp"
namespace asio
{
/// Wrapper for Boost.Asio stream socket acceptor.
/// Provide TCP services.
template <typename Protocol>
class acceptor : public base
{
/// Boost.Asio acceptor instance.
typename Protocol::acceptor acceptor_;
/// Accept handler.
zval* handler(const boost::system::error_code& error,
socket<Protocol>* socket, zval* callback, zval* argument);
public:
/// Constructor.
explicit acceptor(
boost::asio::io_service& io_service
) : base(io_service), acceptor_(io_service) {}
P3_METHOD_DECLARE(open);
P3_METHOD_DECLARE(assign);
P3_METHOD_DECLARE(bind);
P3_METHOD_DECLARE(listen);
P3_METHOD_DECLARE(accept);
P3_METHOD_DECLARE(cancel);
P3_METHOD_DECLARE(close);
PHP_ASIO_CE_DECLARE();
};
using tcp_acceptor = acceptor<tcp>;
using unix_acceptor = acceptor<unix>;
}

58
src/base.hpp Normal file
View File

@ -0,0 +1,58 @@
/**
* php-asio/base.hpp
*
* @author CismonX<admin@cismon.net>
*/
#pragma once
#ifdef ZTS
#include <atomic>
#define HANDLER_COUNT_TYPE std::atomic<unsigned short>
#else
#define HANDLER_COUNT_TYPE unsigned short
#endif // ZTS
#include "common.hpp"
namespace asio
{
/// A base class for all I/O objects.
class base
{
protected:
/// IO service of this object.
boost::asio::io_service& io_service_;
/// Count of pending async handlers.
HANDLER_COUNT_TYPE handler_count_;
/// Constructor.
explicit base(boost::asio::io_service& io_service) : io_service_(io_service) {}
public:
/// Deleted default constructor.
base() = delete;
/// Default destructor.
~base() = default;
/// Deleted copy constructor.
base(const base&) = delete;
/// Deleted copy assignment operator.
base& operator=(const base&) = delete;
/// Increment handler count.
unsigned short handler_count_inc()
{
return ++handler_count_;
}
/// Decrement handler count.
unsigned short handler_count_dec()
{
return --handler_count_;
}
};
}

197
src/common.hpp Normal file
View File

@ -0,0 +1,197 @@
/**
* php-asio/common.hpp
*
* @author CismonX<admin@cismon.net>
*/
#pragma once
#include "p3.hpp"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
// 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>(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<zval*>(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<zend_long>((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<void(const boost::system::error_code&)>(boost::bind( \
&future::resolve<std::remove_pointer_t<decltype(this)>, NOARG>, \
future, boost::asio::placeholders::error, 0))
#define ASYNC_HANDLER_DOUBLE_ARG(obj_type) \
std::function<void(const boost::system::error_code&, obj_type)>(boost::bind( \
&future::resolve<std::remove_pointer_t<decltype(this)>, 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<zend_long>(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;

133
src/future.cpp Normal file
View File

@ -0,0 +1,133 @@
/**
* php-asio/future.cpp
*
* @author CismonX<admin@cismon.net>
*/
#include "future.hpp"
#include "generator.hpp"
#include "timer.hpp"
#include "signal.hpp"
#include "resolver.hpp"
#include "socket.hpp"
#include "acceptor.hpp"
#include "stream_descriptor.hpp"
namespace asio
{
future* future::add(
void* io_object
#ifdef ENABLE_COROUTINE
, zend_object*& obj)
{
obj = p3::alloc_object<future>(class_entry,
[io_object](future* ptr) {
new(ptr) future(io_object);
});
GC_ADDREF(obj);
return p3::to_object<future>(obj);
#else
) {
return new future(io_object);
#endif // ENABLE_COROUTINE
}
template <typename T>
void future::on_resolve(const ASYNC_CALLBACK(T)&& callback)
{
callback_ = new ASYNC_CALLBACK(T)(std::move(callback));
}
template <typename V, typename T>
void future::resolve(const boost::system::error_code& ec, T arg)
{
auto callback = static_cast<ASYNC_CALLBACK(T)*>(callback_);
send_ = (*callback)(ec, arg);
#ifdef ENABLE_COROUTINE
if (yield_) {
last_error_ = static_cast<int64_t>(ec.value());
generator_send(reinterpret_cast<zend_generator*>(Z_OBJ_P(generator_)), send_);
coroutine(generator_);
}
#endif // ENABLE_COROUTINE
zval_ptr_dtor(send_);
efree(send_);
delete callback;
auto io_object = static_cast<V*>(io_object_);
if (io_object->handler_count_dec() == 0)
PHP_ASIO_OBJ_DTOR(io_object);
#ifdef ENABLE_STRAND
if (strand_ && strand_->handler_count_dec() == 0)
PHP_ASIO_OBJ_DTOR(strand_);
#endif // ENABLE_STRAND
#ifdef ENABLE_COROUTINE
PHP_ASIO_OBJ_DTOR(this);
#else
delete this;
#endif // ENABLE_COROUTINE
}
#ifdef ENABLE_STRAND
zval* future::handle_strand(zval* callable)
{
if (callable && Z_TYPE_P(callable) == IS_OBJECT &&
instanceof_function(Z_OBJCE_P(callable), wrapped_handler::class_entry)) {
const auto wrapped_hander = p3::to_object<wrapped_handler>(callable);
strand_ = wrapped_hander->strand_;
return wrapped_hander->callback_;
}
return callable;
}
#endif // ENABLE_STRAND
#ifdef ENABLE_COROUTINE
void future::coroutine(zval* value)
{
if (Z_TYPE_P(value) == IS_OBJECT && instanceof_function(Z_OBJCE_P(value), zend_ce_generator)) {
const auto generator = reinterpret_cast<zend_generator*>(Z_OBJ_P(value));
if (generator_valid(generator)) {
const auto ret = generator_current(generator);
if (ret && instanceof_function(Z_OBJCE_P(ret), class_entry)) {
const auto future = p3::to_object<asio::future>(ret);
future->generator_ = value;
future->yield_ = true;
return;
}
PHP_ASIO_ERROR(E_WARNING, "Invalid yield value. Future expected.");
}
}
zval_ptr_dtor(value);
efree(value);
}
P3_METHOD(future, lastError)
{
RETVAL_LONG(last_error_)
}
thread_local int64_t future::last_error_ = 0;
PHP_ASIO_CE_DEFINE(future);
#endif // ENABLE_COROUTINE
template void future::on_resolve(const ASYNC_CALLBACK(int)&&);
template void future::on_resolve(const ASYNC_CALLBACK(size_t)&&);
template void future::on_resolve(const ASYNC_CALLBACK(tcp::resolver::iterator)&&);
template void future::on_resolve(const ASYNC_CALLBACK(udp::resolver::iterator)&&);
template void future::resolve<timer>(const boost::system::error_code&, int);
template void future::resolve<signal>(const boost::system::error_code&, int);
template void future::resolve<resolver<tcp>>(
const boost::system::error_code&, tcp::resolver::iterator);
template void future::resolve<resolver<udp>>(
const boost::system::error_code&, udp::resolver::iterator);
template void future::resolve<socket<tcp>>(const boost::system::error_code&, int);
template void future::resolve<socket<tcp>>(const boost::system::error_code&, size_t);
template void future::resolve<socket<unix>>(const boost::system::error_code&, int);
template void future::resolve<socket<unix>>(const boost::system::error_code&, size_t);
template void future::resolve<socket<udp>>(const boost::system::error_code&, size_t);
template void future::resolve<socket<udg>>(const boost::system::error_code&, size_t);
template void future::resolve<acceptor<tcp>>(const boost::system::error_code&, int);
template void future::resolve<acceptor<unix>>(const boost::system::error_code&, int);
template void future::resolve<stream_descriptor>(const boost::system::error_code&, size_t);
}

95
src/future.hpp Normal file
View File

@ -0,0 +1,95 @@
/**
* php-asio/future.hpp
*
* @author CismonX<admin@cismon.net>
*/
#pragma once
#include "common.hpp"
#include "wrapped_handler.hpp"
#define ASYNC_CALLBACK(type) std::function<zval*(const boost::system::error_code&, type)>
namespace asio
{
/// Class Future.
/// When an asynchronous operation completes, its Future will be resolved.
/// And the corresponding coroutine will resume (if Future was yielded by a Generator).
class future
{
/// Handler callback of the async operation.
void* callback_ = nullptr;
/// Pointer to the I/O object which created this Future.
void* io_object_;
#ifdef ENABLE_COROUTINE
/// Last error code emitted by yielded async operations of this thread.
static thread_local int64_t last_error_;
/// Generator instance which yielded this Future.
zval* generator_ = nullptr;
/// Whether this future is yielded by a Generator.
bool yield_ = false;
#endif // ENABLE_COROUTINE
/// Value which will be sent back to the Generator.
zval* send_ = nullptr;
#ifdef ENABLE_STRAND
/// Pointer to Strand which wrapped this Future.
strand* strand_ = nullptr;
#endif // ENABLE_STRAND
/// Constructor.
explicit future(void* io_object) : io_object_(io_object) {}
public:
/// Create a new Future instance.
static future* add(
void* io_object
#ifdef ENABLE_COROUTINE
, zend_object*& obj
#endif // ENABLE_COROUTINE
);
/// Deleted default constructor.
explicit future() = delete;
/// Deleted copy constructor.
future(const future&) = delete;
/// Deleted copy assignment operator.
future& operator=(const future&) = delete;
/// Set future resolver callback.
template <typename T>
void on_resolve(const ASYNC_CALLBACK(T)&& callback);
/// Resolve the Future upon operation completion.
template <typename V, typename T>
void resolve(const boost::system::error_code& ec, T arg);
#ifdef ENABLE_STRAND
zval* handle_strand(zval* callable);
/// Get the pointer to the strand which wrapped this Future.
boost::asio::strand* get_strand() const
{
return strand_ ? strand_->implmentation() : nullptr;
}
#endif // ENABLE_STRAND
#ifdef ENABLE_COROUTINE
/// Attempt to start/resume a coroutine with a PHP Generator.
static void coroutine(zval* value);
/// Get last error emitted by handler callback within yielded Future.
static P3_METHOD_DECLARE(lastError);
PHP_ASIO_CE_DECLARE();
#endif // ENABLE_COROUTINE
};
}

63
src/generator.hpp Normal file
View File

@ -0,0 +1,63 @@
/**
* php-asio/generator.hpp
*
* Several functions copied from zend_generators.c,
* which provides PHP's generator functionalities.
* Used by Future when coroutine is enabled.
*
* @author CismonX<admin@cismon.net>
*/
#pragma once
#include "common.hpp"
#include <zend_generators.h>
#ifdef ENABLE_COROUTINE
namespace asio
{
/// Ensure that the generator is initialized.
inline void zend_generator_ensure_initialized(zend_generator* generator)
{
if (UNEXPECTED(Z_TYPE(generator->value) == IS_UNDEF) &&
EXPECTED(generator->execute_data) &&
EXPECTED(generator->node.parent == nullptr)) {
generator->flags |= ZEND_GENERATOR_DO_INIT;
zend_generator_resume(generator);
generator->flags &= ~ZEND_GENERATOR_DO_INIT;
generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
}
}
/// Check whether the generator is valid.
inline bool generator_valid(zend_generator* generator)
{
zend_generator_ensure_initialized(generator);
zend_generator_get_current(generator);
return EXPECTED(generator->execute_data != nullptr);
}
/// Get current yield value of the generator.
inline zval* generator_current(zend_generator* generator)
{
zend_generator_ensure_initialized(generator);
const auto root = zend_generator_get_current(generator);
if (EXPECTED(generator->execute_data != NULL && Z_TYPE(root->value) != IS_UNDEF))
return &root->value;
return nullptr;
}
/// Send a value to the generator.
inline void generator_send(zend_generator* generator, zval* value)
{
zend_generator_ensure_initialized(generator);
if (UNEXPECTED(!generator->execute_data))
return;
const auto root = zend_generator_get_current(generator);
if (root->send_target)
ZVAL_COPY(root->send_target, value);
zend_generator_resume(generator);
}
}
#endif // ENABLE_COROUTINE

178
src/io.hpp Normal file
View File

@ -0,0 +1,178 @@
/**
* php-asio/io.hpp
*
* @author CismonX<admin@cismon.net>
*/
#pragma once
#include "common.hpp"
#define PHP_ASIO_INET_ASSIGN(obj, p) \
zend_bool inet6; \
zval* fd; \
ZEND_PARSE_PARAMETERS_START(2, 2) \
Z_PARAM_BOOL(inet6) \
Z_PARAM_ZVAL(fd) \
ZEND_PARSE_PARAMETERS_END(); \
boost::system::error_code ec; \
const auto protocol = inet6 ? p::v6() : p::v4(); \
if (UNEXPECTED(Z_TYPE_P(fd) == IS_LONG)) \
(obj).assign(protocol, Z_LVAL_P(fd), ec); \
else \
(obj).assign(protocol, resource_to_fd(fd), ec); \
RETVAL_EC(ec)
#define PHP_ASIO_LOCAL_ASSIGN(obj, p) \
zval* fd; \
ZEND_PARSE_PARAMETERS_START(1, 1) \
Z_PARAM_ZVAL(fd) \
ZEND_PARSE_PARAMETERS_END(); \
boost::system::error_code ec; \
if (UNEXPECTED(Z_TYPE_P(fd) == IS_LONG)) \
(obj).assign(p(), Z_LVAL_P(fd), ec); \
else \
(obj).assign(p(), resource_to_fd(fd), ec); \
RETVAL_EC(ec)
#if defined(ENABLE_NULL_BUFFERS) && BOOST_VERSION >= 106600
// Null buffers are deprecated as of Boost 1.66.
// Method `async_wait()` on sockets and stream descriptors is preferred.
#undef ENABLE_NULL_BUFFERS
#endif
#ifdef ENABLE_NULL_BUFFERS
#define PHP_ASIO_BUFFER_LEN_VALIDATE() \
if (UNEXPECTED(length < 0)) { \
PHP_ASIO_ERROR(E_WARNING, "Non-negative integer expected."); \
RETURN_NULL(); \
}
#define PHP_ASIO_EMPTY_READ_BUFFER length == 0 ? ZSTR_EMPTY_ALLOC() :
#define PHP_ASIO_ON_READABLE(obj) \
if (length == 0) \
if (read_some) \
(obj).async_read_some(boost::asio::null_buffers(), \
STRAND_RESOLVE(ASYNC_HANDLER_DOUBLE_ARG(size_t))); \
else \
async_read(obj, boost::asio::null_buffers(), \
STRAND_RESOLVE(ASYNC_HANDLER_DOUBLE_ARG(size_t))); \
else
#define PHP_ASIO_EMPTY_WRITE_BUFFER ZSTR_LEN(data) == 0 ? nullptr :
#define PHP_ASIO_ON_WRITABLE(obj) \
if (ZSTR_LEN(data) == 0) \
if (write_some) \
(obj).async_write_some(boost::asio::null_buffers(), \
STRAND_RESOLVE(ASYNC_HANDLER_DOUBLE_ARG(size_t))); \
else \
async_write(obj, boost::asio::null_buffers(), \
STRAND_RESOLVE(ASYNC_HANDLER_DOUBLE_ARG(size_t))); \
else
#else
#define PHP_ASIO_BUFFER_LEN_VALIDATE() \
if (UNEXPECTED(length <= 0)) { \
PHP_ASIO_ERROR(E_WARNING, "Positive integer expected."); \
RETURN_NULL(); \
}
#define PHP_ASIO_EMPTY_READ_BUFFER
#define PHP_ASIO_ON_READABLE(obj)
#define PHP_ASIO_EMPTY_WRITE_BUFFER
#define PHP_ASIO_ON_WRITABLE(obj)
#endif // ENABLE_NULL_BUFFERS
#define PHP_ASIO_READ(type, obj) \
zend_long length; \
zend_bool read_some = 1; \
zval* callback = nullptr; \
zval* argument = nullptr; \
ZEND_PARSE_PARAMETERS_START(1, 4) \
Z_PARAM_LONG(length) \
Z_PARAM_OPTIONAL \
Z_PARAM_BOOL(read_some) \
Z_PARAM_ZVAL(callback) \
Z_PARAM_ZVAL(argument) \
ZEND_PARSE_PARAMETERS_END(); \
PHP_ASIO_BUFFER_LEN_VALIDATE(); \
auto buffer_container = PHP_ASIO_EMPTY_READ_BUFFER \
zend_string_alloc(static_cast<size_t>(length), 0); \
PHP_ASIO_FUTURE_INIT(); \
future->template on_resolve<size_t>(boost::bind(&type::read_handler, \
this, _1, _2, buffer_container, cb, args)); \
PHP_ASIO_ON_READABLE(obj) \
if (read_some) \
(obj).async_read_some(mutable_buffer(buffer_container), \
STRAND_RESOLVE(ASYNC_HANDLER_DOUBLE_ARG(size_t))); \
else \
async_read(obj, mutable_buffer(buffer_container), \
STRAND_RESOLVE(ASYNC_HANDLER_DOUBLE_ARG(size_t))); \
FUTURE_RETURN()
#define PHP_ASIO_WRITE(type, obj) \
zend_string* data; \
zend_bool write_some = 0; \
zval* callback = nullptr; \
zval* argument = nullptr; \
ZEND_PARSE_PARAMETERS_START(1, 4) \
Z_PARAM_STR(data) \
Z_PARAM_OPTIONAL \
Z_PARAM_BOOL(write_some) \
Z_PARAM_ZVAL(callback) \
Z_PARAM_ZVAL(argument) \
ZEND_PARSE_PARAMETERS_END(); \
auto buffer_container = PHP_ASIO_EMPTY_WRITE_BUFFER \
zend_string_copy(data); \
PHP_ASIO_FUTURE_INIT(); \
future->template on_resolve<size_t>(boost::bind(&type::write_handler, \
this, _1, _2, buffer_container, cb, args)); \
PHP_ASIO_ON_WRITABLE(obj) \
if (write_some) \
(obj).async_write_some(mutable_buffer(buffer_container), \
STRAND_RESOLVE(ASYNC_HANDLER_DOUBLE_ARG(size_t))); \
else \
async_write(obj, mutable_buffer(buffer_container), \
STRAND_RESOLVE(ASYNC_HANDLER_DOUBLE_ARG(size_t))); \
FUTURE_RETURN()
namespace asio
{
/// Wrap a zend string into const buffer.
inline auto const_buffer(const zend_string* str)
{
return boost::asio::const_buffers_1(boost::asio::const_buffer(
ZSTR_LEN(str) ? ZSTR_VAL(str) : nullptr, ZSTR_LEN(str)
));
}
/// Wrap a zend string into mutable buffer.
inline auto mutable_buffer(zend_string* str)
{
return boost::asio::mutable_buffers_1(boost::asio::mutable_buffer(
ZSTR_LEN(str) ? ZSTR_VAL(str) : nullptr, ZSTR_LEN(str)
));
}
/// Extract a valid poll fd from a PHP resource.
inline int resource_to_fd(zval* resource)
{
// The following code is copied from php-uv.
// See https://github.com/bwoebi/php-uv/blob/master/php_uv.c#L401
if (UNEXPECTED(Z_TYPE_P(resource) != IS_RESOURCE))
return -1;
const auto stream = static_cast<php_stream*>(
zend_fetch_resource_ex(resource, nullptr, php_file_le_stream()));
if (stream == nullptr)
return -1;
if (stream->wrapper && !strcmp(stream->wrapper->wops->label, "PHP") &&
(!stream->orig_path ||
strncmp(stream->orig_path, "php://std", sizeof "php://std" - 1) &&
strncmp(stream->orig_path, "php://fd", sizeof "php://fd" - 1)))
return -1;
auto fd = -1;
if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
reinterpret_cast<void**>(&fd), 1) == SUCCESS && fd >= 0) {
if (stream->wrapper && !strcmp(stream->wrapper->wops->label, "plainfile"))
return -1;
return fd;
}
return -1;
}
}

130
src/p3.hpp Normal file
View File

@ -0,0 +1,130 @@
/**
* php-asio/p3.hpp
*
* This header is a simple helper for wrapping C++ classes,
* which is borrowed from https://github.com/phplang/p3.
* The casting/cloning/comparing functionalities are removed,
* because they are not needed by php-asio.
*/
#pragma once
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <php.h>
#include <zend_exceptions.h>
#include <new>
#include <type_traits>
#define P3_METHOD_DECLARE(name) \
void zim_##name(INTERNAL_FUNCTION_PARAMETERS)
#define P3_METHOD(cls, name) \
void cls::zim_##name(INTERNAL_FUNCTION_PARAMETERS)
#define P3_ME(cls, name, meth, arginfo, flags) \
ZEND_FENTRY(name, [](INTERNAL_FUNCTION_PARAMETERS) { \
::p3::to_object<cls>(getThis())->zim_##meth(INTERNAL_FUNCTION_PARAM_PASSTHRU); \
}, arginfo, flags)
#define P3_ME_D(cls, meth, arginfo, flags) \
P3_ME(cls, meth, meth, arginfo, flags)
#define P3_STATIC_ME(cls, meth, arginfo, flags) \
ZEND_FENTRY(meth, &cls::zim_##meth, arginfo, flags | ZEND_ACC_STATIC)
#define P3_ABSTRACT_ME(name, arginfo) \
PHP_ABSTRACT_ME("", name, arginfo)
namespace p3 {
/// Native object to Zend object.
template <class T>
zend_object* to_zend_object(T* obj)
{
return reinterpret_cast<zend_object*>(obj + 1);
}
/// Zend object to native object.
template <class T>
T* to_object(zend_object* obj)
{
return reinterpret_cast<T*>(obj) - 1;
}
/// Zval to native object.
template <class T>
T* to_object(zval* obj)
{
return reinterpret_cast<T*>(Z_OBJ_P(obj)) - 1;
}
/// Allocate new object.
template <class T, typename InitFunc>
zend_object* alloc_object(zend_class_entry* ce, InitFunc init)
{
auto ptr = reinterpret_cast<T*>(ecalloc(1, sizeof(T) +
sizeof(zend_object) + zend_object_properties_size(ce)));
init(ptr);
auto zobj = to_zend_object(ptr);
zend_object_std_init(zobj, ce);
zobj->handlers = &T::handlers;
return zobj;
}
/// Allocate new object with default constructor.
template <class T>
typename std::enable_if<std::is_constructible<T>::value, zend_object*>::type
create_object(zend_class_entry* ce)
{
return alloc_object<T>(ce, [](T* ptr) {
new(ptr) T();
});
}
template <class T>
typename std::enable_if<!std::is_constructible<T>::value, zend_object*>::type
create_object(zend_class_entry* ce)
{
assert(false);
return nullptr;
}
/// Destroy an object.
template <class T>
void dtor_object(zend_object *obj)
{
zend_object_std_dtor(obj);
to_object<T>(obj)->~T();
}
/// Fail to create object if there's no default constructor.
inline zend_object* create_object_fail(zend_class_entry* ce) {
php_error_docref(nullptr, E_ERROR,
"%s should not be directly instantiated.", ZSTR_VAL(ce->name));
return zend_objects_new(ce);
}
template <class T>
zend_class_entry* class_init(const char* name, const zend_function_entry* methods)
{
zend_class_entry ce;
INIT_CLASS_ENTRY_EX(ce, name, strlen(name), methods);
T::class_entry = zend_register_internal_class(&ce);
T::class_entry->create_object =
std::is_constructible<T>::value ? create_object<T> : create_object_fail;
memcpy(&T::handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
T::handlers.offset = sizeof(T);
T::handlers.free_obj = dtor_object<T>;
T::handlers.clone_obj = nullptr;
return T::class_entry;
}
inline zend_class_entry* interface_init(const char* name, const zend_function_entry* methods)
{
zend_class_entry ce;
INIT_CLASS_ENTRY_EX(ce, name, strlen(name), methods);
return zend_register_internal_interface(&ce);
}
}

519
src/php_asio.cpp Normal file
View File

@ -0,0 +1,519 @@
/**
* php-asio/php_asio.cpp
*
* @author CismonX<admin@cismon.net>
*/
#include <php.h>
#include <ext/standard/info.h>
#include "php_asio.hpp"
#include "p3.hpp"
#include "service.hpp"
#include "wrapped_handler.hpp"
/* {{{ arg_info */
ZEND_BEGIN_ARG_INFO(service_run_arginfo, 0)
ZEND_ARG_INFO(1, ec)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(service_run_one_arginfo, 0)
ZEND_ARG_INFO(1, ec)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(service_poll_arginfo, 0)
ZEND_ARG_INFO(1, ec)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(service_poll_one_arginfo, 0)
ZEND_ARG_INFO(1, ec)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(service_dispatch_arginfo, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
#ifdef ENABLE_STRAND
ZEND_BEGIN_ARG_INFO(strand_dispatch_arginfo, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
#endif // ENABLE_STRAND
ZEND_BEGIN_ARG_INFO(strand_wrap_arginfo, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(timer_expires_from_now_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, duration, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(timer_expires_at_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(timer_wait_arginfo, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(signal_add_arginfo, 0)
ZEND_ARG_VARIADIC_INFO(0, sig_num)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(signal_remove_arginfo, 0)
ZEND_ARG_VARIADIC_INFO(0, sig_num)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(signal_wait_arginfo, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(resolver_resolve_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, service, IS_STRING, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(socket_available_arginfo, 0)
ZEND_ARG_INFO(1, ec)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(socket_at_mark_arginfo, 0)
ZEND_ARG_INFO(1, ec)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(inet_socket_open_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, inet6, _IS_BOOL, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(inet_socket_bind_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, address, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(inet_socket_assign_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, inet6, _IS_BOOL, 0)
ZEND_ARG_INFO(0, native_handle)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(local_socket_assign_arginfo, 0)
ZEND_ARG_INFO(0, native_handle)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(stream_socket_read_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, read_some, _IS_BOOL, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(stream_socket_write_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, write_some, _IS_BOOL, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(local_socket_bind_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(datagram_socket_recv_from_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(tcp_socket_connect_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, address, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(unix_socket_connect_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(udp_socket_send_to_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, address, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(udg_socket_send_to_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(acceptor_listen_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, backlog, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(acceptor_accept_arginfo, 0)
ZEND_ARG_CALLABLE_INFO(0, callback, 0)
ZEND_ARG_INFO(0, argument)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(stream_descriptor_assign_arginfo, 0)
ZEND_ARG_INFO(0, native_handle)
ZEND_END_ARG_INFO()
/* }}} */
/* {{{ function_entry */
static zend_function_entry service_methods[] = {
P3_ME_D(asio::service, addTimer, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addSignal, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addTcpResolver, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addUdpResolver, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addTcpSocket, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addUdpSocket, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addUnixSocket, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addUdgSocket, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addTcpAcceptor, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addUnixAcceptor, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, addStreamDescriptor, nullptr, ZEND_ACC_PUBLIC)
#ifdef ENABLE_STRAND
P3_ME_D(asio::service, addStrand, nullptr, ZEND_ACC_PUBLIC)
#endif // ENABLE_STRAND
P3_ME_D(asio::service, run, service_run_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, runOne, service_run_one_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, poll, service_poll_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, pollOne, service_poll_one_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, stop, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, reset, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, stopped, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, post, service_dispatch_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, dispatch, service_dispatch_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, forkPrepare, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, forkParent, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::service, forkChild, nullptr, ZEND_ACC_PUBLIC)
#ifdef ENABLE_COROUTINE
P3_STATIC_ME(asio::future, lastError, nullptr, ZEND_ACC_PUBLIC)
#endif // ENABLE_COROUTINE
PHP_FE_END
};
#ifdef ENABLE_STRAND
static zend_function_entry strand_methods[] = {
P3_ME_D(asio::strand, dispatch, strand_dispatch_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::strand, post, strand_dispatch_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::strand, runningInThisThread, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::strand, wrap, strand_wrap_arginfo, ZEND_ACC_PUBLIC)
PHP_FE_END
};
static zend_function_entry wrapped_handler_methods[] = {
P3_ME_D(asio::wrapped_handler, __invoke, nullptr, ZEND_ACC_PUBLIC)
PHP_FE_END
};
#endif // ENABLE_STRAND
static zend_function_entry io_object_method[] = {
P3_ABSTRACT_ME(cancel, nullptr)
P3_ABSTRACT_ME(destroy, nullptr)
PHP_FE_END
};
static zend_function_entry timer_methods[] = {
P3_ME_D(asio::timer, expiresFromNow, timer_expires_from_now_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::timer, expiresAt, timer_expires_at_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::timer, wait, timer_wait_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::timer, cancel, nullptr, ZEND_ACC_PUBLIC)
PHP_FE_END
};
static zend_function_entry signal_methods[] = {
P3_ME_D(asio::signal, add, signal_add_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::signal, remove, signal_remove_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::signal, wait, signal_wait_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::signal, clear, nullptr, ZEND_ACC_PUBLIC)
P3_ME_D(asio::signal, cancel, nullptr, ZEND_ACC_PUBLIC)
PHP_FE_END
};
static zend_function_entry resolver_methods[] = {
P3_ABSTRACT_ME(resolve, resolver_resolve_arginfo)
P3_ABSTRACT_ME(cancel, nullptr)
PHP_FE_END
};
static zend_function_entry tcp_resolver_methods[] = {
P3_ME_D(asio::tcp_resolver, resolve, resolver_resolve_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::tcp_resolver, cancel, nullptr, ZEND_ACC_PUBLIC)
PHP_FE_END
};
static zend_function_entry udp_resolver_methods[] = {
P3_ME_D(asio::udp_resolver, resolve, resolver_resolve_arginfo, ZEND_ACC_PUBLIC)
P3_ME_D(asio::udp_resolver, cancel, nullptr, ZEND_ACC_PUBLIC)
PHP_FE_END
};
static zend_function_entry socket_methods[] = {
P3_ABSTRACT_ME(available, socket_available_arginfo)
P3_ABSTRACT_ME(atMark, socket_at_mark_arginfo)
P3_ABSTRACT_ME(close, nullptr)
PHP_FE_END
};
static zend_function_entry inet_socket_methods[] = {
P3_ABSTRACT_ME(open, inet_socket_open_arginfo)
P3_ABSTRACT_ME(assign, inet_socket_assign_arginfo)
P3_ABSTRACT_ME(bind, inet_socket_bind_arginfo)
P3_ABSTRACT_ME(remoteAddr, nullptr)
P3_ABSTRACT_ME(remotePort, nullptr)
PHP_FE_END
};
static zend_function_entry local_socket_methods[] = {
P3_ABSTRACT_ME(open, nullptr)
P3_ABSTRACT_ME(assign, local_socket_assign_arginfo)
P3_ABSTRACT_ME(bind, local_socket_bind_arginfo)
P3_ABSTRACT_ME(remotePath, nullptr)
PHP_FE_END
};
static zend_function_entry stream_socket_methods[] = {
P3_ABSTRACT_ME(read, stream_socket_read_arginfo)
P3_ABSTRACT_ME(write, stream_socket_write_arginfo)
PHP_FE_END
};
static zend_function_entry datagram_socket_methods[] = {
P3_ABSTRACT_ME(recvFrom, datagram_socket_recv_from_arginfo)