Exemples

Exemple #1 Simple HTTP client

<?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", $base
);
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();
?>
<?php
/*
 * 1. Connect to 127.0.0.1 at port 80
 * by means of EventBufferEvent::connect().
 *
 * 2. Request /index.cphp via HTTP/1.0
 * using the output buffer.
 *
 * 3. Asyncronously read the response and print it to stdout.
 */

// 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";
// Send request
$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");
}

/* Connect to the host syncronously.
We know the IP, and don't need to resolve DNS. */
if (!$bev->connect("127.0.0.1:80")) {
	exit("Can't connect to host\n");
}

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

Exemple #3 Echo server

<?php
/*
 * Simple echo server based on libevent's connection listener.
 *
 * Usage:
 * 1) In one terminal window run:
 *
 * $ php listener.php 9881
 *
 * 2) In another terminal window open up connection, e.g.:
 *
 * $ nc 127.0.0.1 9881
 *
 * 3) start typing. The server should repeat the input.
 */

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) {
		// Copy all the data from the input buffer to the output buffer
		
		// Variant #1
		$bev->output->addBuffer($bev->input);

		/* Variant #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);
		}

		// Variant #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);
		 */

		// Variant #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();
	}

	// 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);
	}
}

$port = 9808;

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

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

Exemple #4 SSL echo server

<?php
/*
 * SSL echo server
 *
 * To test:
 * 1) Run:
 * $ php examples/ssl-echo-server/server.php 9998
 *
 * 2) in another terminal window run:
 * $ 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();
	}

	// This callback is invoked when there is data to read on $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);
	}

	// This callback is invoked when some even occurs on the event listener,
	// e.g. connection closed, or an error occured
	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();
		}
	}

	// This callback is invoked when a client accepts new connection
	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);
	}

	// This callback is invoked when we failed to setup new connection for a client
	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);
	}

	// Initialize SSL structures, create an EventSslContext
	// Optionally create self-signed certificates
	function init_ssl() {
		// We *must* have entropy. Otherwise there's no point to crypto.
		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;
	}
}

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


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

Exemple #5 Signal handler

<?php
/*
Launch it in a terminal window:

$ php examples/signal.php

In another terminal window find out the pid and send SIGTERM, e.g.:

$ 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

At the first terminal window you should catch the following:

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);
?>

Exemple #6 Use libevent's loop to process requests of `eio' extension

<?php
// Callback for 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";
?>

Exemple #7 Miscellaneous

<?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/deletting 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 requried\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 }}} */
?>

Exemple #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();
?>
<?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();
?>
<?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();
?>

Exemple #11 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'] = $this->connections[$id]['cnx']->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();
LoadingChargement en cours