PHP 8.1.28 Released!

示例

示例 #1 简单的 HTTP 客户端

<?php
// Read callback
function readcb($bev, $base) {
//$input = $bev->input; //$bev->getInput();

//$pos = $input->search("TTP");
$pos = $bev->input->search("TTP");

while ((
$n = $bev->input->remove($buf, 1024)) > 0) {
echo
$buf;
}
}

// Event callback
function eventcb($bev, $events, $base) {
if (
$events & EventBufferEvent::CONNECTED) {
echo
"Connected.\n";
} elseif (
$events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if (
$events & EventBufferEvent::ERROR) {
echo
"DNS error: ", $bev->getDnsErrorString(), PHP_EOL;
}

echo
"Closing\n";
$base->exit();
exit(
"Done\n");
}
}

if (
$argc != 3) {
echo <<<EOS
Trivial HTTP 0.x client
Syntax: php
{$argv[0]} [hostname] [resource]
Example: php
{$argv[0]} www.google.com /

EOS;
exit();
}

$base = new EventBase();

$dns_base = new EventDnsBase($base, TRUE); // We'll use async DNS resolving
if (!$dns_base) {
exit(
"Failed to init DNS Base\n");
}

$bev = new EventBufferEvent($base, /* use internal socket */ NULL,
EventBufferEvent::OPT_CLOSE_ON_FREE | EventBufferEvent::OPT_DEFER_CALLBACKS,
"readcb", /* writecb */ NULL, "eventcb"
);
if (!
$bev) {
exit(
"Failed creating bufferevent socket\n");
}

//$bev->setCallbacks("readcb", /* writecb */ NULL, "eventcb", $base);
$bev->enable(Event::READ | Event::WRITE);

$output = $bev->output; //$bev->getOutput();
if (!$output->add(
"GET {$argv[2]} HTTP/1.0\r\n".
"Host: {$argv[1]}\r\n".
"Connection: Close\r\n\r\n"
)) {
exit(
"Failed adding request to output buffer\n");
}

if (!
$bev->connectHost($dns_base, $argv[1], 80, EventUtil::AF_UNSPEC)) {
exit(
"Can't connect to host {$argv[1]}\n");
}

$base->dispatch();
?>

以上示例的输出类似于:

Connected.
HTTP/1.1 301 Moved Permanently
Date: Fri, 01 Mar 2013 18:47:48 GMT
Location: http://www.google.co.uk/
Content-Type: text/html; charset=UTF-8
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 221
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Age: 133438
Expires: Sat, 30 Mar 2013 05:39:28 GMT
Connection: close

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.co.uk/">here</A>.
</BODY></HTML>
Closing
Done

示例 #2 使用异步 DNS 解析器的 HTTP 客户端

<?php
/*
* 1. 借助 EventBufferEvent::connect()
* 连接到 127.0.0.1 的 80 端口。
*
* 2. 使用输出缓冲通过 HTTP/1.0 请求 /index.cphp。
*
* 3. 异步读取响应并将其打印到标准输出。
*/

// Read callback
function readcb($bev, $base) {
$input = $bev->getInput();

while ((
$n = $input->remove($buf, 1024)) > 0) {
echo
$buf;
}
}

// Event callback
function eventcb($bev, $events, $base) {
if (
$events & EventBufferEvent::CONNECTED) {
echo
"Connected.\n";
} elseif (
$events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if (
$events & EventBufferEvent::ERROR) {
echo
"DNS error: ", $bev->getDnsErrorString(), PHP_EOL;
}

echo
"Closing\n";
$base->exit();
exit(
"Done\n");
}
}

$base = new EventBase();

echo
"step 1\n";
$bev = new EventBufferEvent($base, /* use internal socket */ NULL,
EventBufferEvent::OPT_CLOSE_ON_FREE | EventBufferEvent::OPT_DEFER_CALLBACKS);
if (!
$bev) {
exit(
"Failed creating bufferevent socket\n");
}

echo
"step 2\n";
$bev->setCallbacks("readcb", /* writecb */ NULL, "eventcb", $base);
$bev->enable(Event::READ | Event::WRITE);

echo
"step 3\n";
// 发送请求
$output = $bev->getOutput();
if (!
$output->add(
"GET /index.cphp HTTP/1.0\r\n".
"Connection: Close\r\n\r\n"
)) {
exit(
"Failed adding request to output buffer\n");
}

