archive
commit
9f3a47020d
|
@ -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
|
|
@ -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.
|
|
@ -0,0 +1,28 @@
|
|||
# php-asio
|
||||
|
||||
[](https://travis-ci.org/CismonX/php-asio)
|
||||
[](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**)
|
|
@ -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
|
|
@ -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>;
|
||||
}
|
|
@ -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>;
|
||||
}
|
|
@ -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_;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|