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();
?>
L'exemple ci-dessus va afficher quelque chose de 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 auto-signé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 auto-signé, 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 "fonctionalité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/deletting events\n");
($features & EventConfig::FEATURE_FDS) and print("FDS - arbitrary file descriptor types, and not just sockets\n");
// Nécessite la fonctionalité FDS
if ($cfg->requireFeatures(EventConfig::FEATURE_FDS)) {
echo "La fonctionalité 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();
?>
L'exemple ci-dessus va afficher quelque chose de 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();
?>
L'exemple ci-dessus va afficher quelque chose de 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();
Document créé le 30/01/2003, dernière modification le 26/10/2018
Source du document imprimé : https://www.gaudry.be/php-rf-event.examples.html
L'infobrol est un site personnel dont le contenu n'engage que moi. Le texte est mis à disposition sous licence CreativeCommons(BY-NC-SA). Plus d'info sur les conditions d'utilisation et sur l'auteur.
Références
Ces références et liens indiquent des documents consultés lors de la rédaction de cette page, ou qui peuvent apporter un complément d'information, mais les auteurs de ces sources ne peuvent être tenus responsables du contenu de cette page.
L'auteur de ce site est seul responsable de la manière dont sont présentés ici les différents concepts, et des libertés qui sont prises avec les ouvrages de référence. N'oubliez pas que vous devez croiser les informations de sources multiples afin de diminuer les risques d'erreurs.