/* 同步连接到主机。
知道 IP,所以不需要解析 DNS。 */
if (!$bev->connect("127.0.0.1:80")) {
exit(
"Can't connect to host\n");
}

// Dispatch pending events
$base->dispatch();
?>

示例 #3 Echo 服务器

<?php
/*
* 基于 libevent 连接监听器的简单 echo 服务器。
*
* 用法:
* 1) 在一个终端窗口中运行:
*
* $ php listener.php 9881
*
* 2) 在另一个终端窗口中打开连接,例如:
*
* $ nc 127.0.0.1 9881
*
* 3) 开始打字。服务器应该重复输入部分。
*/

class MyListenerConnection {
private
$bev, $base;

public function
__destruct() {
$this->bev->free();
}

public function
__construct($base, $fd) {
$this->base = $base;

$this->bev = new EventBufferEvent($base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE);

$this->bev->setCallbacks(array($this, "echoReadCallback"), NULL,
array(
$this, "echoEventCallback"), NULL);

if (!
$this->bev->enable(Event::READ)) {
echo
"Failed to enable READ\n";
return;
}
}

public function
echoReadCallback($bev, $ctx) {
// 复制输入缓冲中的所有数据到输出缓冲

// 方式 #1
$bev->output->addBuffer($bev->input);

/* 方式 #2 */
/*
$input = $bev->getInput();
$output = $bev->getOutput();
$output->addBuffer($input);
*/
}

public function
echoEventCallback($bev, $events, $ctx) {
if (
$events & EventBufferEvent::ERROR) {
echo
"Error from bufferevent\n";
}

if (
$events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
//$bev->free();
$this->__destruct();
}
}
}

class
MyListener {
public
$base,
$listener,
$socket;
private
$conn = array();

public function
__destruct() {
foreach (
$this->conn as &$c) $c = NULL;
}

public function
__construct($port) {
$this->base = new EventBase();
if (!
$this->base) {
echo
"Couldn't open event base";
exit(
1);
}

// 方式 #1
/*
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!socket_bind($this->socket, '0.0.0.0', $port)) {
echo "Unable to bind socket\n";
exit(1);
}
$this->listener = new EventListener($this->base,
array($this, "acceptConnCallback"), $this->base,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,
-1, $this->socket);
*/

// 方式 #2
$this->listener = new EventListener($this->base,
array(
$this, "acceptConnCallback"), $this->base,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, -1,
"0.0.0.0:$port");

if (!
$this->listener) {
echo
"Couldn't create listener";
exit(
1);
}

$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}

public function
dispatch() {
$this->base->dispatch();
}

// 当 $bev 上有数据要读取时调用此回调
public function acceptConnCallback($listener, $fd, $address, $ctx) {
// 建立了新连接!为其设置 bufferevent。 */
$base = $this->base;
$this->conn[] = new MyListenerConnection($base, $fd);
}

public function
accept_error_cb($listener, $ctx) {
$base = $this->base;

fprintf(STDERR, "Got an error %d (%s) on the listener. "
."Shutting down.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());

$base->exit(NULL);
}
}

$port = 9808;

if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Invalid port");
}

$l = new MyListener($port);
$l->dispatch();
?>

示例 #4 SSL echo 服务器

<?php
/*
* SSL echo 服务器
*
* 测试:
* 1) 运行:
* $ php examples/ssl-echo-server/server.php 9998
*
* 2) 在另一个终端窗口中运行:
* $ socat - SSL:127.0.0.1:9998,verify=1,cafile=examples/ssl-echo-server/cert.pem
*/

class MySslEchoServer {
public
$port,
$base,
$bev,
$listener,
$ctx;

function
__construct ($port, $host = "127.0.0.1") {
$this->port = $port;
$this->ctx = $this->init_ssl();
if (!
$this->ctx) {
exit(
"Failed creating SSL context\n");
}

$this->base = new EventBase();
if (!
$this->base) {
exit(
"Couldn't open event base\n");
}

$this->listener = new EventListener($this->base,
array(
$this, "ssl_accept_cb"), $this->ctx,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,
-
1, "$host:$port");
if (!
$this->listener) {
exit(
"Couldn't create listener\n");
}

$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}
function
dispatch() {
$this->base->dispatch();
}

// 当 $bev 上有数据要读取时,会调用此回调。
function ssl_read_cb($bev, $ctx) {
$in = $bev->input; //$bev->getInput();

printf("Received %zu bytes\n", $in->length);
printf("----- data ----\n");
printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));

