<commit_before>/*
  +----------------------------------------------------------------------+
  | Swoole                                                               |
  +----------------------------------------------------------------------+
  | This source file is subject to version 2.0 of the Apache license,    |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.apache.org/licenses/LICENSE-2.0.html                      |
  | If you did not receive a copy of the Apache2.0 license and are unable|
  | to obtain it through the world-wide-web, please send a note to       |
  | <EMAIL> so we can mail you a copy immediately.            |
  +----------------------------------------------------------------------+
  | Author: <NAME>  <<EMAIL>>                        |
  +----------------------------------------------------------------------+
*/

#include "php_swoole.h"

#include "thirdparty/hiredis/hiredis.h"
#include "thirdparty/hiredis/async.h"

#define SW_REDIS_COMMAND_BUFFER_SIZE   64
#define SW_REDIS_COMMAND_KEY_SIZE      128

typedef struct
{
    redisAsyncContext *context;
    uint8_t state;
    uint8_t connected;
    uint8_t subscribe;
    uint8_t connecting;
    uint32_t reqnum;

    zval *object;
    zval *message_callback;

    double timeout;
    swTimer_node *timer;

    char *password;
    uint8_t password_len;
    int8_t database;
    uint8_t failure;
    uint8_t wait_count;

    zval _message_callback;
    zval _object;

} swRedisClient;

enum swoole_redis_state
{
    SWOOLE_REDIS_STATE_CONNECT,
    SWOOLE_REDIS_STATE_READY,
    SWOOLE_REDIS_STATE_WAIT_RESULT,
    SWOOLE_REDIS_STATE_SUBSCRIBE,
    SWOOLE_REDIS_STATE_CLOSED,
};

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_construct, 0, 0, 0)
    ZEND_ARG_ARRAY_INFO(0, setting, 1)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_connect, 0, 0, 3)
    ZEND_ARG_INFO(0, host)
    ZEND_ARG_INFO(0, port)
    ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_call, 0, 0, 2)
    ZEND_ARG_INFO(0, command)
    ZEND_ARG_INFO(0, params)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_on, 0, 0, 2)
    ZEND_ARG_INFO(0, event_name)
    ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0)
ZEND_END_ARG_INFO()

static PHP_METHOD(swoole_redis, __construct);
static PHP_METHOD(swoole_redis, __destruct);
static PHP_METHOD(swoole_redis, on);
static PHP_METHOD(swoole_redis, connect);
static PHP_METHOD(swoole_redis, getState);
static PHP_METHOD(swoole_redis, __call);
static PHP_METHOD(swoole_redis, close);

static void swoole_redis_onConnect(const redisAsyncContext *c, int status);
static void swoole_redis_onClose(const redisAsyncContext *c, int status);
static int swoole_redis_onRead(swReactor *reactor, swEvent *event);
static int swoole_redis_onWrite(swReactor *reactor, swEvent *event);
static int swoole_redis_onError(swReactor *reactor, swEvent *event);
static void swoole_redis_onResult(redisAsyncContext *c, void *r, void *privdata);
static void swoole_redis_parse_result(swRedisClient *redis, zval* return_value, redisReply* reply);
static void swoole_redis_onCompleted(redisAsyncContext *c, void *r, void *privdata);
static void swoole_redis_onTimeout(swTimer *timer, swTimer_node *tnode);

static void swoole_redis_event_AddRead(void *privdata);
static void swoole_redis_event_AddWrite(void *privdata);
static void swoole_redis_event_DelRead(void *privdata);
static void swoole_redis_event_DelWrite(void *privdata);
static void swoole_redis_event_Cleanup(void *privdata);

static zend_class_entry swoole_redis_ce;
static zend_class_entry *swoole_redis_class_entry_ptr;

