Exemples

Exemple #1 Client HTTP simple

<?php
// Lecture de la fonction de rappel
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;
}
}

// Fonction de rappel de l'événement
function eventcb($bev, $events, $base) {
if (
$events & EventBufferEvent::CONNECTED) {
echo
"Connecté.\n";
} elseif (
$events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if (
$events & EventBufferEvent::ERROR) {
echo
"erreur DNS : ", $bev->getDnsErrorString(), PHP_EOL;
}

echo
"Fermeture\n";
$base->exit();
exit(
"Fait !\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); // Nous utilisons une résolution DNS asynchrone
if (!$dns_base) {
exit(
"Echec dans l'initialisation de la base DNS\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(
"Echec dans la création du socket bufferevent\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(
"Echec dans l'ajout de la requête dans le buffer de sortie\n");
}

if (!
$bev->connectHost($dns_base, $argv[1], 80, EventUtil::AF_UNSPEC)) {
exit(
"Connexion impossible à l'hôte {$argv[1]}\n");
}

$base->dispatch();
?>

Résultat de l'exemple ci-dessus est similaire à :

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

Exemple #2 Client HTTP en utilisant une résolution DNS asynchrone

<?php
/*
* 1. Connexion à 127.0.0.1 sur le port 80
* en utilisant EventBufferEvent::connect().
*
* 2. Requête /index.cphp via HTTP/1.0
* en utilisant le buffer de sortie.
*
* 3. Lecture asynchrone de la réponse et l'affiche dans stdout.
*/

// Lecture de la fonction de rappel
function readcb($bev, $base) {
$input = $bev->getInput();

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

// Fonction de rappel de l'événement
function eventcb($bev, $events, $base) {
if (
$events & EventBufferEvent::CONNECTED) {
echo
"Connecté.\n";
} elseif (
$events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if (
$events & EventBufferEvent::ERROR) {
echo
"Erreur DNS : ", $bev->getDnsErrorString(), PHP_EOL;
}

echo
"Fermeture\n";
$base->exit();
exit(
"Fait !\n");
}
}

$base = new EventBase();

echo
"étape n°1\n";
$bev = new EventBufferEvent($base, /* use internal socket */ NULL,
EventBufferEvent::OPT_CLOSE_ON_FREE | EventBufferEvent::OPT_DEFER_CALLBACKS);
if (!
$bev) {
exit(
"Echec lors de la création du socket bufferevent\n");
}

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

echo
"étape n°3\n";
// Envoi de la requête
$output = $bev->getOutput();
if (!
$output->add(
"GET /index.cphp HTTP/1.0\r\n".
"Connection: Close\r\n\r\n"
)) {
exit(
"Echec lors de l'ajout de la requête dans le buffer de sortie\n");
}

/* COnnexion à l'hôte de façon synchrone.
Nous connaissons l'IP, nous n'avons donc pas besoin de résolution DNS. */
if (!$bev->connect("127.0.0.1:80")) {
exit(
"Impossible de se connecter à l'hôte\n");
}

// Diffuse les événements en attente
$base->dispatch();
?>

Exemple #3 Serveur d'affichage

<?php
/*
* Serveur d'affichage simpple basé sur les écoutes de connexion libevent.
*
* Utilisation :
* 1) Dans un terminal, exécutez :
*
* $ php listener.php 9881
*
* 2) Dans un autre terminal, ouvrez une connexion, i.e. :
*
* $ nc 127.0.0.1 9881
*
* 3) Commencez à taper. Le serveur devrait répéter les entrées.
*/

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
"Echec dans l'activation de READ\n";
return;
}
}

public function
echoReadCallback($bev, $ctx) {
// Copie toutes les données depuis le buffer d'entrée vers le buffer de sortie

// 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
"Erreur depuis 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
"Impossible d'ouvrir la base de l'événement";
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 "Impossible de lier le 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
"Impossible de créer l'écouteur";
exit(
1);
}

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

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

// Cette fonction de rappel est appelée lorsqu'il y a des données à lire sur $bev
public function acceptConnCallback($listener, $fd, $address, $ctx) {
// Nous avons une nouvelle connexion ! On définit un bufferevent pour elle. */
$base = $this->base;
$this->conn[] = new MyListenerConnection($base, $fd);
}

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

fprintf(STDERR, "On recoit une erreur %d (%s) sur l'écouteur. "
."Arrêt.\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 Serveur d'affichage SSL

<?php
/*
* Serveur d'affichage SSL
*
* Pour tester :
* 1) Exécutez :
* $ php examples/ssl-echo-server/server.php 9998
*
* 2) dans un autre terminal, exécutez :
* $ 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(
"Echec lors de la création du contexte SSL\n");
}

$this->base = new EventBase();
if (!
$this->base) {
exit(
"Impossible d'ouvrir la base de l'événement\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(
"Impossible de créer l'écouteur\n");
}

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

// Cette fonction de rappel est appelée lorsqu'il y a des données à lire sur $bev.
function ssl_read_cb($bev, $ctx) {
$in = $bev->input; //$bev->getInput();

printf("Réception de %zu octets\n", $in->length);
printf("----- données ----\n");
printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));

$bev->writeBuffer($in);
}

// Cette fonction de rappel est appelée lorsque des erreurs surviennent sur l'écouteur d'événement,
// i.e. la connexion se ferme, ou une erreur survient
function ssl_event_cb($bev, $events, $ctx) {
if (
$events & EventBufferEvent::ERROR) {
// Récupère les erreurs depuis la pile d'erreur SSL
while ($err = $bev->sslError()) {
fprintf(STDERR, "Erreur Bufferevent %s.\n", $err);
}
}

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

// Cette fonction de rappel est appelée lorsqu'un client accepte une nouvelle connexion
function ssl_accept_cb($listener, $fd, $address, $ctx) {
// Nous avons une nouvelle connexion ! On définit un bufferevent pour elle.
$this->bev = EventBufferEvent::sslSocket($this->base, $fd, $this->ctx,
EventBufferEvent::SSL_ACCEPTING, EventBufferEvent::OPT_CLOSE_ON_FREE);

if (!
$this->bev) {
echo
"Echec lors de la création du buffer SSL\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);
}

// Cette fonction de rappel est appelée lorsqu'on échoue à définir une nouvelle connexion pour un client
function accept_error_cb($listener, $ctx) {
fprintf(STDERR, "On reçoit une erreur %d (%s) sur l'écouteur. "
."Arrêt.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());

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

// Initialise les structures SSL ; crée un EventSslContext
// Optionnellement, crée des certificats autosignés
function init_ssl() {
// Nous *devons* avoir l'entropie. Sinon, il n'y a aucun intérêt à crypter.
if (!EventUtil::sslRandPoll()) {
exit(
"Echec de EventUtil::sslRandPoll\n");
}

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

if (!
file_exists($local_cert) || !file_exists($local_pk)) {
echo
"Impossible de lire $local_cert ni $local_pk file. Pour générer une clé\n",
"et un certificat autosigné, exécutez :\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;
}
}

// Autorise l'écrasement du port
$port = 9999;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Port invalide\n");
}


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

Exemple #5 Gestionnaire de signal

<?php
/*
Dans un terminal, exécutez :

$ php examples/signal.php

Dans un autre terminal, trouvez le pid et envoyé le signal SIGTERM, i.e. :

$ 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

Dans le premier terminal, vous devriez attrapper ce qui suit :

On attrappe le signal 15
*/
class MyEventSignal {
private
$base;

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

function
eventSighandler($no, $c) {
echo
"On attrappe le 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 Utilisation d'une boucle libevent pour réaliser les requêtes de l'extension `eio'

<?php
// Fonction de rappel pour eio_nop()
function my_nop_cb($d, $r) {
echo
"étape 6\n";
}

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

echo
"étape 1\n";

$base = new EventBase();

echo
"étape 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
"étape 3\n";

$event->add();

echo
"étape 4\n";

$base->dispatch();

echo
"Fait !\n";
?>

Exemple #7 Divers

<?php
/* {{{ Configuration & méthodes supportées */
echo "Méthodes supportées :\n";
foreach (
Event::getSupportedMethods() as $m) {
echo
$m, PHP_EOL;
}

// On désactive la méthode "select"
$cfg = new EventConfig();
if (
$cfg->avoidMethod("select")) {
echo
"La méthode 'select' a été désactivée\n";
}

// Crée un event_base associé à la configuration
$base = new EventBase($cfg);
echo
"Méthode d'événement utilisée : ", $base->getMethod(), PHP_EOL;

echo
"fonctionnalités :\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";

// Nécessite la fonctionnalité FDS
if ($cfg->requireFeatures(EventConfig::FEATURE_FDS)) {
echo
"La fonctionnalité FDS est maintenant requise\n";

$base = new EventBase($cfg);
(
$base->getFeatures() & EventConfig::FEATURE_FDS)
and print
"FDS - type de descripteur de fichiers arbitraire, et non uniquement les 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) {
/* on sort après 5 itérations avec un délai maximal d'attente de 2.33 secondes */
echo "Arrêt...\n";
$arg[0]->exit(2.33);
}

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

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

Exemple #8 Serveur HTTP simple

<?php
/*
* Serveur HTTP simple.
*
* Pour le tester :
* 1) Exécutez-le sur le port de votre choix, i.e. :
* $ php examples/http.php 8010
* 2) Dans un autre terminal, connectez-vous sur une adresse de ce port
* et effectuez des requêtes GET ou POST (les autres types sont désactivées dans cet exemple), i.e. :
* $ nc -t 127.0.0.1 8010
* POST /about HTTP/1.0
* Content-Type: text/plain
* Content-Length: 4
* Connection: close
* (press Enter)
*
* Il devrait afficher :
* 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)
*
* Il devrait afficher :
* 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
*
* Il devrait afficher :
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
*
* 3) Voir ce que le serveur affiche dans le terminal précédent.
*/

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

if (++
$counter >= $max_requests) {
echo
"Le compteur a atteint le nombre maximal de requête ($max_requests). Sortie !\n";
exit();
}

echo
__METHOD__, " called\n";
echo
"Requête :"; var_dump($req);
echo
"Données :"; var_dump($data);

echo
"\n===== DUMP =====\n";
echo
"Commande :", $req->getCommand(), PHP_EOL;
echo
"URI :", $req->getUri(), PHP_EOL;
echo
"En-têtes en entrée :"; var_dump($req->getInputHeaders());
echo
"En-têtes en sortie :"; var_dump($req->getOutputHeaders());

echo
"\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo
"OK\n";

echo
"\n >> Lecture du buffer d'entrée ...\n";
$buf = $req->getInputBuffer();
while (
$s = $buf->readLine(EventBuffer::EOL_ANY)) {
echo
$s, PHP_EOL;
}
echo
"Il n'y a plus de données dans le buffer\n";
}

function
_http_about($req) {
echo
__METHOD__, PHP_EOL;
echo
"URI : ", $req->getUri(), PHP_EOL;
echo
"\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

function
_http_default($req, $data) {
echo
__METHOD__, PHP_EOL;
echo
"URI: ", $req->getUri(), PHP_EOL;
echo
"\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

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

$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", "valeur de données personnalisées");

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

Résultat de l'exemple ci-dessus est similaire à :

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

Exemple #9 Serveur HTTPS simple

<?php
/*
* Serveur HTTPS simple.
*
* 1) Exécutez le serveur : `php examples/https.php 9999`
* 2) Testez le : `php examples/ssl-connection.php 9999`
*/

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

if (++
$counter >= $max_requests) {
echo
"Le compteur a atteint le nombre maximal de requêtes ($max_requests). Sortie\n";
exit();
}

echo
__METHOD__, " appelée\n";
echo
"Requête :"; var_dump($req);
echo
"Données :"; var_dump($data);

echo
"\n===== DUMP =====\n";
echo
"Commande :", $req->getCommand(), PHP_EOL;
echo
"URI :", $req->getUri(), PHP_EOL;
echo
"En-têtes d'entrée :"; var_dump($req->getInputHeaders());
echo
"En-têtes de sortie :"; var_dump($req->getOutputHeaders());

echo
"\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo
"OK\n";

$buf = $req->getInputBuffer();
echo
"\n >> Lecture du buffer d'entrée (", $buf->length, ") ...\n";
while (
$s = $buf->read(1024)) {
echo
$s;
}
echo
"\nIl n'y a plus de données dans le buffer\n";
}

function
_http_about($req) {
echo
__METHOD__, PHP_EOL;
echo
"URI : ", $req->getUri(), PHP_EOL;
echo
"\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

function
_http_default($req, $data) {
echo
__METHOD__, PHP_EOL;
echo
"URI: ", $req->getUri(), PHP_EOL;
echo
"\n >> Envoi de la réponse ...";
$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(
"Port invalide");
}
$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", "Valeur des données personnalisées");

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

Exemple #10 Connexion OpenSSL

<?php
/*
* Exemple de Client OpenSSL.
*
* Utilisation :
* 1) Exécutez un serveur, i.e. :
* $ php examples/https.php 9999
*
* 2) Lancez le client dans un autre terminal :
* $ php examples/ssl-connection.php 9999
*/

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

if (
is_null($req)) {
echo
"Délai d'attente maximal atteint\n";
} else {
$response_code = $req->getResponseCode();

if (
$response_code == 0) {
echo
"Connexion refusée\n";
} elseif (
$response_code != 200) {
echo
"Réponse innatendue : $response_code\n";
} else {
echo
"Succès : $response_code\n";
$buf = $req->getInputBuffer();
echo
"Corps :\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;
}


// Autorise l'écrasement du port
$port = 9999;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Port invalide\n");
}
$host = '127.0.0.1';

$ctx = _init_ssl();
if (!
$ctx) {
trigger_error("Echec lors de la création du contexte SSL", E_USER_ERROR);
}

$base = new EventBase();
if (!
$base) {
trigger_error("Echec dans l'initialisation de la base d'événement", 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
"FIN\n";
?>

Exemple #11 Exemple avec EventHttpConnection::makeRequest()

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

if (
is_null($req)) {
echo
"Délai d'attente maximal atteint\n";
} else {
$response_code = $req->getResponseCode();

if (
$response_code == 0) {
echo
"Connexion refusée\n";
} elseif (
$response_code != 200) {
echo
"Réponse innatendue : $response_code\n";
} else {
echo
"Succès : $response_code\n";
$buf = $req->getInputBuffer();
echo
"Corps :\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();
?>

Résultat de l'exemple ci-dessus est similaire à :

_request_handler
Succès : 200
Corps :
PHP, date:
2013-03-13T20:27:52+05:00

Exemple #12 Ecoute d'une connexion en se basant sur un socket de domaine Unix

<?php
/*
* Serveur d'écoute en se basant sur un écouteur de connexion libevent.
*
* Utilisation :
* 1) Dans un terminal, exécutez :
*
* $ php unix-domain-listener.php [path-to-socket]
*
* 2) Dans un autre terminal, ouvrez la connexion
* vers le socket, i.e. :
*
* $ socat - GOPEN:/tmp/1.sock
*
* 3) Commencez à taper. Le serveur doit répéter les entrées.
*/

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
"Echec dans l'activation de READ\n";
return;
}
}

public function
echoReadCallback($bev, $ctx) {
// Copie toutes les données depuis le buffer d'entrée dans le buffer de sortie
$bev->output->addBuffer($bev->input);
}

public function
echoEventCallback($bev, $events, $ctx) {
if (
$events & EventBufferEvent::ERROR) {
echo
"Erreur depuis 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("Impossible de créer l'écouteur", E_USER_ERROR);
}

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

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

// Cette fonction de rappel sera appelée lorsqu'il y aura des données à lire sur $bev
public function acceptConnCallback($listener, $fd, $address, $ctx) {
// Nous avons une nouvelle connexion ! On définit un bufferevent pour elle. */
$base = $this->base;
$this->conn[] = new MyListenerConnection($base, $fd);
}

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

fprintf(STDERR, "On reçoit une erreur %d (%s) sur l'écouteur. "
."Shutting down.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());

$base->exit(NULL);
}
}

if (
$argc <= 1) {
exit(
"Le chemin vers le socket n'est pas fourni\n");
}
$sock_path = $argv[1];

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

Exemple #13 Exemple de serveur SMTP

<?php
/*
* Auteur : Andrew Rose <hello at andrewrose dot co dot uk>
*
* Utilisation :
* 1) On prépare le certificat cert.pem et la clé privée privkey.pem.
* 2) On démarre le script du serveur
* 3) On ouvre la connexion TLS, i.e. :
* $ openssl s_client -connect localhost:25 -starttls smtp -crlf
* 4) On commence à tester les commandes listées dans la méthode `cmd` ci-dessous.
*/

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, // modifier en TRUE avec les certificats authentiques
EventSslContext::OPT_ALLOW_SELF_SIGNED => true // modifier en FALSE avec les certificats authentiques
]);

$this->base = new EventBase();
if (!
$this->base) {
exit(
"Impossible d'ouvrir la base de l'événement\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(
"Impossible de créer l'écouteur\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
"Echec lors de la création du 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, "On reçoit l'erreur %d (%s) sur l'écouteur. On stoppe.\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")
{
// Supprime les caractères \r\n à la fin
$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
'Commande inconnue : '.$line."\n";
break;
}
}
}

new
Handler();
add a note

User Contributed Notes 1 note

up
4
Bas Vijfwinkel
9 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