$bev->writeBuffer($in);
}

// 当事件监听器上发生某些事件时,会调用此回调,
// 例如连接已关闭,或发生错误
function ssl_event_cb($bev, $events, $ctx) {
if (
$events & EventBufferEvent::ERROR) {
// Fetch errors from the SSL error stack
while ($err = $bev->sslError()) {
fprintf(STDERR, "Bufferevent error %s.\n", $err);
}
}

if (
$events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
$bev->free();
}
}

// 当客户端接受新连接时调用此回调
function ssl_accept_cb($listener, $fd, $address, $ctx) {
// We got a new connection! Set up a bufferevent for it.
$this->bev = EventBufferEvent::sslSocket($this->base, $fd, $this->ctx,
EventBufferEvent::SSL_ACCEPTING, EventBufferEvent::OPT_CLOSE_ON_FREE);

if (!
$this->bev) {
echo
"Failed creating ssl buffer\n";
$this->base->exit(NULL);
exit(
1);
}

$this->bev->enable(Event::READ);
$this->bev->setCallbacks(array($this, "ssl_read_cb"), NULL,
array(
$this, "ssl_event_cb"), NULL);
}

// 当无法为客户端建立新连接时调用此回调
function accept_error_cb($listener, $ctx) {
fprintf(STDERR, "Got an error %d (%s) on the listener. "
."Shutting down.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());

$this->base->exit(NULL);
}

// 初始化 SSL 结构,创建 EventSslContext
// 视需要使用自签名证书
function init_ssl() {
// *必须有* entropy。否则加密没有意义。
if (!EventUtil::sslRandPoll()) {
exit(
"EventUtil::sslRandPoll failed\n");
}

$local_cert = __DIR__."/cert.pem";
$local_pk = __DIR__."/privkey.pem";

if (!
file_exists($local_cert) || !file_exists($local_pk)) {
echo
"Couldn't read $local_cert or $local_pk file. To generate a key\n",
"and self-signed certificate, run:\n",
" openssl genrsa -out $local_pk 2048\n",
" openssl req -new -key $local_pk -out cert.req\n",
" openssl x509 -req -days 365 -in cert.req -signkey $local_pk -out $local_cert\n";

return
FALSE;
}

$ctx = new EventSslContext(EventSslContext::SSLv3_SERVER_METHOD, array (
EventSslContext::OPT_LOCAL_CERT => $local_cert,
EventSslContext::OPT_LOCAL_PK => $local_pk,
//EventSslContext::OPT_PASSPHRASE => "echo server",
EventSslContext::OPT_VERIFY_PEER => true,
EventSslContext::OPT_ALLOW_SELF_SIGNED => false,
));

return
$ctx;
}
}

// 允许覆盖端口
$port = 9999;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Invalid port\n");
}


$l = new MySslEchoServer($port);
$l->dispatch();
?>

示例 #5 信号处理程序

<?php
/*
在终端窗口中启动:

$ php examples/signal.php

在另一个终端窗口中找出 pid 并发送 SIGTERM,例如:

$ ps aux | grep examp
ruslan 3976 0.2 0.0 139896 11256 pts/1 S+ 10:25 0:00 php examples/signal.php
ruslan 3978 0.0 0.0 9572 864 pts/2 S+ 10:26 0:00 grep --color=auto examp
$ kill -TERM 3976

在第一个终端窗口中,应该捕获以下内容:

Caught signal 15
*/
class MyEventSignal {
private
$base;

function
__construct($base) {
$this->base = $base;
}

function
eventSighandler($no, $c) {
echo
"Caught signal $no\n";
event_base_loopexit($c->base);
}
}

$base = event_base_new();
$c = new MyEventSignal($base);
$no = SIGTERM;
$ev = evsignal_new($base, $no, array($c,'eventSighandler'), $c);

evsignal_add($ev);

event_base_loop($base);
?>