static const zend_function_entry swoole_redis_methods[] =
{
    PHP_ME(swoole_redis, __construct, arginfo_swoole_redis_construct, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, on, arginfo_swoole_redis_on, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, connect, arginfo_swoole_redis_connect, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, close, arginfo_swoole_void, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, getState, arginfo_swoole_void, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, __call, arginfo_swoole_redis_call, ZEND_ACC_PUBLIC)
    PHP_FE_END
};

static sw_inline int swoole_redis_is_message_command(char *command, int command_len)
{
    if (strncasecmp("subscribe", command, command_len) == 0)
    {
        return SW_TRUE;
    }
    else if (strncasecmp("psubscribe", command, command_len) == 0)
    {
        return SW_TRUE;
    }
    else if (strncasecmp("unsubscribe", command, command_len) == 0)
    {
        return SW_TRUE;
    }
    else if (strncasecmp("punsubscribe", command, command_len) == 0)
    {
        return SW_TRUE;
    }
    else
    {
        return SW_FALSE;
    }
}

static sw_inline void redis_execute_connect_callback(swRedisClient *redis, int success)
{
    zval *result, *retval;
    SW_MAKE_STD_ZVAL(result);
    ZVAL_BOOL(result, success);

    zval args[2];
    zval *zcallback = sw_zend_read_property(swoole_redis_class_entry_ptr, redis->object, ZEND_STRL("onConnect"), 0);
    args[0] = *redis->object;
    args[1] = *result;
    redis->connecting = 1;
    if (sw_call_user_function_ex(EG(function_table), NULL, zcallback, &retval, 2, args, 0, NULL) != SUCCESS)
    {
        swoole_php_fatal_error(E_WARNING, "swoole_async_redis connect_callback handler error.");
    }
    if (EG(exception))
    {
        zend_exception_error(EG(exception), E_ERROR);
    }
    if (retval != NULL)
    {
        zval_ptr_dtor(retval);
    }
    zval_ptr_dtor(result);
    redis->connecting = 0;
}

