Skip to content

Commit 6e13e84

Browse files
author
ace411
committed
feat: add writev() function
1 parent 2b13c87 commit 6e13e84

File tree

7 files changed

+155
-7
lines changed

7 files changed

+155
-7
lines changed

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class Mrloop
5555
callable $callback,
5656
): void
5757
public tcpServer(int $port, callable $callback): void
58+
public writev(int $fd, string $message): void
5859
public static parseHttpRequest(string $request, int $headerlimit = 100): iterable
5960
public static parseHttpResponse(string $response, int $headerlimit = 100): iterable
6061
public addTimer(float $interval, callable $callback): void
@@ -69,6 +70,7 @@ class Mrloop
6970
- [`Mrloop::addReadStream`](#mrloopaddreadstream)
7071
- [`Mrloop::addWriteStream`](#mrloopaddwritestream)
7172
- [`Mrloop::tcpServer`](#mrlooptcpserver)
73+
- [`Mrloop::writev`](#mrloopwritev)
7274
- [`Mrloop::parseHttpRequest`](#mrloopparsehttprequest)
7375
- [`Mrloop::parseHttpResponse`](#mrloopparsehttpresponse)
7476
- [`Mrloop::addTimer`](#mrloopaddtimer)
@@ -242,6 +244,7 @@ Instantiates a simple TCP server.
242244
- **client** (iterable) - An array containing client socket information.
243245
- **client_addr** (string) - The client IP address.
244246
- **client_port** (integer) - The client socket port.
247+
- **client_fd** (integer) - The client socket file descriptor.
245248

246249
**Return value(s)**
247250

@@ -282,6 +285,60 @@ The example above will produce output similar to that in the snippet to follow.
282285
283286
```
284287

288+
### `Mrloop::writev`
289+
290+
```php
291+
public Mrloop::writev(int $fd, string $contents): void
292+
```
293+
294+
Performs vectorized non-blocking write operation on a specified file descriptor.
295+
296+
**Parameter(s)**
297+
298+
- **fd** (integer) - The file descriptor to write to.
299+
- **contents** (string) - The arbitrary contents to write.
300+
301+
**Return value(s)**
302+
303+
The parser will throw an exception in the event that an invalid file descriptor is encountered and will not return anything otherwise.
304+
305+
```php
306+
use ringphp\Mrloop;
307+
308+
$loop = Mrloop::init();
309+
310+
$loop->tcpServer(
311+
8080,
312+
function (string $message, iterable $client) use ($loop) {
313+
[
314+
'client_addr' => $addr,
315+
'client_port' => $port,
316+
'client_fd' => $fd,
317+
] = $client;
318+
319+
$loop->writev(
320+
$fd,
321+
\sprintf(
322+
"Hello, %s:%d\r\n",
323+
$addr,
324+
$port,
325+
),
326+
);
327+
},
328+
);
329+
330+
echo "Listening on port 8080\n";
331+
332+
$loop->run();
333+
```
334+
335+
The example above will produce output similar to that in the snippet to follow.
336+
337+
```
338+
Listening on port 8080
339+
340+
```
341+
285342
### `Mrloop::parseHttpRequest`
286343

287344
```php

mrloop_arginfo.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ ZEND_ARG_TYPE_INFO(0, contents, IS_STRING, 0)
5050
ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0)
5151
ZEND_END_ARG_INFO()
5252

53+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Mrloop_writev, 0, 0, 2)
54+
ZEND_ARG_TYPE_INFO(0, fd, IS_LONG, 0)
55+
ZEND_ARG_TYPE_INFO(0, contents, IS_STRING, 0)
56+
ZEND_END_ARG_INFO()
57+
5358
ZEND_METHOD(Mrloop, init);
5459
ZEND_METHOD(Mrloop, stop);
5560
ZEND_METHOD(Mrloop, run);
@@ -61,6 +66,7 @@ ZEND_METHOD(Mrloop, addSignal);
6166
ZEND_METHOD(Mrloop, addReadStream);
6267
ZEND_METHOD(Mrloop, addWriteStream);
6368
ZEND_METHOD(Mrloop, parseHttpResponse);
69+
ZEND_METHOD(Mrloop, writev);
6470

6571
static const zend_function_entry class_Mrloop_methods[] = {
6672
PHP_ME(Mrloop, init, arginfo_class_Mrloop_init, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
@@ -74,4 +80,5 @@ static const zend_function_entry class_Mrloop_methods[] = {
7480
PHP_ME(Mrloop, addReadStream, arginfo_class_Mrloop_addReadStream, ZEND_ACC_PUBLIC)
7581
PHP_ME(Mrloop, addWriteStream, arginfo_class_Mrloop_addWriteStream, ZEND_ACC_PUBLIC)
7682
PHP_ME(Mrloop, parseHttpResponse, arginfo_class_Mrloop_parseHttpResponse, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
77-
PHP_FE_END};
83+
PHP_ME(Mrloop, writev, arginfo_class_Mrloop_writev, ZEND_ACC_PUBLIC)
84+
PHP_FE_END};

php_mrloop.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ PHP_METHOD(Mrloop, addWriteStream)
8484
}
8585
/* }}} */
8686

87+
/* {{{ proto void Mrloop::writev( int fd [, string contents ] ) */
88+
PHP_METHOD(Mrloop, writev)
89+
{
90+
php_mrloop_writev(INTERNAL_FUNCTION_PARAM_PASSTHRU);
91+
}
92+
/* }}} */
93+
8794
/* {{{ PHP_MINIT_FUNCTION */
8895
PHP_MINIT_FUNCTION(mrloop)
8996
{

src/loop.c

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,25 +261,29 @@ static int php_mrloop_tcp_server_recv(void *conn, int fd, ssize_t nbytes, char *
261261
array_init(&args[1]);
262262
add_assoc_string(&args[1], "client_addr", (char *)client->addr);
263263
add_assoc_long(&args[1], "client_port", client->port);
264+
add_assoc_long(&args[1], "client_fd", client->fd);
264265

265266
MRLOOP_G(tcp_cb)->fci.retval = &result;
266267
MRLOOP_G(tcp_cb)->fci.param_count = 2;
267268
MRLOOP_G(tcp_cb)->fci.params = args;
268269

269-
if (zend_call_function(&MRLOOP_G(tcp_cb)->fci, &MRLOOP_G(tcp_cb)->fci_cache) == FAILURE ||
270-
Z_TYPE(result) != IS_STRING)
270+
if (zend_call_function(&MRLOOP_G(tcp_cb)->fci, &MRLOOP_G(tcp_cb)->fci_cache) == FAILURE)
271271
{
272272
PHP_MRLOOP_THROW("There is an error in your callback");
273273
zval_ptr_dtor(&result);
274274

275275
return 1;
276276
}
277277

278-
client->iov.iov_base = Z_STRVAL(result);
279-
client->iov.iov_len = Z_STRLEN(result);
278+
if (Z_TYPE(result) == IS_STRING)
279+
{
280+
client->iov.iov_base = Z_STRVAL(result);
281+
client->iov.iov_len = Z_STRLEN(result);
282+
283+
mr_writev(loop, client->fd, &(client->iov), 1);
284+
mr_flush(loop);
285+
}
280286

281-
mr_writev(loop, client->fd, &(client->iov), 1);
282-
mr_flush(loop);
283287
zval_ptr_dtor(&result);
284288

285289
return 1;
@@ -629,3 +633,36 @@ static void php_mrloop_add_write_stream(INTERNAL_FUNCTION_PARAMETERS)
629633

630634
return;
631635
}
636+
static void php_mrloop_writev(INTERNAL_FUNCTION_PARAMETERS)
637+
{
638+
zend_long fd;
639+
zend_string *contents;
640+
php_iovec_t iov;
641+
php_mrloop_t *this;
642+
zval *obj;
643+
size_t nbytes;
644+
645+
obj = getThis();
646+
647+
ZEND_PARSE_PARAMETERS_START(2, 2)
648+
Z_PARAM_LONG(fd)
649+
Z_PARAM_STR(contents)
650+
ZEND_PARSE_PARAMETERS_END();
651+
652+
this = PHP_MRLOOP_OBJ(obj);
653+
654+
if (fcntl((int)fd, F_GETFD) < 0)
655+
{
656+
PHP_MRLOOP_THROW("Detected invalid file descriptor");
657+
mr_stop(this->loop);
658+
659+
return;
660+
}
661+
662+
nbytes = ZSTR_LEN(contents);
663+
iov.iov_base = ZSTR_VAL(contents);
664+
iov.iov_len = nbytes;
665+
666+
mr_writev(this->loop, fd, &iov, 1);
667+
mr_flush(this->loop);
668+
}

src/loop.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ static void php_mrloop_add_signal(INTERNAL_FUNCTION_PARAMETERS);
156156
static void php_mrloop_add_read_stream(INTERNAL_FUNCTION_PARAMETERS);
157157
/* funnels file descriptor in writable stream into event loop and thence executes a non-blocking write operation */
158158
static void php_mrloop_add_write_stream(INTERNAL_FUNCTION_PARAMETERS);
159+
/* performs vectorized non-blocking write operation on a specified file descriptor */
160+
static void php_mrloop_writev(INTERNAL_FUNCTION_PARAMETERS);
159161

160162
zend_class_entry *php_mrloop_ce, *php_mrloop_exception_ce;
161163

tests/011.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
writev() performs vectorized non-blocking write operation on file descriptor
3+
--FILE--
4+
<?php
5+
6+
use ringphp\Mrloop;
7+
8+
$loop = Mrloop::init();
9+
10+
$loop->writev(1, "Hello, user");
11+
$loop->stop();
12+
13+
$loop->run();
14+
15+
?>
16+
--EXPECT--
17+
Hello, user

tests/012.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
writev() throws exception on detection of invalid file descriptor
3+
--FILE--
4+
<?php
5+
6+
use ringphp\Mrloop;
7+
8+
$loop = Mrloop::init();
9+
10+
try {
11+
$loop->writev(987874, "Hello, user");
12+
} catch (\Throwable $err) {
13+
$loop->writev(1, $err->getMessage());
14+
}
15+
$loop->stop();
16+
17+
$loop->run();
18+
19+
?>
20+
--EXPECT--
21+
Detected invalid file descriptor

0 commit comments

Comments
 (0)