示例 #6 使用 libevent 的循环处理 `eio` 扩展的请求

<?php
// eio_nop() 的回调
function my_nop_cb($d, $r) {
echo
"step 6\n";
}

$dir = "/tmp/abc-eio-temp";
if (
file_exists($dir)) {
rmdir($dir);
}

echo
"step 1\n";

$base = new EventBase();

echo
"step 2\n";

eio_init();

eio_mkdir($dir, 0750, EIO_PRI_DEFAULT, "my_nop_cb");

$event = new Event($base, eio_get_event_stream(),
Event::READ | Event::PERSIST, function ($fd, $events, $base) {
echo
"step 5\n";

while (
eio_nreqs()) {
eio_poll();
}

$base->stop();
},
$base);

echo
"step 3\n";

$event->add();

echo
"step 4\n";

$base->dispatch();

echo
"Done\n";
?>

示例 #7 杂项

<?php
/* {{{ Config & supported stuff */
echo "Supported methods:\n";
foreach (
Event::getSupportedMethods() as $m) {
echo
$m, PHP_EOL;
}

// Avoiding "select" method
$cfg = new EventConfig();
if (
$cfg->avoidMethod("select")) {
echo
"'select' method avoided\n";
}

// Create event_base associated with the config
$base = new EventBase($cfg);
echo
"Event method used: ", $base->getMethod(), PHP_EOL;

echo
"Features:\n";
$features = $base->getFeatures();
(
$features & EventConfig::FEATURE_ET) and print "ET - edge-triggered IO\n";
(
$features & EventConfig::FEATURE_O1) and print "O1 - O(1) operation for adding/deleting events\n";
(
$features & EventConfig::FEATURE_FDS) and print "FDS - arbitrary file descriptor types, and not just sockets\n";

// Require FDS feature
if ($cfg->requireFeatures(EventConfig::FEATURE_FDS)) {
echo
"FDS feature is now required\n";

$base = new EventBase($cfg);
(
$base->getFeatures() & EventConfig::FEATURE_FDS)
and print
"FDS - arbitrary file descriptor types, and not just sockets\n";
}
/* }}} */

/* {{{ Base */
$base = new EventBase();
$event = new Event($base, STDIN, Event::READ | Event::PERSIST, function ($fd, $events, $arg) {
static
$max_iterations = 0;

if (++
$max_iterations >= 5) {
/* exit after 5 iterations with timeout of 2.33 seconds */
echo "Stopping...\n";
$arg[0]->exit(2.33);
}

echo
fgets($fd);
}, array (&
$base));

$event->add();
$base->loop();
/* Base }}} */
?>

示例 #8 Simple HTTP server

<?php
/*
* Simple HTTP server.
*
* To test it:
* 1) Run it on a port of your choice, e.g.:
* $ php examples/http.php 8010
* 2) In another terminal connect to some address on this port
* and make GET or POST request(others are turned off here), e.g.:
* $ nc -t 127.0.0.1 8010
* POST /about HTTP/1.0
* Content-Type: text/plain
* Content-Length: 4
* Connection: close
* (press Enter)
*
* It will output
* a=12
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
*
* $ nc -t 127.0.0.1 8010
* GET /dump HTTP/1.0
* Content-Type: text/plain
* Content-Encoding: UTF-8
* Connection: close
* (press Enter)
*
* It will output:
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
* (press Enter)
*
* $ nc -t 127.0.0.1 8010
* GET /unknown HTTP/1.0
* Connection: close
*
* It will output:
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
*
* 3) See what the server outputs on the previous terminal window.
*/

function _http_dump($req, $data) {
static
$counter = 0;
static
$max_requests = 2;

if (++
$counter >= $max_requests) {
echo
"Counter reached max requests $max_requests. Exiting\n";
exit();
}

echo
__METHOD__, " called\n";
echo
"request:"; var_dump($req);
echo
"data:"; var_dump($data);

echo
"\n===== DUMP =====\n";
echo
"Command:", $req->getCommand(), PHP_EOL;
echo
"URI:", $req->getUri(), PHP_EOL;
echo
"Input headers:"; var_dump($req->getInputHeaders());
echo
"Output headers:"; var_dump($req->getOutputHeaders());

echo
"\n >> Sending reply ...";
$req->sendReply(200, "OK");
echo
"OK\n";

echo
"\n >> Reading input buffer ...\n";
$buf = $req->getInputBuffer();
while (
$s = $buf->readLine(EventBuffer::EOL_ANY)) {
echo
$s, PHP_EOL;
}
echo
"No more data in the buffer\n";
}