void swoole_redis_init(int module_number)
{
    SWOOLE_INIT_CLASS_ENTRY(swoole_redis_ce, "swoole_redis", "Swoole\\Redis", swoole_redis_methods);
    swoole_redis_class_entry_ptr = zend_register_internal_class(&swoole_redis_ce);
    SWOOLE_CLASS_ALIAS(swoole_redis, "Swoole\\Redis");

    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("onConnect"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("onClose"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("onMessage"), ZEND_ACC_PUBLIC);

    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("setting"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("host"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("port"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("sock"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("errCode"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("errMsg"), ZEND_ACC_PUBLIC);

    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_CONNECT"), SWOOLE_REDIS_STATE_CONNECT);
    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_READY"), SWOOLE_REDIS_STATE_READY);
    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_WAIT_RESULT"), SWOOLE_REDIS_STATE_WAIT_RESULT);
    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_SUBSCRIBE"), SWOOLE_REDIS_STATE_SUBSCRIBE);
    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_CLOSED"), SWOOLE_REDIS_STATE_CLOSED);
}

static PHP_METHOD(swoole_redis, __construct)
{
    zval *zset = NULL;
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &zset) == FAILURE)
    {
        RETURN_FALSE;
    }

    swRedisClient *redis = emalloc(sizeof(swRedisClient));
    bzero(redis, sizeof(swRedisClient));

    redis->object = getThis();
    redis->timeout = SW_REDIS_CONNECT_TIMEOUT;
    redis->database = -1;

    if (zset && ZVAL_IS_ARRAY(zset))
    {
        php_swoole_array_separate(zset);
        zend_update_property(swoole_redis_class_entry_ptr, getThis(), ZEND_STRL("setting"), zset);
        zval_ptr_dtor(zset);

        HashTable *vht;
        zval *ztmp;
        vht = Z_ARRVAL_P(zset);
        /**
         * timeout
         */
        if (php_swoole_array_get_value(vht, "timeout", ztmp))
        {
            convert_to_double(ztmp);
            redis->timeout = (double) Z_DVAL_P(ztmp);
        }
        /**
         * password
         */
        if (php_swoole_array_get_value(vht, "password", ztmp))
        {
            convert_to_string(ztmp);
            if (Z_STRLEN_P(ztmp) >= 1 << 8)
            {
                swoole_php_fatal_error(E_WARNING, "redis password is too long.");
            }
            else if (Z_STRLEN_P(ztmp) > 0)
            {
                redis->password = <PASSWORD>(Z_STRVAL_P(ztmp));
                redis->password_len = Z_STRLEN_P(ztmp);
            }
        }
        /**
         * database
         */
        if (php_swoole_array_get_value(vht, "database", ztmp))
        {
            convert_to_long(ztmp);
            if (Z_LVAL_P(ztmp) > 1 << 8)
            {
                swoole_php_fatal_error(E_WARNING, "redis database number is too big.");
            }
            else
            {
                redis->database = (int8_t) Z_LVAL_P(ztmp);
            }
        }
    }

    sw_copy_to_stack(redis->object, redis->_object);
    swoole_set_object(getThis(), redis);
}

static PHP_METHOD(swoole_redis, on)
{
    char *name;
    size_t len;
    zval *cb;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name, &len, &cb) == FAILURE)
    {
        RETURN_FALSE;
    }

    swRedisClient *redis = swoole_get_object(getThis());
    if (redis->context != NULL)
    {
        swoole_php_fatal_error(E_WARNING, "Must be called before connecting.");
        RETURN_FALSE;
    }

    if (strncasecmp("close", name, len) == 0)
    {
        zend_update_property(swoole_redis_class_entry_ptr, getThis(), ZEND_STRL("onClose"), cb);
    }
    else if (strncasecmp("message", name, len) == 0)
    {
        zend_update_property(swoole_redis_class_entry_ptr, getThis(), ZEND_STRL("onMessage"), cb);
        redis->message_callback = sw_zend_read_property(swoole_redis_class_entry_ptr, getThis(), ZEND_STRL("onMessage"), 0);
        sw_copy_to_stack(redis->message_callback, redis->_message_callback);

        redis->subscribe = 1;
    }
    else
    {
        swoole_php_error(E_WARNING, "Unknown event type[%s]", name);
        RETURN_FALSE;
    }
    RETURN_TRUE;
}

static PHP_METHOD(swoole_redis, connect)
{
    char *host;
    size_t host_len;
    long port;
    zval *callback;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &host, &host_len, &port, &callback) == FAILURE)
    {
        RETURN_FALSE;
    }

    if (host_len <= 0)
    {
        swoole_php_error(E_WARNING, "redis server host is empty.");
        RETURN_FALSE;
    }

    swRedisClient *redis = swoole_get_object(getThis());
    redisAsyncContext *context;

    if (strncasecmp(host, ZEND_STRL("unix:/")) == 0)
    {
        context = redisAsyncConnectUnix(host + 5);
    }
    else
    {
        if (port <= 1 || port > 65535)
        {
            swoole_php_error(E_WARNING, "redis server port is invalid.");
            RETURN_FALSE;
        }
        context = redisAsyncConnect(host, (int) port);
    }

    if (context == NULL)
    {
        swoole_php_error(E_WARNING, "redisAsyncConnect() failed.");
        RETURN_FALSE;
    }

    if (context->err)
    {
        redisFree(context);
        swoole_php_error(E_WARNING, "failed to connect to the redis-server[%s:%d], Erorr: %s[%d]", host, (int) port, context->errstr, context->err);
        RETURN_FALSE;
    }

    php_swoole_check_reactor();
    if (!swReactor_handle_isset(SwooleG.main_reactor, PHP_SWOOLE_FD_REDIS))<commit_msg>Fix free.<commit_after>/*
  +----------------------------------------------------------------------+
  | Swoole                                                               |
  +----------------------------------------------------------------------+
  | This source file is subject to version 2.0 of the Apache license,    |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.apache.org/licenses/LICENSE-2.0.html                      |
  | If you did not receive a copy of the Apache2.0 license and are unable|
  | to obtain it through the world-wide-web, please send a note to       |
  | <EMAIL> so we can mail you a copy immediately.            |
  +----------------------------------------------------------------------+
  | Author: <NAME>  <<EMAIL>>                        |
  +----------------------------------------------------------------------+
*/

