Skip to content

Commit

Permalink
Fixed #5561
Browse files Browse the repository at this point in the history
  • Loading branch information
matyhtf committed Nov 18, 2024
1 parent f975ae7 commit 7ce419c
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 39 deletions.
84 changes: 47 additions & 37 deletions ext-src/swoole_redis_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ zend_object_handlers swoole_redis_server_handlers;

static SW_THREAD_LOCAL std::unordered_map<std::string, zend::Callable *> redis_handlers;

static bool redis_response_format(String *buf, zend_long type, zval *value);

SW_EXTERN_C_BEGIN
static PHP_METHOD(swoole_redis_server, setHandler);
static PHP_METHOD(swoole_redis_server, getHandler);
Expand Down Expand Up @@ -257,31 +259,34 @@ static PHP_METHOD(swoole_redis_server, getHandler) {
RETURN_ZVAL(handler, 1, 0);
}

static PHP_METHOD(swoole_redis_server, format) {
zend_long type;
zval *value = nullptr;

ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_LONG(type)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(value)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

char small_buf[1024];
size_t length;
auto buf = std::shared_ptr<String>(swoole::make_string(1024, sw_zend_string_allocator()));
static void redis_response_format_array_item(String *buf, zval *item) {
switch (Z_TYPE_P(item)) {
case IS_LONG:
case IS_FALSE:
case IS_TRUE:
redis_response_format(buf, Redis::REPLY_INT, item);
break;
case IS_ARRAY:
if (zend_array_is_list(Z_ARRVAL_P(item))) {
redis_response_format(buf, Redis::REPLY_SET, item);
} else {
redis_response_format(buf, Redis::REPLY_MAP, item);
}
break;
default:
redis_response_format(buf, Redis::REPLY_STRING, item);
break;
}
}

static bool redis_response_format(String *buf, zend_long type, zval *value) {
if (type == Redis::REPLY_NIL) {
RETURN_STRINGL(SW_REDIS_RETURN_NIL, sizeof(SW_REDIS_RETURN_NIL) - 1);
buf->append(SW_STRL(SW_REDIS_RETURN_NIL));
} else if (type == Redis::REPLY_ERROR || type == Redis::REPLY_STATUS) {
char flag = type == Redis::REPLY_ERROR ? '-' : '+';
const char *default_message = type == Redis::REPLY_ERROR ? "ERR" : "OK";
if (value) {
zend::String str_value(value);
if (str_value.len() > sizeof(small_buf) - 8) {
php_swoole_fatal_error(E_WARNING, "value is too long, max size is %lu", sizeof(small_buf) - 8);
RETURN_FALSE;
}
SW_STRING_FORMAT(buf, "%c%.*s\r\n", flag, (int) str_value.len(), str_value.val());
} else {
SW_STRING_FORMAT(buf, "%c%s\r\n", flag, default_message);
Expand All @@ -295,12 +300,12 @@ static PHP_METHOD(swoole_redis_server, format) {
if (!value) {
_no_value:
php_swoole_fatal_error(E_WARNING, "require more parameters");
RETURN_FALSE;
return false;
}
zend::String str_value(value);
if (str_value.len() > SW_REDIS_MAX_STRING_SIZE || str_value.len() < 1) {
php_swoole_fatal_error(E_WARNING, "invalid string size");
RETURN_FALSE;
return false;
}
SW_STRING_FORMAT(buf, "$%zu\r\n", str_value.len());
buf->append(str_value.val(), str_value.len());
Expand All @@ -316,11 +321,7 @@ static PHP_METHOD(swoole_redis_server, format) {

zval *item;
SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(value), item)
zend::String str_value(item);
length = sw_snprintf(small_buf, sizeof(small_buf), "$%zu\r\n", str_value.len());
buf->append(small_buf, length);
buf->append(str_value.val(), str_value.len());
buf->append(SW_CRLF, SW_CRLF_LEN);
redis_response_format_array_item(buf, item);
SW_HASHTABLE_FOREACH_END();
} else if (type == Redis::REPLY_MAP) {
if (!value) {
Expand All @@ -340,21 +341,30 @@ static PHP_METHOD(swoole_redis_server, format) {
if (key == nullptr || keylen == 0) {
continue;
}

zend::String str_value(item);
if (str_value.len() > sizeof(small_buf) - 16) {
php_swoole_fatal_error(E_WARNING, "value is too long, max size is %lu", sizeof(small_buf) - 16);
RETURN_FALSE;
}

length = sw_snprintf(small_buf, sizeof(small_buf), "$%d\r\n%s\r\n$%zu\r\n", keylen, key, str_value.len());
buf->append(small_buf, length);
buf->append(str_value.val(), str_value.len());
buf->append(SW_CRLF, SW_CRLF_LEN);
SW_STRING_FORMAT(buf, "$%d\r\n%.*s\r\n", keylen, keylen, key);
redis_response_format_array_item(buf, item);
(void) keytype;
SW_HASHTABLE_FOREACH_END();
} else {
php_swoole_error(E_WARNING, "Unknown type[" ZEND_LONG_FMT "]", type);
php_swoole_error(E_WARNING, "Unknown type[%d]", (int) type);
return false;
}

return true;
}

static PHP_METHOD(swoole_redis_server, format) {
zend_long type;
zval *value = nullptr;

ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_LONG(type)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(value)
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);

auto buf = std::shared_ptr<String>(swoole::make_string(1024, sw_zend_string_allocator()));
if (!redis_response_format(buf.get(), type, value)) {
RETURN_FALSE;
}

Expand Down
3 changes: 2 additions & 1 deletion tests/include/lib/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"symfony/http-client": "^5.3",
"nyholm/psr7": "^1.4",
"friendsofphp/php-cs-fixer": "^3.3",
"php-http/message-factory": "^1.1"
"php-http/message-factory": "^1.1",
"predis/predis": "^2.2"
},
"config": {
"allow-plugins": {
Expand Down
2 changes: 1 addition & 1 deletion tests/swoole_redis_server/format.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ echo Server::format(Server::STATUS, "SUCCESS");
echo Server::format(Server::INT, 1000);
echo Server::format(Server::STRING, "hello swoole");
echo Server::format(Server::SET, ["php", "is", "best"]);
echo Server::format(Server::MAP, ["php" => 99, "java" => 88, "c++" => '666']);
echo Server::format(Server::MAP, ["php" => '99', "java" => '88', "c++" => '666']);
?>
--EXPECT--
-ERR
Expand Down
69 changes: 69 additions & 0 deletions tests/swoole_redis_server/nested_map.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
--TEST--
swoole_redis_server: nested map
--SKIPIF--
<?php require __DIR__ . '/../include/skipif.inc'; ?>
--FILE--
<?php
require __DIR__ . '/../include/bootstrap.php';

use Swoole\Redis\Server;

define("UUID", uniqid());
define("NUMBER", random_int(1, 1000000));

$pm = new ProcessManager;
$pm->parentFunc = function ($pid) use ($pm) {
$redis = new Predis\Client('tcp://127.0.0.1:' . $pm->getFreePort());
$map = $redis->get('map');
Assert::notEmpty($map);
Assert::eq($map[0], 'uuid');
Assert::eq($map[1], UUID);
Assert::isArray($map[3]);
Assert::eq($map[5], NUMBER);
Assert::eq($map[3][1], NUMBER);
Assert::eq($map[3][2], UUID);

$set = $redis->get('set');
Assert::notEmpty($set);
Assert::eq($set[0], UUID);
Assert::isArray($set[1]);
Assert::eq($set[2], NUMBER);
Assert::eq($set[1][1], NUMBER);
Assert::eq($set[1][3], UUID);

$pm->kill();
};

$pm->childFunc = function () use ($pm) {
$server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE);
$server->setHandler('GET', function ($fd, $data) use ($server) {
$key = $data[0];
if ($key == 'map') {
$out = Server::format(Server::MAP, [
'uuid' => UUID,
'list' => [1, NUMBER, UUID],
'number' => NUMBER,
]);
} elseif ($key == 'set') {
$out = Server::format(Server::SET, [
UUID,
['number' => NUMBER, 'uuid' => UUID],
NUMBER,
]);
} else {
$out = Server::format(Server::ERROR, 'bad key');
}
$server->send($fd, $out);
});

$server->on('WorkerStart', function ($server) use ($pm) {
$pm->wakeup();
});

$server->start();
};

$pm->childFirst();
$pm->run();
?>
--EXPECT--

0 comments on commit 7ce419c

Please sign in to comment.