function
_http_about($req) {
echo
__METHOD__, PHP_EOL;
echo
"URI: ", $req->getUri(), PHP_EOL;
echo
"\n >> Sending reply ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

function
_http_default($req, $data) {
echo
__METHOD__, PHP_EOL;
echo
"URI: ", $req->getUri(), PHP_EOL;
echo
"\n >> Sending reply ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

$port = 8010;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Invalid port");
}

$base = new EventBase();
$http = new EventHttp($base);
$http->setAllowedMethods(EventHttpRequest::CMD_GET | EventHttpRequest::CMD_POST);

$http->setCallback("/dump", "_http_dump", array(4, 8));
$http->setCallback("/about", "_http_about");
$http->setDefaultCallback("_http_default", "custom data value");

$http->bind("0.0.0.0", 8010);
$base->loop();
?>

以上示例的输出类似于:

a=12
HTTP/1.0 200 OK
Content-Type: text/html; charset=ISO-8859-1
Connection: close

HTTP/1.0 200 OK
Content-Type: text/html; charset=ISO-8859-1
Connection: close
(press Enter)

HTTP/1.0 200 OK
Content-Type: text/html; charset=ISO-8859-1
Connection: close

示例 #9 Simple HTTPS server

<?php
/*
* Simple HTTPS server.
*
* 1) Run the server: `php examples/https.php 9999`
* 2) Test it: `php examples/ssl-connection.php 9999`
*/

function _http_dump($req, $data) {
static
$counter = 0;
static
$max_requests = 200;

if (++
$counter >= $max_requests) {
echo
"Counter reached max requests $max_requests. Exiting\n";
exit();
}

echo
__METHOD__, " called\n";
echo
"request:"; var_dump($req);
echo
"data:"; var_dump($data);

echo
"\n===== DUMP =====\n";
echo
"Command:", $req->getCommand(), PHP_EOL;
echo
"URI:", $req->getUri(), PHP_EOL;
echo
"Input headers:"; var_dump($req->getInputHeaders());
echo
"Output headers:"; var_dump($req->getOutputHeaders());

echo
"\n >> Sending reply ...";
$req->sendReply(200, "OK");
echo
"OK\n";

$buf = $req->getInputBuffer();
echo
"\n >> Reading input buffer (", $buf->length, ") ...\n";
while (
$s = $buf->read(1024)) {
echo
$s;
}
echo
"\nNo more data in the buffer\n";
}

function
_http_about($req) {
echo
__METHOD__, PHP_EOL;
echo
"URI: ", $req->getUri(), PHP_EOL;
echo
"\n >> Sending reply ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

function
_http_default($req, $data) {
echo
__METHOD__, PHP_EOL;
echo
"URI: ", $req->getUri(), PHP_EOL;
echo
"\n >> Sending reply ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

function
_http_400($req) {
$req->sendError(400);
}

function
_init_ssl() {
$local_cert = __DIR__."/ssl-echo-server/cert.pem";
$local_pk = __DIR__."/ssl-echo-server/privkey.pem";

$ctx = new EventSslContext(EventSslContext::SSLv3_SERVER_METHOD, array (
EventSslContext::OPT_LOCAL_CERT => $local_cert,
EventSslContext::OPT_LOCAL_PK => $local_pk,
//EventSslContext::OPT_PASSPHRASE => "test",
EventSslContext::OPT_ALLOW_SELF_SIGNED => true,
));

return
$ctx;
}

$port = 9999;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Invalid port");
}
$ip = '0.0.0.0';

$base = new EventBase();
$ctx = _init_ssl();
$http = new EventHttp($base, $ctx);
$http->setAllowedMethods(EventHttpRequest::CMD_GET | EventHttpRequest::CMD_POST);

$http->setCallback("/dump", "_http_dump", array(4, 8));
$http->setCallback("/about", "_http_about");
$http->setCallback("/err400", "_http_400");
$http->setDefaultCallback("_http_default", "custom data value");

$http->bind($ip, $port);
$base->dispatch();

示例 #10 OpenSSL connection

<?php
/*
* Sample OpenSSL client.
*
* Usage:
* 1) Launch a server, e.g.:
* $ php examples/https.php 9999
*
* 2) Launch the client in another terminal:
* $ php examples/ssl-connection.php 9999
*/

function _request_handler($req, $base) {
echo
__FUNCTION__, PHP_EOL;

if (
is_null($req)) {
echo
"Timed out\n";
} else {
$response_code = $req->getResponseCode();

if (
$response_code == 0) {
echo
"Connection refused\n";
} elseif (
$response_code != 200) {
echo
"Unexpected response: $response_code\n";
} else {
echo
"Success: $response_code\n";
$buf = $req->getInputBuffer();
echo
"Body:\n";
while (
$s = $buf->readLine(EventBuffer::EOL_ANY)) {
echo
$s, PHP_EOL;
}
}
}

$base->exit(NULL);
}

function
_init_ssl() {
$ctx = new EventSslContext(EventSslContext::SSLv3_CLIENT_METHOD, array ());

return
$ctx;
}


// Allow to override the port
$port = 9999;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Invalid port\n");
}
$host = '127.0.0.1';

$ctx = _init_ssl();
if (!
$ctx) {
trigger_error("Failed creating SSL context", E_USER_ERROR);
}

$base = new EventBase();
if (!
$base) {
trigger_error("Failed to initialize event base", E_USER_ERROR);
}

$conn = new EventHttpConnection($base, NULL, $host, $port, $ctx);
$conn->setTimeout(50);

$req = new EventHttpRequest("_request_handler", $base);
$req->addHeader("Host", $host, EventHttpRequest::OUTPUT_HEADER);
$buf = $req->getOutputBuffer();
$buf->add("<html>HTML TEST</html>");
//$req->addHeader("Content-Length", $buf->length, EventHttpRequest::OUTPUT_HEADER);
//$req->addHeader("Connection", "close", EventHttpRequest::OUTPUT_HEADER);
$conn->makeRequest($req, EventHttpRequest::CMD_POST, "/dump");

$base->dispatch();
echo
"END\n";
?>

示例 #11 EventHttpConnection::makeRequest() example

<?php
function _request_handler($req, $base) {
echo
__FUNCTION__, PHP_EOL;

if (
is_null($req)) {
echo
"Timed out\n";
} else {
$response_code = $req->getResponseCode();

if (
$response_code == 0) {
echo
"Connection refused\n";
} elseif (
$response_code != 200) {
echo
"Unexpected response: $response_code\n";
} else {
echo
"Success: $response_code\n";
$buf = $req->getInputBuffer();
echo
"Body:\n";
while (
$s = $buf->readLine(EventBuffer::EOL_ANY)) {
echo
$s, PHP_EOL;
}
}
}

$base->exit(NULL);
}

$address = "127.0.0.1";
$port = 80;

$base = new EventBase();
$conn = new EventHttpConnection($base, NULL, $address, $port);
$conn->setTimeout(5);
$req = new EventHttpRequest("_request_handler", $base);

$req->addHeader("Host", $address, EventHttpRequest::OUTPUT_HEADER);
$req->addHeader("Content-Length", "0", EventHttpRequest::OUTPUT_HEADER);
$conn->makeRequest($req, EventHttpRequest::CMD_GET, "/index.cphp");

$base->loop();
?>

以上示例的输出类似于:

_request_handler
Success: 200
Body:
PHP, date:
2013-03-13T20:27:52+05:00

示例 #12 Connection listener based on a UNIX domain socket

<?php
/*
* Simple echo server based on libevent's connection listener.
*
* Usage:
* 1) In one terminal window run:
*
* $ php unix-domain-listener.php [path-to-socket]
*
* 2) In another terminal window open up connection
* to the socket, e.g.:
*
* $ socat - GOPEN:/tmp/1.sock
*
* 3) Start typing. The server should repeat the input.
*/

class MyListenerConnection {
private
$bev, $base;

public function
__destruct() {
if (
$this->bev) {
$this->bev->free();
}
}

public function
__construct($base, $fd) {
$this->base = $base;

$this->bev = new EventBufferEvent($base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE);

$this->bev->setCallbacks(array($this, "echoReadCallback"), NULL,
array(
$this, "echoEventCallback"), NULL);

if (!
$this->bev->enable(Event::READ)) {
echo
"Failed to enable READ\n";
return;
}
}

public function
echoReadCallback($bev, $ctx) {
// Copy all the data from the input buffer to the output buffer
$bev->output->addBuffer($bev->input);
}

public function
echoEventCallback($bev, $events, $ctx) {
if (
$events & EventBufferEvent::ERROR) {
echo
"Error from bufferevent\n";
}

if (
$events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
$bev->free();
$bev = NULL;
}
}
}

class
MyListener {
public
$base,
$listener,
$socket;
private
$conn = array();

public function
__destruct() {
foreach (
$this->conn as &$c) $c = NULL;
}

public function
__construct($sock_path) {
$this->base = new EventBase();
if (!
$this->base) {
echo
"Couldn't open event base";
exit(
1);
}

if (
file_exists($sock_path)) {
unlink($sock_path);
}

$this->listener = new EventListener($this->base,
array(
$this, "acceptConnCallback"), $this->base,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, -1,
"unix:$sock_path");

if (!
$this->listener) {
trigger_error("Couldn't create listener", E_USER_ERROR);
}

$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}

public function
dispatch() {
$this->base->dispatch();
}

// This callback is invoked when there is data to read on $bev
public function acceptConnCallback($listener, $fd, $address, $ctx) {
// We got a new connection! Set up a bufferevent for it. */
$base = $this->base;
$this->conn[] = new MyListenerConnection($base, $fd);
}

public function
accept_error_cb($listener, $ctx) {
$base = $this->base;

fprintf(STDERR, "Got an error %d (%s) on the listener. "
."Shutting down.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());

$base->exit(NULL);
}
}

if (
$argc <= 1) {
exit(
"Socket path is not provided\n");
}
$sock_path = $argv[1];

$l = new MyListener($sock_path);
$l->dispatch();
?>

示例 #13 Simple SMTP server

<?php
/*
* Author: Andrew Rose <hello at andrewrose dot co dot uk>
*
* Usage:
* 1) Prepare cert.pem certificate and privkey.pem private key files.
* 2) Launch the server script
* 3) Open TLS connection, e.g.:
* $ openssl s_client -connect localhost:25 -starttls smtp -crlf
* 4) Start testing the commands listed in `cmd` method below.
*/

class Handler {
public
$domainName = FALSE;
public
$connections = [];
public
$buffers = [];
public
$maxRead = 256000;

public function
__construct() {
$this->ctx = new EventSslContext(EventSslContext::SSLv3_SERVER_METHOD, [
EventSslContext::OPT_LOCAL_CERT => 'cert.pem',
EventSslContext::OPT_LOCAL_PK => 'privkey.pem',
//EventSslContext::OPT_PASSPHRASE => '',
EventSslContext::OPT_VERIFY_PEER => false, // change to true with authentic cert
EventSslContext::OPT_ALLOW_SELF_SIGNED => true // change to false with authentic cert
]);

$this->base = new EventBase();
if (!
$this->base) {
exit(
"Couldn't open event base\n");
}

if (!
$this->listener = new EventListener($this->base,
[
$this, 'ev_accept'],
$this->ctx,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,
-
1,
'0.0.0.0:25'))
{
exit(
"Couldn't create listener\n");
}

$this->listener->setErrorCallback([$this, 'ev_error']);
$this->base->dispatch();
}

public function
ev_accept($listener, $fd, $address, $ctx) {
static
$id = 0;
$id += 1;

$this->connections[$id]['clientData'] = '';
$this->connections[$id]['cnx'] = new EventBufferEvent($this->base, $fd,
EventBufferEvent::OPT_CLOSE_ON_FREE);

if (!
$this->connections[$id]['cnx']) {
echo
"Failed creating buffer\n";
$this->base->exit(NULL);
exit(
1);
}

$this->connections[$id]['cnx']->setCallbacks([$this, "ev_read"], NULL,
[
$this, 'ev_error'], $id);
$this->connections[$id]['cnx']->enable(Event::READ | Event::WRITE);

$this->ev_write($id, '220 '.$this->domainName." wazzzap?\r\n");
}

function
ev_error($listener, $ctx) {
$errno = EventUtil::getLastSocketErrno();

fprintf(STDERR, "Got an error %d (%s) on the listener. Shutting down.\n",
$errno, EventUtil::getLastSocketError());

if (
$errno != 0) {
$this->base->exit(NULL);
exit();
}
}

public function
ev_close($id) {
$this->connections[$id]['cnx']->disable(Event::READ | Event::WRITE);
unset(
$this->connections[$id]);
}

protected function
ev_write($id, $string) {
echo
'S('.$id.'): '.$string;
$this->connections[$id]['cnx']->write($string);
}

public function
ev_read($buffer, $id) {
while(
$buffer->input->length > 0) {
$this->connections[$id]['clientData'] .= $buffer->input->read($this->maxRead);
$clientDataLen = strlen($this->connections[$id]['clientData']);

if(
$this->connections[$id]['clientData'][$clientDataLen-1] == "\n"
&& $this->connections[$id]['clientData'][$clientDataLen-2] == "\r")
{
// remove the trailing \r\n
$line = substr($this->connections[$id]['clientData'], 0,
strlen($this->connections[$id]['clientData']) - 2);

$this->connections[$id]['clientData'] = '';
$this->cmd($buffer, $id, $line);
}
}
}

protected function
cmd($buffer, $id, $line) {
switch (
$line) {
case
strncmp('EHLO ', $line, 4):
$this->ev_write($id, "250-STARTTLS\r\n");
$this->ev_write($id, "250 OK ehlo\r\n");
break;

case
strncmp('HELO ', $line, 4):
$this->ev_write($id, "250-STARTTLS\r\n");
$this->ev_write($id, "250 OK helo\r\n");
break;

case
strncmp('QUIT', $line, 3):
$this->ev_write($id, "250 OK quit\r\n");
$this->ev_close($id);
break;

case
strncmp('STARTTLS', $line, 3):
$this->ev_write($id, "220 Ready to start TLS\r\n");
$this->connections[$id]['cnx'] = EventBufferEvent::sslFilter($this->base,
$this->connections[$id]['cnx'], $this->ctx,
EventBufferEvent::SSL_ACCEPTING,
EventBufferEvent::OPT_CLOSE_ON_FREE);
$this->connections[$id]['cnx']->setCallbacks([$this, "ev_read"], NULL, [$this, 'ev_error'], $id);
$this->connections[$id]['cnx']->enable(Event::READ | Event::WRITE);
break;

default:
echo
'unknown command: '.$line."\n";
break;
}
}
}

new
Handler();
add a note

User Contributed Notes 1 note

up
4
Bas Vijfwinkel
8 years ago
In order to use certain features used in the examples above here you need a very recent version of libevent (>= 2.1).
Although 'pecl install event' will not show any errors, certain features are disabled and certain function calls might use a different number of parameters.
For example EventHttp will throw a warning that the number of parameters should be 1 instead of 2 (when using it with a SSL context) and as a bonus cause a segmentation fault.

On some distributions of Linux, the stable libevent library might not be recent enough for all features to be enabled and you might need to use an alpha version.

You can install an alpha version of libevent next to the stable version that might already be on your machine.
The basic steps are:
- download the alpha tarball to a folder e.g libevent-2.1.3-alpha.tar.gz
- tar zxvf libevent-2.1.3-alpha.tar.gz
- cd libevent-2.1.3-alpha
- ./configure --prefix=/opt/libevent-alpha
- make
- make install

Now the alpha version of libevent is created in /opt/libevent-alpha.

Before running pecl, first export the library folder of libevent so that pecl knows where your most recent libevent stuff is:
export LD_LIBRARY_PATH=/opt/libevent-2.1.3-alpha/lib
Without this export I couldn't get pecl to use the correct libevent

Now run 'pecl install event'
and when asked libevent installation prefix [/usr] :
enter : /opt/libevent-2.1.3-alpha
Now pecl will use your alpha version of libevent instead of the default version.

If everything goes well then you should be able to enjoy the full glory of this wonderfull extension.

If you try to install a very recent version of libevent on a system with a old version of openssl (0.9), you also need to update that because there are some dependencies in libevent that do not work with 0.9
To Top