#include "php_swoole.h"

#include "thirdparty/hiredis/hiredis.h"
#include "thirdparty/hiredis/async.h"

#define SW_REDIS_COMMAND_BUFFER_SIZE   64
#define SW_REDIS_COMMAND_KEY_SIZE      128

typedef struct
{
    redisAsyncContext *context;
    uint8_t state;
    uint8_t connected;
    uint8_t subscribe;
    uint8_t connecting;
    uint32_t reqnum;

    zval *object;
    zval *message_callback;

    double timeout;
    swTimer_node *timer;

    char *password;
    uint8_t password_len;
    int8_t database;
    uint8_t failure;
    uint8_t wait_count;

    zval _message_callback;
    zval _object;

} swRedisClient;

enum swoole_redis_state
{
    SWOOLE_REDIS_STATE_CONNECT,
    SWOOLE_REDIS_STATE_READY,
    SWOOLE_REDIS_STATE_WAIT_RESULT,
    SWOOLE_REDIS_STATE_SUBSCRIBE,
    SWOOLE_REDIS_STATE_CLOSED,
};

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_construct, 0, 0, 0)
    ZEND_ARG_ARRAY_INFO(0, setting, 1)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_connect, 0, 0, 3)
    ZEND_ARG_INFO(0, host)
    ZEND_ARG_INFO(0, port)
    ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_call, 0, 0, 2)
    ZEND_ARG_INFO(0, command)
    ZEND_ARG_INFO(0, params)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_on, 0, 0, 2)
    ZEND_ARG_INFO(0, event_name)
    ZEND_ARG_INFO(0, callback)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_void, 0, 0, 0)
ZEND_END_ARG_INFO()

static PHP_METHOD(swoole_redis, __construct);
static PHP_METHOD(swoole_redis, __destruct);
static PHP_METHOD(swoole_redis, on);
static PHP_METHOD(swoole_redis, connect);
static PHP_METHOD(swoole_redis, getState);
static PHP_METHOD(swoole_redis, __call);
static PHP_METHOD(swoole_redis, close);

static void swoole_redis_onConnect(const redisAsyncContext *c, int status);
static void swoole_redis_onClose(const redisAsyncContext *c, int status);
static int swoole_redis_onRead(swReactor *reactor, swEvent *event);
static int swoole_redis_onWrite(swReactor *reactor, swEvent *event);
static int swoole_redis_onError(swReactor *reactor, swEvent *event);
static void swoole_redis_onResult(redisAsyncContext *c, void *r, void *privdata);
static void swoole_redis_parse_result(swRedisClient *redis, zval* return_value, redisReply* reply);
static void swoole_redis_onCompleted(redisAsyncContext *c, void *r, void *privdata);
static void swoole_redis_onTimeout(swTimer *timer, swTimer_node *tnode);

static void swoole_redis_event_AddRead(void *privdata);
static void swoole_redis_event_AddWrite(void *privdata);
static void swoole_redis_event_DelRead(void *privdata);
static void swoole_redis_event_DelWrite(void *privdata);
static void swoole_redis_event_Cleanup(void *privdata);

static zend_class_entry swoole_redis_ce;
static zend_class_entry *swoole_redis_class_entry_ptr;

static const zend_function_entry swoole_redis_methods[] =
{
    PHP_ME(swoole_redis, __construct, arginfo_swoole_redis_construct, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, on, arginfo_swoole_redis_on, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, connect, arginfo_swoole_redis_connect, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, close, arginfo_swoole_void, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, getState, arginfo_swoole_void, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_redis, __call, arginfo_swoole_redis_call, ZEND_ACC_PUBLIC)
    PHP_FE_END
};

