AMPHP adalah kumpulan pustaka berbasis peristiwa untuk PHP yang dirancang dengan mempertimbangkan serat dan konkurensi. Paket ini menyediakan server aplikasi HTTP/1.1 dan HTTP/2 yang non-pemblokiran dan bersamaan untuk PHP berdasarkan Revolt. Beberapa fitur disediakan dalam paket terpisah, seperti komponen WebSocket.
Paket ini dapat diinstal sebagai ketergantungan Komposer.
composer require amphp/http-server Selain itu, Anda mungkin ingin menginstal perpustakaan nghttp2 untuk memanfaatkan FFI guna mempercepat dan mengurangi penggunaan memori.
Pustaka ini menyediakan akses ke aplikasi Anda melalui protokol HTTP, menerima permintaan klien dan meneruskan permintaan tersebut ke penangan yang ditentukan oleh aplikasi Anda yang akan mengembalikan respons.
Permintaan yang masuk diwakili oleh objek Request . Permintaan diberikan kepada pelaksana RequestHandler , yang mendefinisikan metode handleRequest() yang mengembalikan instance Response .
public function handleRequest( Request $ request ): Response Penangan permintaan dibahas secara lebih rinci di bagian RequestHandler .
Server HTTP ini dibangun di atas event-loop Revolt dan kerangka konkurensi non-pemblokiran Amp. Oleh karena itu, ia mewarisi dukungan penuh dari semua primitifnya dan dimungkinkan untuk menggunakan semua perpustakaan non-pemblokiran yang dibangun di atas Revolt.
Catatan Secara umum, Anda harus membiasakan diri dengan konsep
Future, dengan coroutine, dan mengetahui beberapa fungsi kombinator agar benar-benar berhasil dalam menggunakan server HTTP.
Hampir setiap fungsi bawaan PHP melakukan pemblokiran I/O, artinya, thread yang menjalankan (kebanyakan setara dengan proses dalam kasus PHP) akan dihentikan secara efektif hingga respons diterima. Beberapa contoh fungsi tersebut: mysqli_query , file_get_contents , usleep dan masih banyak lagi.
Aturan praktis yang baik adalah: Setiap fungsi PHP bawaan yang melakukan I/O melakukannya dengan cara memblokir, kecuali Anda tahu pasti hal itu tidak terjadi.
Ada perpustakaan yang menyediakan implementasi yang menggunakan I/O non-pemblokiran. Anda harus menggunakan ini alih-alih fungsi bawaan.
Kami memenuhi kebutuhan I/O yang paling umum, seperti soket jaringan, akses file, permintaan HTTP dan soket web, klien database MySQL dan Postgres, serta Redis. Jika penggunaan pemblokiran I/O atau komputasi yang panjang diperlukan untuk memenuhi permintaan, pertimbangkan untuk menggunakan pustaka Paralel untuk menjalankan kode tersebut dalam proses atau thread terpisah.
Peringatan Jangan gunakan fungsi pemblokiran I/O apa pun di server HTTP.
// Here's a bad example, DO NOT do something like the following!
$ handler = new ClosureRequestHandler ( function () {
sleep ( 5 ); // Equivalent to a blocking I/O function with a 5 second timeout
return new Response ;
});
// Start a server with this handler and hit it twice.
// You'll have to wait until the 5 seconds are over until the second request is handled. Aplikasi Anda akan dilayani oleh instance HttpServer . Pustaka ini menyediakan SocketHttpServer , yang cocok untuk sebagian besar aplikasi, dibangun berdasarkan komponen yang ditemukan di pustaka ini dan di amphp/socket .
Untuk membuat instance SocketHttpServer dan mendengarkan permintaan, diperlukan minimal empat hal:
RequestHandler untuk merespons permintaan masuk,ErrorHander untuk memberikan respons terhadap permintaan yang tidak valid,PsrLogLoggerInterface , dan <?php
use Amp ByteStream ;
use Amp Http HttpStatus ;
use Amp Http Server DefaultErrorHandler ;
use Amp Http Server Request ;
use Amp Http Server RequestHandler ;
use Amp Http Server Response ;
use Amp Http Server SocketHttpServer ;
use Amp Log ConsoleFormatter ;
use Amp Log StreamHandler ;
use Monolog Logger ;
use Monolog Processor PsrLogMessageProcessor ;
require __DIR__ . ' /vendor/autoload.php ' ;
// Note any PSR-3 logger may be used, Monolog is only an example.
$ logHandler = new StreamHandler ( ByteStream getStdout ());
$ logHandler -> pushProcessor ( new PsrLogMessageProcessor ());
$ logHandler -> setFormatter ( new ConsoleFormatter ());
$ logger = new Logger ( ' server ' );
$ logger -> pushHandler ( $ logHandler );
$ requestHandler = new class () implements RequestHandler {
public function handleRequest ( Request $ request ) : Response
{
return new Response (
status: HttpStatus:: OK ,
headers: [ ' Content-Type ' => ' text/plain ' ],
body: ' Hello, world! ' ,
);
}
};
$ errorHandler = new DefaultErrorHandler ();
$ server = SocketHttpServer:: createForDirectAccess ( $ logger );
$ server -> expose ( ' 127.0.0.1:1337 ' );
$ server -> start ( $ requestHandler , $ errorHandler );
// Serve requests until SIGINT or SIGTERM is received by the process.
Amp trapSignal ([ SIGINT , SIGTERM ]);
$ server -> stop ();Contoh di atas membuat server sederhana yang mengirimkan respons teks biasa untuk setiap permintaan yang diterima.
SocketHttpServer menyediakan dua konstruktor statis untuk kasus penggunaan umum selain konstruktor normal untuk penggunaan lebih lanjut dan khusus.
SocketHttpServer::createForDirectAccess() : Digunakan pada contoh di atas, ini akan membuat server aplikasi HTTP yang cocok untuk akses jaringan langsung. Batasan yang dapat disesuaikan diterapkan pada koneksi per IP, total koneksi, dan permintaan bersamaan (masing-masing 10, 1000, dan 1000 secara default). Kompresi respons dapat diaktifkan atau dinonaktifkan (diaktifkan secara default) dan metode permintaan dibatasi pada kumpulan kata kerja HTTP yang diketahui secara default.SocketHttpServer::createForBehindProxy() : Membuat server yang sesuai untuk digunakan saat berada di belakang layanan proxy seperti nginx. Konstruktor statis ini memerlukan daftar IP proksi tepercaya (dengan subnet mask opsional) dan kasus enum ForwardedHeaderType (sesuai dengan Forwarded atau X-Forwarded-For ) untuk mengurai IP klien asli dari header permintaan. Tidak ada batasan yang dikenakan pada jumlah koneksi ke server, namun jumlah permintaan bersamaan dibatasi (1000 secara default, dapat disesuaikan atau dihapus). Kompresi respons dapat diaktifkan atau dinonaktifkan (diaktifkan secara default). Metode permintaan terbatas pada sekumpulan kata kerja HTTP yang diketahui secara default. Jika tidak satu pun dari metode ini memenuhi kebutuhan aplikasi Anda, konstruktor SocketHttpServer dapat digunakan secara langsung. Hal ini memberikan fleksibilitas yang sangat besar dalam cara pembuatan dan penanganan koneksi masuk koneksi klien, namun akan memerlukan lebih banyak kode untuk membuatnya. Konstruktor mengharuskan pengguna untuk meneruskan instance SocketServerFactory , yang digunakan untuk membuat instance Socket klien (keduanya komponen perpustakaan amphp/socket ), dan instance ClientFactory , yang dengan tepat membuat instance Client yang dilampirkan ke setiap Request yang dibuat oleh klien .
RequestHandler Permintaan yang masuk diwakili oleh objek Request . Permintaan diberikan kepada pelaksana RequestHandler , yang mendefinisikan metode handleRequest() yang mengembalikan instance Response .
public function handleRequest( Request $ request ): Response Setiap permintaan klien (yaitu, panggilan ke RequestHandler::handleRequest() ) dieksekusi dalam coroutine terpisah sehingga permintaan secara otomatis ditangani secara kooperatif dalam proses server. Ketika penangan permintaan menunggu pada I/O non-pemblokiran, permintaan klien lainnya diproses dalam coroutine secara bersamaan. Penangan permintaan Anda sendiri dapat membuat coroutine lain menggunakan Ampasync() untuk menjalankan beberapa tugas dalam satu permintaan.
Biasanya RequestHandler langsung menghasilkan respons, namun mungkin juga didelegasikan ke RequestHandler lain. Contoh pendelegasian RequestHandler adalah Router .
Antarmuka RequestHandler dimaksudkan untuk diimplementasikan oleh kelas khusus. Untuk kasus penggunaan yang sangat sederhana atau tiruan cepat, Anda dapat menggunakan CallableRequestHandler , yang dapat menggabungkan callable apa pun dan menerima Request serta mengembalikan Response .
Middleware memungkinkan pra-pemrosesan permintaan dan pasca-pemrosesan tanggapan. Selain itu, middleware juga dapat mencegat pemrosesan permintaan dan mengembalikan respons tanpa mendelegasikan ke penangan permintaan yang diteruskan. Kelas harus mengimplementasikan antarmuka Middleware untuk itu.
Catatan Middleware umumnya mengikuti kata lain seperti perangkat lunak dan perangkat keras dengan bentuk jamak. Namun, kami menggunakan istilah middlewares untuk merujuk pada beberapa objek yang mengimplementasikan antarmuka
Middleware.
public function handleRequest( Request $ request , RequestHandler $ next ): Response handleRequest adalah satu-satunya metode antarmuka Middleware . Jika Middleware tidak menangani permintaan itu sendiri, ia harus mendelegasikan pembuatan respons ke RequestHandler yang diterima.
function stackMiddleware( RequestHandler $ handler , Middleware ... $ middleware ): RequestHandler Beberapa middleware dapat ditumpuk dengan menggunakan AmpHttpServerMiddlewarestackMiddleware() , yang menerima RequestHandler sebagai argumen pertama dan sejumlah variabel instance Middleware . RequestHandler yang dikembalikan akan memanggil setiap middleware dalam urutan yang disediakan.
$ requestHandler = new class implements RequestHandler {
public function handleRequest ( Request $ request ): Response
{
return new Response (
status: HttpStatus:: OK ,
headers: [ " content-type " => " text/plain; charset=utf-8 " ],
body: " Hello, World! " ,
);
}
}
$ middleware = new class implements Middleware {
public function handleRequest ( Request $ request , RequestHandler $ next ): Response
{
$ requestTime = microtime ( true );
$ response = $ next -> handleRequest ( $ request );
$ response -> setHeader ( " x-request-time " , microtime ( true ) - $ requestTime );
return $ response ;
}
};
$ stackedHandler = Middleware stackMiddleware ( $ requestHandler , $ middleware );
$ errorHandler = new DefaultErrorHandler ();
// $logger is a PSR-3 logger instance.
$ server = SocketHttpServer:: createForDirectAccess ( $ logger );
$ server -> expose ( ' 127.0.0.1:1337 ' );
$ server -> start ( $ stackedHandler , $ errorHandler );ErrorHandler ErrorHander digunakan oleh server HTTP ketika permintaan yang salah format atau tidak valid diterima. Objek Request disediakan jika dibuat dari data yang masuk, tetapi mungkin tidak selalu disetel.
public function handleError(
int $ status ,
? string $ reason = null ,
? Request $ request = null ,
): Response Pustaka ini menyediakan DefaultErrorHandler yang mengembalikan halaman HTML bergaya sebagai isi respons. Anda mungkin ingin memberikan implementasi yang berbeda untuk aplikasi Anda, yang mungkin menggunakan beberapa implementasi bersama dengan router.
Request Jarang sekali Anda perlu membuat objek Request sendiri, karena objek tersebut biasanya disediakan ke RequestHandler::handleRequest() oleh server.
/**
* @param string $method The HTTP method verb.
* @param array<string>|array<string, array<string>> $headers An array of strings or an array of string arrays.
*/
public function __construct(
private readonly Client $ client ,
string $ method ,
Psr Http Message UriInterface $ uri ,
array $ headers = [],
Amp ByteStream ReadableStream | string $ body = '' ,
private string $ protocol = ' 1.1 ' ,
? Trailers $ trailers = null ,
) public function getClient(): Client Mengembalikan Сlient yang mengirimkan permintaan
public function getMethod(): string Mengembalikan metode HTTP yang digunakan untuk membuat permintaan ini, misalnya "GET" .
public function setMethod( string $ method ): voidMenetapkan metode HTTP permintaan.
public function getUri(): Psr Http Message UriInterface Mengembalikan URI permintaan.
public function setUri( Psr Http Message UriInterface $ uri ): void Menetapkan URI baru untuk permintaan tersebut.
public function getProtocolVersion(): stringMengembalikan versi protokol HTTP sebagai string (misalnya "1.0", "1.1", "2").
public function setProtocolVersion( string $ protocol )Menetapkan nomor versi protokol baru untuk permintaan tersebut.
/** @return array<non-empty-string, list<string>> */
public function getHeaders(): arrayMengembalikan header sebagai array yang diindeks string dari array string atau array kosong jika tidak ada header yang disetel.
public function hasHeader( string $ name ): boolMemeriksa apakah header yang diberikan ada.
/** @return list<string> */
public function getHeaderArray( string $ name ): arrayMengembalikan array nilai untuk header tertentu atau array kosong jika header tidak ada.
public function getHeader( string $ name ): ? string Mengembalikan nilai header yang diberikan. Jika ada beberapa header untuk header bernama, hanya nilai header pertama yang akan dikembalikan. Gunakan getHeaderArray() untuk mengembalikan array berisi semua nilai untuk header tertentu. Mengembalikan null jika header tidak ada.
public function setHeaders( array $ headers ): voidMengatur header dari array yang diberikan.
/** @param array<string>|string $value */
public function setHeader( string $ name , array | string $ value ): voidMenyetel header ke nilai yang diberikan. Semua baris header sebelumnya dengan nama yang diberikan akan diganti.
/** @param array<string>|string $value */
public function addHeader( string $ name , array | string $ value ): voidMenambahkan baris header tambahan dengan nama yang diberikan.
public function removeHeader( string $ name ): voidMenghapus header yang diberikan jika ada. Jika ada beberapa baris header dengan nama yang sama, semuanya akan dihapus.
public function getBody(): RequestBody Mengembalikan isi permintaan. RequestBody memungkinkan akses streaming dan buffered ke InputStream .
public function setBody( ReadableStream | string $ body )Menyetel aliran untuk isi pesan
Catatan Menggunakan string akan secara otomatis mengatur header
Content-Lengthdengan panjang string yang diberikan. MenyetelReadableStreamakan menghapus headerContent-Length. Jika Anda mengetahui persis panjang konten streaming Anda, Anda dapat menambahkan headercontent-lengthsetelah memanggilsetBody().
/** @return array<non-empty-string, RequestCookie> */
public function getCookies(): array Mengembalikan semua cookie dalam peta asosiatif nama cookie ke RequestCookie .
public function getCookie( string $ name ): ? RequestCookie Mendapatkan nilai cookie berdasarkan nama atau null .
public function setCookie( RequestCookie $ cookie ): void Menambahkan Cookie ke permintaan.
public function removeCookie( string $ name ): voidMenghapus cookie dari permintaan.
public function getAttributes(): arrayMengembalikan larik semua atribut yang disimpan dalam penyimpanan lokal permintaan yang dapat diubah.
public function removeAttributes(): arrayMenghapus semua atribut permintaan dari penyimpanan lokal permintaan yang dapat diubah.
public function hasAttribute( string $ name ): boolPeriksa apakah atribut dengan nama tertentu ada di penyimpanan lokal permintaan yang dapat diubah.
public function getAttribute( string $ name ): mixedAmbil variabel dari penyimpanan lokal permintaan yang dapat diubah.
Catatan Nama atribut harus diberi namespace dengan vendor dan namespace paket, seperti kelas.
public function setAttribute( string $ name , mixed $ value ): voidTetapkan variabel ke penyimpanan lokal permintaan yang dapat diubah.
Catatan Nama atribut harus diberi namespace dengan vendor dan namespace paket, seperti kelas.
public function removeAttribute( string $ name ): voidMenghapus variabel dari penyimpanan lokal permintaan yang dapat diubah.
public function getTrailers(): Trailers Mengizinkan akses ke Trailers permintaan.
public function setTrailers( Trailers $ trailers ): void Menetapkan objek Trailers untuk digunakan dalam permintaan.
Detail terkait klien digabungkan ke dalam objek AmpHttpServerDriverClient yang dikembalikan dari Request::getClient() . Antarmuka Client menyediakan metode untuk mengambil alamat soket jarak jauh dan lokal serta info TLS (jika berlaku).
Response Kelas Response mewakili respons HTTP. Response dikembalikan oleh penangan permintaan dan middleware.
/**
* @param int $code The HTTP response status code.
* @param array<string>|array<string, array<string>> $headers An array of strings or an array of string arrays.
*/
public function __construct(
int $ code = HttpStatus:: OK ,
array $ headers = [],
Amp ByteStream ReadableStream | string $ body = '' ,
? Trailers $ trailers = null ,
) Memanggil penangan pembuangan (yaitu fungsi yang didaftarkan melalui metode onDispose() ).
Catatan Pengecualian yang tidak tertangkap dari penangan pembuangan akan diteruskan ke penangan kesalahan perulangan peristiwa.
public function getBody(): Amp ByteStream ReadableStreamMengembalikan aliran untuk isi pesan.
public function setBody( Amp ByteStream ReadableStream | string $ body )Menyetel aliran untuk isi pesan.
Catatan Menggunakan string akan secara otomatis mengatur header
Content-Lengthdengan panjang string yang diberikan. MenyetelReadableStreamakan menghapus headerContent-Length. Jika Anda mengetahui persis panjang konten streaming Anda, Anda dapat menambahkan headercontent-lengthsetelah memanggilsetBody().
/** @return array<non-empty-string, list<string>> */
public function getHeaders(): arrayMengembalikan header sebagai array yang diindeks string dari array string atau array kosong jika tidak ada header yang disetel.
public function hasHeader( string $ name ): boolMemeriksa apakah header yang diberikan ada.
/** @return list<string> */
public function getHeaderArray( string $ name ): arrayMengembalikan array nilai untuk header tertentu atau array kosong jika header tidak ada.
public function getHeader( string $ name ): ? string Mengembalikan nilai header yang diberikan. Jika ada beberapa header untuk header bernama, hanya nilai header pertama yang akan dikembalikan. Gunakan getHeaderArray() untuk mengembalikan array berisi semua nilai untuk header tertentu. Mengembalikan null jika header tidak ada.
public function setHeaders( array $ headers ): voidMengatur header dari array yang diberikan.
/** @param array<string>|string $value */
public function setHeader( string $ name , array | string $ value ): voidMenyetel header ke nilai yang diberikan. Semua baris header sebelumnya dengan nama yang diberikan akan diganti.
/** @param array<string>|string $value */
public function addHeader( string $ name , array | string $ value ): voidMenambahkan baris header tambahan dengan nama yang diberikan.
public function removeHeader( string $ name ): voidMenghapus header yang diberikan jika ada. Jika ada beberapa baris header dengan nama yang sama, semuanya akan dihapus.
public function getStatus(): intMengembalikan kode status respons.
public function getReason(): stringMengembalikan frasa alasan yang menjelaskan kode status.
public function setStatus( int $ code , string | null $ reason ): voidMenetapkan kode status HTTP numerik (antara 100 dan 599) dan frasa alasan. Gunakan null sebagai frase alasan untuk menggunakan frase default yang terkait dengan kode status.
/** @return array<non-empty-string, ResponseCookie> */
public function getCookies(): array Mengembalikan semua cookie dalam peta asosiatif nama cookie ke ResponseCookie .
public function getCookie( string $ name ): ? ResponseCookie Mendapatkan nilai cookie berdasarkan nama atau null jika tidak ada cookie dengan nama tersebut.
public function setCookie( ResponseCookie $ cookie ): voidMenambahkan cookie ke respons.
public function removeCookie( string $ name ): voidMenghapus cookie dari respons.
/** @return array<string, Push> Map of URL strings to Push objects. */
public function getPushes(): array Mengembalikan daftar sumber daya push dalam peta asosiatif string URL ke objek Push .
/** @param array<string>|array<string, array<string>> $headers */
public function push( string $ url , array $ headers ): void Tunjukkan sumber daya yang mungkin perlu diambil oleh klien. (misalnya Link: preload atau HTTP/2 Server Push).
public function isUpgraded(): bool Mengembalikan true jika panggilan balik pelepasan telah disetel, false jika tidak ada.
/** @param Closure(DriverUpgradedSocket, Request, Response): void $upgrade */
public function upgrade( Closure $ upgrade ): void Menyetel panggilan balik yang akan dipanggil setelah respons ditulis ke klien dan mengubah status respons menjadi 101 Switching Protocols . Callback menerima instance DriverUpgradedSocket , Request yang memulai pemutakhiran, dan Response ini.
Panggilan balik dapat dihapus dengan mengubah status menjadi selain 101.
public function getUpgradeCallable(): ? ClosureMengembalikan fungsi peningkatan jika ada.
/** @param Closure():void $onDispose */
public function onDispose( Closure $ onDispose ): voidMendaftarkan fungsi yang dipanggil ketika Respons dibuang. Respons akan dibuang setelah ditulis ke klien atau jika diganti dalam rantai middleware.
public function getTrailers(): Trailers Mengizinkan akses ke Trailers respons.
public function setTrailers( Trailers $ trailers ): void Menetapkan objek Trailers untuk digunakan dalam respons. Cuplikan dikirim setelah seluruh isi respons ditetapkan ke klien.
RequestBody , yang dikembalikan dari Request::getBody() , menyediakan akses buffered dan streaming ke isi permintaan. Gunakan akses streaming untuk menangani pesan berukuran besar, yang sangat penting jika Anda memiliki batas pesan yang lebih besar (seperti puluhan megabyte) dan tidak ingin melakukan buffering semuanya di memori. Jika beberapa orang mengunggah gambar berukuran besar secara bersamaan, memori mungkin akan cepat habis.
Oleh karena itu, penanganan bertahap sangatlah penting, dapat diakses melalui API read() dari AmpByteStreamReadableStream .
Jika klien terputus, read() gagal dengan AmpHttpServerClientException . Pengecualian ini diberikan untuk API read() dan buffer() .
Catatan
ClientExceptions tidak perlu ditangkap. Anda dapat menangkapnya jika ingin melanjutkan, tetapi tidak harus. Server akan secara diam-diam mengakhiri siklus permintaan dan kemudian membuang pengecualian itu.
Daripada menetapkan batas isi umum tinggi, Anda harus mempertimbangkan untuk meningkatkan batas isi hanya jika diperlukan, yang secara dinamis dapat dilakukan dengan metode increaseSizeLimit() pada RequestBody .
Catatan
RequestBodysendiri tidak menyediakan penguraian data formulir. Anda dapat menggunakanamphp/http-server-form-parserjika Anda membutuhkannya.
Seperti Request , jarang sekali perlu membuat instance RequestBody karena instance tersebut akan disediakan sebagai bagian dari Request .
public function __construct(
ReadableStream | string $ stream ,
? Closure $ upgradeSize = null ,
) public function increaseSizeLimit( int $ limit ): voidMeningkatkan batas ukuran isi secara dinamis untuk memungkinkan penangan permintaan individual menangani isi permintaan yang lebih besar daripada set default untuk server HTTP.
Kelas Trailers mengizinkan akses ke cuplikan permintaan HTTP, dapat diakses melalui Request::getTrailers() . null dikembalikan jika cuplikan tidak diharapkan berdasarkan permintaan. Trailers::await() mengembalikan Future yang diselesaikan dengan objek HttpMessage yang menyediakan metode untuk mengakses header trailer.
$ trailers = $ request -> getTrailers ();
$ message = $ trailers ?->await();Server HTTP tidak akan menjadi hambatan. Kesalahan konfigurasi, penggunaan pemblokiran I/O, atau aplikasi yang tidak efisien.
Server ini dioptimalkan dengan baik dan dapat menangani puluhan ribu permintaan per detik pada perangkat keras biasa sambil mempertahankan tingkat konkurensi yang tinggi pada ribuan klien.
Namun kinerja tersebut akan menurun drastis jika aplikasi tidak efisien. Server memiliki keuntungan yang bagus karena kelas dan penangannya selalu dimuat, sehingga tidak ada waktu yang terbuang saat kompilasi dan inisialisasi.
Jebakan yang umum terjadi adalah mulai mengoperasikan data besar dengan operasi string sederhana, yang memerlukan banyak salinan besar yang tidak efisien. Sebaliknya, streaming harus digunakan jika memungkinkan untuk badan permintaan dan respons yang lebih besar.
Masalahnya sebenarnya adalah biaya CPU. Manajemen I/O yang tidak efisien (selama tidak memblokir!) hanya menunda permintaan individual. Disarankan untuk mengirimkan secara bersamaan dan pada akhirnya menggabungkan beberapa permintaan I/O independen melalui kombinator Amp, namun penangan yang lambat juga akan memperlambat setiap permintaan lainnya. Saat satu penangan sedang melakukan komputasi, semua penangan lainnya tidak dapat melanjutkan. Oleh karena itu, sangat penting untuk mengurangi waktu komputasi penangan ke minimum.
Beberapa contoh dapat ditemukan di direktori ./examples repositori. Ini dapat dijalankan seperti skrip PHP biasa pada baris perintah.
php examples/hello-world.php Anda kemudian dapat mengakses server contoh di http://localhost:1337/ di browser Anda.
Jika Anda menemukan masalah apa pun terkait keamanan, harap gunakan pelapor masalah keamanan pribadi daripada menggunakan pelacak masalah publik.
Lisensi MIT (MIT). Silakan lihat LISENSI untuk informasi lebih lanjut.