AMPHP adalah kumpulan pustaka berbasis peristiwa untuk PHP yang dirancang dengan mempertimbangkan serat dan konkurensi. amphp/amp
secara khusus menyediakan masa depan dan pembatalan sebagai primitif mendasar untuk pemrograman asinkron. Kami sekarang menggunakan Revolt alih-alih mengirimkan implementasi event loop dengan amphp/amp
.
Amp banyak menggunakan serat yang dikirimkan dengan PHP 8.1 untuk menulis kode asinkron seperti kode pemblokiran sinkron. Berbeda dengan versi sebelumnya, coroutine atau callback berbasis generator tidak diperlukan. Mirip dengan thread, setiap fiber memiliki tumpukan panggilannya sendiri, tetapi fiber dijadwalkan secara kooperatif berdasarkan event loop. Gunakan Ampasync()
untuk menjalankan berbagai hal secara bersamaan.
Secara tradisional, PHP mengikuti model eksekusi berurutan. Mesin PHP mengeksekusi baris demi baris secara berurutan. Namun seringkali program terdiri dari beberapa sub-program independen yang dapat dijalankan secara bersamaan.
Jika Anda menanyakan database, Anda mengirimkan kueri dan menunggu respons dari server database dengan cara memblokir. Setelah Anda mendapat tanggapan, Anda dapat mulai melakukan hal berikutnya. Daripada hanya duduk diam dan tidak melakukan apa pun sambil menunggu, kita sudah bisa mengirimkan query database berikutnya, atau melakukan panggilan HTTP ke API. Mari manfaatkan waktu yang biasa kita habiskan untuk menunggu I/O!
Revolt memungkinkan operasi I/O secara bersamaan. Kami menjaga beban kognitif tetap rendah dengan menghindari panggilan balik. API kami dapat digunakan seperti perpustakaan lainnya, hanya saja semuanya juga berfungsi secara bersamaan, karena kami menggunakan I/O non-pemblokiran. Jalankan berbagai hal secara bersamaan menggunakan Ampasync()
dan tunggu hasilnya menggunakan Future::await()
di mana pun dan kapan pun Anda membutuhkannya!
Ada berbagai teknik untuk mengimplementasikan konkurensi di PHP selama bertahun-tahun, misalnya callback dan generator yang dikirimkan dalam PHP 5. Pendekatan ini mengalami masalah "Apa warna fungsi Anda", yang kami selesaikan dengan mengirimkan Fibers dengan PHP 8.1. Mereka memungkinkan konkurensi dengan beberapa tumpukan panggilan independen.
Fiber dijadwalkan secara kooperatif oleh event-loop, itulah sebabnya fiber juga disebut coroutine. Penting untuk dipahami bahwa hanya satu coroutine yang berjalan pada waktu tertentu, sementara itu semua coroutine lainnya ditangguhkan.
Anda dapat membandingkan coroutine dengan komputer yang menjalankan beberapa program menggunakan satu inti CPU. Setiap program mendapat slot waktu untuk dieksekusi. Namun, coroutine bukanlah tindakan preemptif. Mereka tidak mendapatkan slot waktu tetap. Mereka harus secara sukarela menyerahkan kendali pada loop peristiwa.
Fungsi pemblokiran I/O apa pun akan memblokir seluruh proses sambil menunggu I/O. Anda pasti ingin menghindarinya. Jika Anda belum membaca panduan instalasi, lihat contoh Hello World yang menunjukkan efek pemblokiran fungsi. Pustaka yang disediakan oleh AMPHP menghindari pemblokiran untuk I/O.
Paket ini dapat diinstal sebagai ketergantungan Komposer.
composer require amphp/amp
Jika Anda menggunakan perpustakaan ini, kemungkinan besar Anda ingin menjadwalkan acara menggunakan Revolt, yang harus Anda perlukan secara terpisah, meskipun secara otomatis diinstal sebagai dependensi.
composer require revolt/event-loop
Paket-paket ini menyediakan blok bangunan dasar untuk aplikasi asinkron/bersamaan di PHP. Kami menawarkan banyak paket yang dibangun di atas ini, misalnya
amphp/byte-stream
menyediakan abstraksi aliranamphp/socket
menyediakan lapisan soket untuk UDP dan TCP termasuk TLSamphp/parallel
menyediakan pemrosesan paralel untuk memanfaatkan beberapa inti CPU dan operasi pemblokiran offloadamphp/http-client
menyediakan klien HTTP/1.1 dan HTTP/2amphp/http-server
menyediakan server aplikasi HTTP/1.1 dan HTTP/2amphp/mysql
dan amphp/postgres
untuk akses database yang tidak memblokirPaket ini memerlukan PHP 8.1 atau lebih baru. Tidak diperlukan ekstensi!
Ekstensi hanya diperlukan jika aplikasi Anda memerlukan koneksi soket bersamaan dalam jumlah besar, biasanya batas ini dikonfigurasi hingga 1024 deskriptor file.
Coroutine adalah fungsi yang dapat diinterupsi. Di PHP, mereka dapat diimplementasikan menggunakan fiber.
Catatan Versi Amp sebelumnya menggunakan generator untuk tujuan serupa, namun serat dapat diinterupsi di mana saja dalam tumpukan panggilan sehingga boilerplate sebelumnya seperti
Ampcall()
tidak diperlukan.
Pada waktu tertentu, hanya satu fiber yang berjalan. Ketika coroutine ditangguhkan, eksekusi coroutine dihentikan sementara, sehingga tugas lain dapat dijalankan. Eksekusi dilanjutkan setelah pengatur waktu berakhir, operasi streaming dapat dilakukan, atau Future
yang ditunggu selesai.
Penangguhan tingkat rendah dan dimulainya kembali coroutine ditangani oleh API Suspension
Revolt.
<?php
require __DIR__ . ' /vendor/autoload.php ' ;
use Revolt EventLoop ;
$ suspension = EventLoop:: getSuspension ();
EventLoop:: delay ( 5 , function () use ( $ suspension ): void {
print ' ++ Executing callback created by EventLoop::delay() ' . PHP_EOL ;
$ suspension -> resume ( null );
});
print ' ++ Suspending to event loop... ' . PHP_EOL ;
$ suspension -> suspend ();
print ' ++ Script end ' . PHP_EOL ;
Callback yang terdaftar di event-loop Revolt secara otomatis dijalankan sebagai coroutine dan aman untuk ditangguhkan. Selain API event-loop, Ampasync()
dapat digunakan untuk memulai tumpukan panggilan independen.
<?php
use function Amp delay ;
require __DIR__ . ' /vendor/autoload.php ' ;
Amp async ( function () {
print ' ++ Executing callback passed to async() ' . PHP_EOL ;
delay ( 3 );
print ' ++ Finished callback passed to async() ' . PHP_EOL ;
});
print ' ++ Suspending to event loop... ' . PHP_EOL ;
delay ( 5 );
print ' ++ Script end ' . PHP_EOL ;
Future
adalah objek yang mewakili hasil akhir dari operasi asinkron. Ada tiga negara bagian:
Masa depan yang berhasil diselesaikan dianalogikan dengan nilai kembalian, sedangkan masa depan yang error dianalogikan dengan pelemparan pengecualian.
Salah satu cara untuk mendekati API asinkron adalah menggunakan callback yang diteruskan saat operasi dimulai dan dipanggil setelah operasi selesai:
doSomething ( function ( $ error , $ value ) {
if ( $ error ) {
/* ... */
} else {
/* ... */
}
});
Pendekatan panggilan balik memiliki beberapa kelemahan.
Di situlah masa depan berperan. Mereka adalah pengganti untuk hasil yang dikembalikan seperti nilai pengembalian lainnya. Penelepon mempunyai pilihan untuk menunggu hasilnya menggunakan Future::await()
atau mendaftarkan satu atau beberapa callback.
try {
$ value = doSomething ()-> await ();
} catch (...) {
/* ... */
}
Dalam aplikasi bersamaan, akan ada beberapa masa depan, di mana Anda mungkin ingin menunggu semuanya atau hanya yang pertama.
AmpFutureawait($iterable, $cancellation)
menunggu semua objek Future
dari iterable
. Jika salah satu instance Future
error, operasi akan dibatalkan dengan pengecualian tersebut. Jika tidak, hasilnya adalah array yang cocok dengan kunci dari input iterable
hingga nilai penyelesaiannya.
Kombinator await()
sangat kuat karena memungkinkan Anda menjalankan banyak operasi asinkron secara bersamaan. Mari kita lihat contoh penggunaan amphp/http-client
untuk mengambil beberapa sumber daya HTTP secara bersamaan:
<?php
use Amp Future ;
use Amp Http Client HttpClientBuilder ;
use Amp Http Client Request ;
$ httpClient = HttpClientBuilder:: buildDefault ();
$ uris = [
" google " => " https://www.google.com " ,
" news " => " https://news.google.com " ,
" bing " => " https://www.bing.com " ,
" yahoo " => " https://www.yahoo.com " ,
];
try {
$ responses = Future await ( array_map ( function ( $ uri ) use ( $ httpClient ) {
return Amp async ( fn () => $ httpClient -> request ( new Request ( $ uri , ' HEAD ' )));
}, $ uris ));
foreach ( $ responses as $ key => $ response ) {
printf (
" %s | HTTP/%s %d %s n" ,
$ key ,
$ response -> getProtocolVersion (),
$ response -> getStatus (),
$ response -> getReason ()
);
}
} catch ( Exception $ e ) {
// If any one of the requests fails the combo will fail
echo $ e -> getMessage (), "n" ;
}
AmpFutureawaitAnyN($count, $iterable, $cancellation)
sama dengan await()
kecuali ia menoleransi kesalahan individual. Hasil dikembalikan tepat setelah $count
instance dalam iterable
berhasil diselesaikan. Nilai yang dikembalikan adalah array nilai. Kunci individual dalam array komponen dipertahankan dari iterable
yang diteruskan ke fungsi untuk evaluasi.
AmpFutureawaitAll($iterable, $cancellation)
menunggu semua masa depan dan mengembalikan hasilnya sebagai array [$errors, $values]
.
AmpFutureawaitFirst($iterable, $cancellation)
membuka bungkusan Future
pertama yang diselesaikan, apakah berhasil diselesaikan atau mengalami kesalahan.
AmpFutureawaitAny($iterable, $cancellation)
membuka bungkus Future
pertama yang berhasil diselesaikan.
Masa depan dapat diciptakan dengan beberapa cara. Sebagian besar kode akan menggunakan Ampasync()
yang mengambil fungsi dan menjalankannya sebagai coroutine di Fiber lain.
Terkadang sebuah antarmuka mengharuskan Future
dikembalikan, namun hasilnya segera tersedia, misalnya karena disimpan dalam cache. Dalam kasus ini Future::complete(mixed)
dan Future::error(Throwable)
dapat digunakan untuk membuat Future
yang segera diselesaikan.
Catatan
DeferredFuture
API yang dijelaskan di bawah ini adalah API tingkat lanjut yang mungkin tidak diperlukan oleh banyak aplikasi. GunakanAmpasync()
atau kombinator jika memungkinkan.
AmpDeferredFuture
bertanggung jawab untuk menyelesaikan Future
yang tertunda. Anda membuat AmpDeferredFuture
dan menggunakan metode getFuture
untuk mengembalikan AmpFuture
ke pemanggil. Setelah hasilnya siap, Anda menyelesaikan Future
yang dipegang oleh penelepon menggunakan complete
atau error
pada DeferredFuture
yang tertaut.
final class DeferredFuture
{
public function getFuture (): Future ;
public function complete ( mixed $ value = null );
public function error ( Throwable $ throwable );
}
Peringatan Jika Anda meneruskan objek
DeferredFuture
, Anda mungkin melakukan kesalahan. Itu seharusnya merupakan kondisi internal operasi Anda.
Peringatan Anda tidak dapat menyelesaikan masa depan dengan masa depan yang lain; Gunakan
Future::await()
sebelum memanggilDeferredFuture::complete()
dalam kasus seperti itu.
Berikut adalah contoh sederhana dari penghasil nilai asinkron asyncMultiply()
yang membuat DeferredFuture
dan mengembalikan Future
terkait ke pemanggilnya.
<?php // Example async producer using DeferredFuture
use Revolt EventLoop ;
function asyncMultiply ( int $ x , int $ y ): Future
{
$ deferred = new Amp DeferredFuture ;
// Complete the async result one second from now
EventLoop:: delay ( 1 , function () use ( $ deferred , $ x , $ y ) {
$ deferred -> complete ( $ x * $ y );
});
return $ deferred -> getFuture ();
}
$ future = asyncMultiply ( 6 , 7 );
$ result = $ future -> await ();
var_dump ( $ result ); // int(42)
Setiap operasi yang mendukung pembatalan menerima instance Cancellation
sebagai argumen. Pembatalan adalah objek yang memungkinkan penangan yang mendaftar untuk berlangganan permintaan pembatalan. Objek-objek ini diturunkan ke sub-operasi atau harus ditangani oleh operasi itu sendiri.
$cancellation->throwIfRequested()
dapat digunakan untuk menggagalkan operasi saat ini dengan CancelledException
setelah pembatalan diminta. Meskipun throwIfRequested()
berfungsi dengan baik, beberapa operasi mungkin ingin berlangganan dengan callback. Mereka dapat melakukannya menggunakan Cancellation::subscribe()
untuk berlangganan permintaan pembatalan apa pun yang mungkin terjadi.
Penelepon membuat Cancellation
dengan menggunakan salah satu implementasi di bawah ini.
Catatan Pembatalan hanya bersifat saran. Penyelesai DNS mungkin mengabaikan permintaan pembatalan setelah kueri dikirim karena respons tetap harus diproses dan masih dapat di-cache. Klien HTTP mungkin melanjutkan permintaan HTTP yang hampir selesai untuk menggunakan kembali koneksi, namun mungkin membatalkan respons pengkodean yang terpotong karena tidak dapat mengetahui apakah melanjutkan sebenarnya lebih murah daripada membatalkan.
TimeoutCancellations
secara otomatis membatalkan dirinya sendiri setelah jumlah detik yang ditentukan.
request ( " ... " , new Amp TimeoutCancellation ( 30 ));
SignalCancellation
secara otomatis membatalkan dirinya sendiri setelah sinyal tertentu diterima oleh proses saat ini.
request ( " ... " , new Amp SignalCancellation ( SIGINT ));
DeferredCancellation
memungkinkan pembatalan manual dengan pemanggilan suatu metode. Ini adalah cara yang lebih disukai jika Anda perlu mendaftarkan beberapa panggilan balik khusus di suatu tempat daripada mengirimkan implementasi Anda sendiri. Hanya penelepon yang memiliki akses ke DeferredCancellation
dan dapat membatalkan operasi menggunakan DeferredCancellation::cancel()
.
$ deferredCancellation = new Amp DeferredCancellation ();
// Register some custom callback somewhere
onSomeEvent ( fn () => $ deferredCancellation -> cancel ());
request ( " ... " , $ deferredCancellation -> getCancellation ());
NullCancellation
tidak akan pernah dibatalkan. Pembatalan seringkali bersifat opsional, yang biasanya diterapkan dengan menjadikan parameter dapat dibatalkan. Untuk menghindari penjaga seperti if ($cancellation)
, NullCancellation
dapat digunakan sebagai gantinya.
$ cancellation ??= new NullCancellationToken ();
CompositeCancellation
menggabungkan beberapa objek pembatalan independen. Jika salah satu dari pembatalan ini dibatalkan, CompositeCancellation
itu sendiri akan dibatalkan.
amphp/amp
mengikuti spesifikasi versi semantik semver seperti semua paket amphp
lainnya.
Paket yang kompatibel harus menggunakan topik amphp
di GitHub.
Jika Anda menemukan masalah apa pun terkait keamanan, silakan kirim email ke [email protected]
alih-alih menggunakan pelacak masalah.
Lisensi MIT (MIT). Silakan lihat LICENSE
untuk informasi lebih lanjut.