static sw_inline int swoole_redis_is_message_command(char *command, int command_len)
{
    if (strncasecmp("subscribe", command, command_len) == 0)
    {
        return SW_TRUE;
    }
    else if (strncasecmp("psubscribe", command, command_len) == 0)
    {
        return SW_TRUE;
    }
    else if (strncasecmp("unsubscribe", command, command_len) == 0)
    {
        return SW_TRUE;
    }
    else if (strncasecmp("punsubscribe", command, command_len) == 0)
    {
        return SW_TRUE;
    }
    else
    {
        return SW_FALSE;
    }
}

static sw_inline void redis_execute_connect_callback(swRedisClient *redis, int success)
{
    zval *result, *retval;
    SW_MAKE_STD_ZVAL(result);
    ZVAL_BOOL(result, success);

    zval args[2];
    zval *zcallback = sw_zend_read_property(swoole_redis_class_entry_ptr, redis->object, ZEND_STRL("onConnect"), 0);
    args[0] = *redis->object;
    args[1] = *result;
    redis->connecting = 1;
    if (sw_call_user_function_ex(EG(function_table), NULL, zcallback, &retval, 2, args, 0, NULL) != SUCCESS)
    {
        swoole_php_fatal_error(E_WARNING, "swoole_async_redis connect_callback handler error.");
    }
    if (EG(exception))
    {
        zend_exception_error(EG(exception), E_ERROR);
    }
    if (retval != NULL)
    {
        zval_ptr_dtor(retval);
    }
    zval_ptr_dtor(result);
    redis->connecting = 0;
}

