1. Go to this page and download the library: Download mikepultz/php-quic library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
mikepultz / php-quic example snippets
// open a QUIC connection and exchange data on a stream
$conn = new Quic\Connection('example.com', 443, ['alpn' => 'myproto']);
$stream = $conn->openStream();
// send the data, then conclude the stream (FIN)
$stream->write("hello", true);
$reply = $stream->read(65535);
$conn->close();
try
{
$ready = Quic\poll($items, 1.0);
if (count($ready) == 0)
{
// timed out: nothing became ready within 1.0s
}
foreach ($ready as $i => $revents)
{
if (($revents & Quic\POLL_ERROR) != 0)
{
// this object errored or closed
}
if (($revents & Quic\POLL_READ) != 0)
{
// readable
}
}
} catch (Quic\Exception $e)
{
// the poll call itself failed
}
$info = $conn->getCloseInfo();
if ($info !== null && $info['transport'] == true
&& $info['error_code'] === Quic\ERR_PROTOCOL_VIOLATION)
{
// the peer tore down the connection for a QUIC protocol violation
}
$host = 'cloudflare-quic.com';
$conn = new Quic\Connection($host, 443, ['alpn' => 'h3']);
//
// HTTP/3 needs a control stream with SETTINGS plus QPACK encoder/decoder
// streams. Keep these references for the connection's lifetime: closing the
// control stream is a fatal H3 error.
//
// unidirectional control stream: type 0x00 (control) + empty SETTINGS
$control = $conn->openStream(false);
$control->write("\x00\x04\x00");
// QPACK encoder (0x02) and decoder (0x03) streams
$qpackEnc = $conn->openStream(false);
$qpackEnc->write("\x02");
$qpackDec = $conn->openStream(false);
$qpackDec->write("\x03");
//
// a GET is a HEADERS frame holding a QPACK field section. Pseudo-headers come
// from the QPACK static table: GET=17, https=23, :path /=1, :authority=0.
//
// QPACK prefix
$fields = "\x00\x00";
// :method GET
$fields .= "\xD1";
// :scheme https
$fields .= "\xD7";
// :authority (literal)
$fields .= "\x50" . chr(strlen($host)) . $host;
// :path /
$fields .= "\xC1";
// HEADERS frame
$request = "\x01" . chr(strlen($fields)) . $fields;
// request stream (bidirectional): send the request + FIN (GET has no body)
$stream = $conn->openStream(true);
$stream->write($request, true);
$resp = '';
while (($chunk = $stream->read(65535)) !== null)
{
if ($chunk !== '')
{
$resp .= $chunk;
}
}
//
// parse the HTTP/3 frames and collect DATA frames (the body)
//
$read_varint = function (string $_d, int &$_o): int
{
$b = ord($_d[$_o]);
$n = 1 << ($b >> 6);
$v = $b & 0x3f;
for ($i = 1; $i < $n; $i++)
{
$v = ($v << 8) | ord($_d[$_o + $i]);
}
$_o += $n;
return $v;
};
$off = 0;
$body = '';
while ($off < strlen($resp))
{
$type = $read_varint($resp, $off);
$len = $read_varint($resp, $off);
$payload = substr($resp, $off, $len);
$off += $len;
// type 0x00 is a DATA frame; 0x01 is the response HEADERS frame (QPACK)
if ($type === 0x00)
{
$body .= $payload;
}
}
// e.g. "<!DOCTYPE html>"
echo strlen($body), " bytes: ", strtok($body, "\n"), "\n";
$conn->close();
$conn = new Quic\Connection('dns.adguard-dns.com', 853, ['alpn' => 'doq']);
$stream = $conn->openStream();
// a wire-format DNS message (id 0), e.g. from a DNS library
$query = "...";
// frame with a 2-octet length prefix and conclude (FIN)
$stream->write(pack('n', strlen($query)) . $query, true);
$len = unpack('n', $stream->read(2))[1];
$resp = $stream->read($len);
$conn->close();
$listener = new Quic\Listener('0.0.0.0', 4433, [
'local_cert' => '/etc/ssl/server.crt',
'local_pk' => '/etc/ssl/server.key',
'alpn' => 'myproto',
]);
while (true)
{
// blocking accept of a connection and its first stream
$conn = $listener->accept();
$stream = $conn->acceptStream();
$data = '';
while (($chunk = $stream->read(65535)) !== null)
{
if ($chunk !== '')
{
$data .= $chunk;
}
}
$stream->write("echo: $data", true);
$conn->close();
}
$conn = new Quic\Connection($host, $port, ['alpn' => 'myproto']);
$conn->setBlocking(false);
$stream = $conn->openStream();
// send the request + FIN
$stream->write($request, true);
$response = '';
while (true)
{
// wait until the stream is readable (or a QUIC timer needs servicing)
Quic\poll([[$stream, Quic\POLL_READ | Quic\POLL_ERROR]], 1.0);
$chunk = $stream->read(65535);
// end of stream
if ($chunk === null)
{
break;
}
if ($chunk !== '')
{
$response .= $chunk;
}
}
$conn->close();
$conn->setBlocking(false);
$sock = fopen('php://fd/' . $conn->getFd(), 'r');
$items = [[$stream, Quic\POLL_READ | Quic\POLL_ERROR]];
while (true)
{
//
// never sleep past the connection's own deadline
//
$quic = $conn->getTimeout();
$wait = $quic === null ? $deadline : min($quic, $deadline);
$r = [$sock, ...$yourOtherDescriptors];
$w = $conn->wantsWrite() == true ? [$sock] : [];
$e = [];
@stream_select($r, $w, $e, (int)$wait, (int)(fmod($wait, 1.0) * 1000000));
//
// pump the QUIC engine and collect stream readiness in one call
//
foreach (Quic\poll($items, 0.0) as $key => $revents)
{
// ... read/write the ready streams
}
// ... your own descriptors, handled exactly as they always were
}