void swoole_redis_init(int module_number)
{
    SWOOLE_INIT_CLASS_ENTRY(swoole_redis_ce, "swoole_redis", "Swoole\\Redis", swoole_redis_methods);
    swoole_redis_class_entry_ptr = zend_register_internal_class(&swoole_redis_ce);
    SWOOLE_CLASS_ALIAS(swoole_redis, "Swoole\\Redis");

    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("onConnect"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("onClose"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("onMessage"), ZEND_ACC_PUBLIC);

    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("setting"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("host"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("port"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("sock"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("errCode"), ZEND_ACC_PUBLIC);
    zend_declare_property_null(swoole_redis_class_entry_ptr, ZEND_STRL("errMsg"), ZEND_ACC_PUBLIC);

    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_CONNECT"), SWOOLE_REDIS_STATE_CONNECT);
    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_READY"), SWOOLE_REDIS_STATE_READY);
    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_WAIT_RESULT"), SWOOLE_REDIS_STATE_WAIT_RESULT);
    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_SUBSCRIBE"), SWOOLE_REDIS_STATE_SUBSCRIBE);
    zend_declare_class_constant_long(swoole_redis_class_entry_ptr, ZEND_STRL("STATE_CLOSED"), SWOOLE_REDIS_STATE_CLOSED);
}

static PHP_METHOD(swoole_redis, __construct)
{
    zval *zset = NULL;
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z", &zset) == FAILURE)
    {
        RETURN_FALSE;
    }

    swRedisClient *redis = emalloc(sizeof(swRedisClient));
    bzero(redis, sizeof(swRedisClient));

    redis->object = getThis();
    redis->timeout = SW_REDIS_CONNECT_TIMEOUT;
    redis->database = -1;

    if (zset && ZVAL_IS_ARRAY(zset))
    {
        php_swoole_array_separate(zset);
        zend_update_property(swoole_redis_class_entry_ptr, getThis(), ZEND_STRL("setting"), zset);
        zval_ptr_dtor(zset);

        HashTable *vht;
        zval *ztmp;
        vht = Z_ARRVAL_P(zset);
        /**
         * timeout
         */
        if (php_swoole_array_get_value(vht, "timeout", ztmp))
        {
            convert_to_double(ztmp);
            redis->timeout = (double) Z_DVAL_P(ztmp);
        }
        /**
         * password
         */
        if (php_swoole_array_get_value(vht, "password", ztmp))
        {
            convert_to_string(ztmp);
            if (Z_STRLEN_P(ztmp) >= 1 << 8)
            {
                swoole_php_fatal_error(E_WARNING, "redis password is too long.");
            }
            else if (Z_STRLEN_P(ztmp) > 0)
            {
                redis->password = <PASSWORD>(Z_STRVAL_P(ztmp));
                redis->password_len = Z_STRLEN_P(ztmp);
            }
        }
        /**
         * database
         */
        if (php_swoole_array_get_value(vht, "database", ztmp))
        {
            convert_to_long(ztmp);
            if (Z_LVAL_P(ztmp) > 1 << 8)
            {
                swoole_php_fatal_error(E_WARNING, "redis database number is too big.");
            }
            else
            {
                redis->database = (int8_t) Z_LVAL_P(ztmp);
            }
        }
    }

    sw_copy_to_stack(redis->object, redis->_object);
    swoole_set_object(getThis(), redis);
}

static PHP_METHOD(swoole_redis, on)
{
    char *name;
    size_t len;
    zval *cb;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name, &len, &cb) == FAILURE)
    {
        RETURN_FALSE;
    }

    swRedisClient *redis = swoole_get_object(getThis());
    if (redis->context != NULL)
    {
        swoole_php_fatal_error(E_WARNING, "Must be called before connecting.");
        RETURN_FALSE;
    }

    if (strncasecmp("close", name, len) == 0)
    {
        zend_update_property(swoole_redis_class_entry_ptr, getThis(), ZEND_STRL("onClose"), cb);
    }
    else if (strncasecmp("message", name, len) == 0)
    {
        zend_update_property(swoole_redis_class_entry_ptr, getThis(), ZEND_STRL("onMessage"), cb);
        redis->message_callback = sw_zend_read_property(swoole_redis_class_entry_ptr, getThis(), ZEND_STRL("onMessage"), 0);
        sw_copy_to_stack(redis->message_callback, redis->_message_callback);

        redis->subscribe = 1;
    }
    else
    {
        swoole_php_error(E_WARNING, "Unknown event type[%s]", name);
        RETURN_FALSE;
    }
    RETURN_TRUE;
}

static PHP_METHOD(swoole_redis, connect)
{
    char *host;
    size_t host_len;
    long port;
    zval *callback;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &host, &host_len, &port, &callback) == FAILURE)
    {
        RETURN_FALSE;
    }

    if (host_len <= 0)
    {
        swoole_php_error(E_WARNING, "redis server host is empty.");
        RETURN_FALSE;
    }

    swRedisClient *redis = swoole_get_object(getThis());
    redisAsyncContext *context;

    if (strncasecmp(host, ZEND_STRL("unix:/")) == 0)
    {
        context = redisAsyncConnectUnix(host + 5);
    }
    else
    {
        if (port <= 1 || port > 65535)
        {
            swoole_php_error(E_WARNING, "redis server port is invalid.");
            RETURN_FALSE;
        }
        context = redisAsyncConnect(host, (int) port);
    }

    if (context == NULL)
    {
        swoole_php_error(E_WARNING, "redisAsyncConnect() failed.");
        RETURN_FALSE;
    }

    if (context->err)
    {
        redisAsyncFree(context);
        swoole_php_error(E_WARNING, "failed to connect to the redis-server[%s:%d], Erorr: %s[%d]", host, (int) port, context->errstr, context->err);
        RETURN_FALSE;
    }

    php_swoole_check_reactor();
    if (!swReactor_handle_isset(SwooleG.main_reactor, PHP_SWOOLE_FD_REDIS))
{'fraction_non_alphanumeric': 0.12270839451273231, 'fraction_numerical': 0.006460544531610522, 'mean_word_length': 3.2881813275769023, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6150103', 'n_tokens_mistral': 8929, 'n_tokens_neox': 8678, 'n_words': 1418}