AMPHP عبارة عن مجموعة من المكتبات المستندة إلى الأحداث لـ PHP والمصممة مع وضع الألياف والتزامن في الاعتبار. يوفر amphp/amp
على وجه التحديد العقود الآجلة والإلغاءات كأوليات أساسية للبرمجة غير المتزامنة. نحن الآن نستخدم Revolt بدلاً من شحن تنفيذ حلقة الحدث باستخدام amphp/amp
.
يستخدم Amp بشكل مكثف الألياف المشحونة مع PHP 8.1 لكتابة تعليمات برمجية غير متزامنة تمامًا مثل التعليمات البرمجية المحظورة المتزامنة. على النقيض من الإصدارات السابقة، ليست هناك حاجة إلى coroutines أو عمليات الاسترجاعات المستندة إلى المولد. على غرار الخيوط، كل ليف لديه مكدس استدعاء خاص به، ولكن تتم جدولة الألياف بشكل تعاوني بواسطة حلقة الحدث. استخدم Ampasync()
لتشغيل الأشياء بشكل متزامن.
تقليديا، PHP يتبع نموذج التنفيذ المتسلسل. يقوم محرك PHP بتنفيذ سطر تلو الآخر بترتيب تسلسلي. ومع ذلك، في كثير من الأحيان، تتكون البرامج من عدة برامج فرعية مستقلة يمكن تنفيذها بشكل متزامن.
إذا قمت بالاستعلام عن قاعدة بيانات، فإنك ترسل الاستعلام وتنتظر الرد من خادم قاعدة البيانات بطريقة الحظر. بمجرد حصولك على الرد، يمكنك البدء في فعل الشيء التالي. بدلاً من الجلوس هناك وعدم القيام بأي شيء أثناء الانتظار، يمكننا بالفعل إرسال استعلام قاعدة البيانات التالية، أو إجراء اتصال HTTP بواجهة برمجة التطبيقات (API). دعونا نستفيد من الوقت الذي نقضيه عادة في انتظار I/O!
يسمح Revolt بعمليات الإدخال/الإخراج المتزامنة. نحافظ على الحمل المعرفي منخفضًا عن طريق تجنب عمليات الاسترجاعات. يمكن استخدام واجهات برمجة التطبيقات الخاصة بنا مثل أي مكتبة أخرى، باستثناء أن الأشياء تعمل أيضًا بشكل متزامن، لأننا نستخدم عمليات الإدخال/الإخراج غير المحظورة تحت الغطاء. قم بتشغيل الأشياء بشكل متزامن باستخدام Ampasync()
وانتظر النتيجة باستخدام Future::await()
أينما ومتى تحتاج إليها!
كانت هناك تقنيات مختلفة لتنفيذ التزامن في PHP على مر السنين، على سبيل المثال عمليات الاسترجاعات والمولدات التي تم شحنها في PHP 5. عانت هذه الأساليب من مشكلة "ما هو لون وظيفتك"، والتي قمنا بحلها عن طريق شحن الألياف باستخدام PHP 8.1. أنها تسمح بالتزامن مع مكدسات مكالمات مستقلة متعددة.
تتم جدولة الألياف بشكل تعاوني من خلال حلقة الحدث، ولهذا السبب يطلق عليها أيضًا اسم coroutines. من المهم أن نفهم أنه يتم تشغيل كوروتين واحد فقط في أي وقت، ويتم تعليق جميع الكوروتينات الأخرى في هذه الأثناء.
يمكنك مقارنة coroutines بجهاز كمبيوتر يقوم بتشغيل برامج متعددة باستخدام نواة وحدة المعالجة المركزية (CPU) واحدة. يحصل كل برنامج على فترة زمنية للتنفيذ. ومع ذلك، فإن Coroutines ليست وقائية. لا يحصلون على الموعد المحدد لهم. عليهم أن يتخلوا طوعًا عن السيطرة على حلقة الحدث.
تقوم أي وظيفة حظر للإدخال/الإخراج بحظر العملية بأكملها أثناء انتظار الإدخال/الإخراج. سوف ترغب في تجنبها. إذا لم تكن قد قرأت دليل التثبيت، فقم بإلقاء نظرة على مثال Hello World الذي يوضح تأثير وظائف الحظر. تتجنب المكتبات التي توفرها AMPHP حظر الإدخال/الإخراج.
يمكن تثبيت هذه الحزمة باعتبارها تبعية للملحن.
composer require amphp/amp
إذا كنت تستخدم هذه المكتبة، فمن المحتمل جدًا أنك تريد جدولة الأحداث باستخدام Revolt، وهو ما يجب أن تطلبه بشكل منفصل، حتى لو تم تثبيته تلقائيًا باعتباره تبعية.
composer require revolt/event-loop
توفر هذه الحزم اللبنات الأساسية للتطبيقات غير المتزامنة/المتزامنة في PHP. نحن نقدم الكثير من الحزم المبنية فوق هذه، على سبيل المثال
amphp/byte-stream
تجريدًا للدفقamphp/socket
طبقة مأخذ توصيل لـ UDP وTCP بما في ذلك TLSamphp/parallel
معالجة متوازية للاستفادة من نوى وحدة المعالجة المركزية المتعددة وعمليات حظر التفريغamphp/http-client
يوفر عميل HTTP/1.1 وHTTP/2amphp/http-server
خادم تطبيقات HTTP/1.1 وHTTP/2amphp/mysql
و amphp/postgres
للوصول إلى قاعدة البيانات غير المحظورةتتطلب هذه الحزمة PHP 8.1 أو الأحدث. لا ملحقات المطلوبة!
تكون الإضافات مطلوبة فقط إذا كان تطبيقك يتطلب عددًا كبيرًا من اتصالات المقبس المتزامنة، وعادةً ما يتم تكوين هذا الحد حتى 1024 واصف ملف.
Coroutines هي وظائف قابلة للمقاطعة. في PHP، يمكن تنفيذها باستخدام الألياف.
ملاحظة: استخدمت الإصدارات السابقة من Amp مولدات لغرض مشابه، ولكن يمكن مقاطعة الألياف في أي مكان في مكدس الاستدعاءات مما يجعل النموذج المعياري السابق مثل
Ampcall()
غير ضروري.
في أي وقت، يتم تشغيل ليف واحد فقط. عندما يتم تعليق الكوروتين، يتم مقاطعة تنفيذ الكوروتين مؤقتًا، مما يسمح بتشغيل مهام أخرى. يتم استئناف التنفيذ بمجرد انتهاء صلاحية المؤقت، أو إمكانية إجراء عمليات الدفق، أو اكتمال أي Future
منتظر.
تتم معالجة التعليق منخفض المستوى واستئناف coroutines بواسطة Revolt's Suspension
API.
<?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 ;
يتم تشغيل عمليات الاسترجاعات المسجلة في حلقة حدث Revolt تلقائيًا كإجراءات روتينية ومن الآمن تعليقها. بصرف النظر عن واجهة برمجة تطبيقات حلقة الأحداث، يمكن استخدام Ampasync()
لبدء مكدس استدعاءات مستقل.
<?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
هو كائن يمثل النتيجة النهائية لعملية غير متزامنة. هناك ثلاث ولايات:
المستقبل المكتمل بنجاح يشبه القيمة المرجعة، في حين أن المستقبل الخاطئ يشبه رمي استثناء.
إحدى طرق التعامل مع واجهات برمجة التطبيقات غير المتزامنة هي استخدام عمليات الاسترجاعات التي يتم تمريرها عند بدء العملية ويتم استدعاؤها بمجرد اكتمالها:
doSomething ( function ( $ error , $ value ) {
if ( $ error ) {
/* ... */
} else {
/* ... */
}
});
أسلوب رد الاتصال له عيوب عديدة.
وهنا يأتي دور العقود الآجلة. إنها عناصر نائبة للنتيجة التي يتم إرجاعها مثل أي قيمة إرجاع أخرى. لدى المتصل خيار انتظار النتيجة باستخدام Future::await()
أو تسجيل رد اتصال واحد أو أكثر.
try {
$ value = doSomething ()-> await ();
} catch (...) {
/* ... */
}
في التطبيقات المتزامنة، سيكون هناك العديد من العقود الآجلة، حيث قد ترغب في انتظارها جميعًا أو الأول فقط.
AmpFutureawait($iterable, $cancellation)
ينتظر جميع الكائنات Future
لكائن iterable
. إذا حدث خطأ في أحد المثيلات Future
، فسيتم إحباط العملية مع هذا الاستثناء. وبخلاف ذلك، ستكون النتيجة عبارة عن مصفوفة مطابقة للمفاتيح من المدخلات iterable
مع قيم الاكتمال الخاصة بها.
يعتبر مُجمِع await()
قويًا للغاية لأنه يسمح لك بتنفيذ العديد من العمليات غير المتزامنة في نفس الوقت. دعونا نلقي نظرة على مثال يستخدم amphp/http-client
لاسترداد موارد HTTP متعددة بشكل متزامن:
<?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)
هو نفس await()
باستثناء أنه يتحمل الأخطاء الفردية. يتم إرجاع النتيجة بمجرد اكتمال مثيلات $count
في الملف iterable
بنجاح. القيمة المرجعة هي مجموعة من القيم. يتم الاحتفاظ بالمفاتيح الفردية في مصفوفة المكونات من iterable
الذي تم تمريره إلى الوظيفة للتقييم.
AmpFutureawaitAll($iterable, $cancellation)
ينتظر جميع العقود الآجلة ويعيد نتائجها كمصفوفة [$errors, $values]
.
يقوم AmpFutureawaitFirst($iterable, $cancellation)
بإلغاء تغليف أول Future
مكتمل، سواء تم إكماله بنجاح أو حدث خطأ.
يقوم AmpFutureawaitAny($iterable, $cancellation)
بكشف أول ملف Future
.
يمكن إنشاء العقود الآجلة بعدة طرق. ستستخدم معظم التعليمات البرمجية Ampasync()
الذي يأخذ وظيفة ويقوم بتشغيلها ككوروتين في ألياف أخرى.
في بعض الأحيان تتطلب الواجهة إرجاع Future
، لكن النتائج تكون متاحة على الفور، على سبيل المثال لأنها مخزنة مؤقتًا. في هذه الحالات، يمكن استخدام Future::complete(mixed)
و Future::error(Throwable)
لإنشاء Future
مكتمل على الفور.
ملاحظة: واجهة برمجة تطبيقات
DeferredFuture
الموضحة أدناه هي واجهة برمجة تطبيقات متقدمة ربما لا تحتاج إليها العديد من التطبيقات. استخدمAmpasync()
أو أدوات التجميع بدلاً من ذلك حيثما أمكن ذلك.
AmpDeferredFuture
هو المسؤول عن إكمال Future
المعلق. يمكنك إنشاء AmpDeferredFuture
واستخدام أسلوب getFuture
الخاص به لإرجاع AmpFuture
إلى المتصل. بمجرد أن تصبح النتيجة جاهزة، يمكنك إكمال Future
الذي يحتفظ به المتصل باستخدام complete
أو error
في DeferredFuture
المرتبط.
final class DeferredFuture
{
public function getFuture (): Future ;
public function complete ( mixed $ value = null );
public function error ( Throwable $ throwable );
}
تحذير إذا كنت تقوم بتمرير كائنات
DeferredFuture
، فمن المحتمل أنك تفعل شيئًا خاطئًا. من المفترض أن تكون الحالة الداخلية لعمليتك.
تحذير: لا يمكنك إكمال مستقبل بمستقبل آخر؛ استخدم
Future::await()
قبل استدعاءDeferredFuture::complete()
في مثل هذه الحالات.
فيما يلي مثال بسيط لمنتج القيمة غير المتزامنة asyncMultiply()
الذي ينشئ DeferredFuture
ويعيد Future
المرتبط إلى المتصل به.
<?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)
تقبل كل عملية تدعم الإلغاء مثيل Cancellation
كوسيطة. الإلغاءات هي كائنات تسمح لمعالجات التسجيل بالاشتراك في طلبات الإلغاء. يتم تمرير هذه الكائنات إلى العمليات الفرعية أو يجب معالجتها بواسطة العملية نفسها.
يمكن استخدام $cancellation->throwIfRequested()
لفشل العملية الحالية باستخدام CancelledException
بمجرد طلب الإلغاء. على الرغم من أن throwIfRequested()
يعمل بشكل جيد، إلا أن بعض العمليات قد ترغب في الاشتراك باستخدام رد اتصال بدلاً من ذلك. يمكنهم القيام بذلك باستخدام Cancellation::subscribe()
للاشتراك في أي طلبات إلغاء قد تحدث.
يقوم المتصل بإنشاء Cancellation
باستخدام أحد التطبيقات أدناه.
ملاحظة الإلغاءات هي استشارية فقط. قد يتجاهل محلل DNS طلبات الإلغاء بعد إرسال الاستعلام حيث يجب معالجة الاستجابة على أي حال ولا يزال من الممكن تخزينها مؤقتًا. قد يستمر عميل HTTP في طلب HTTP منتهي تقريبًا لإعادة استخدام الاتصال، ولكنه قد يلغي استجابة ترميز مقسمة لأنه لا يستطيع معرفة ما إذا كانت المتابعة أرخص بالفعل من الإجهاض.
يقوم TimeoutCancellations
بإلغاء نفسه تلقائيًا بعد عدد الثواني المحدد.
request ( " ... " , new Amp TimeoutCancellation ( 30 ));
يقوم SignalCancellation
بإلغاء نفسه تلقائيًا بعد تلقي إشارة محددة بواسطة العملية الحالية.
request ( " ... " , new Amp SignalCancellation ( SIGINT ));
يسمح الإلغاء DeferredCancellation
بالإلغاء اليدوي من خلال استدعاء الطريقة. هذه هي الطريقة المفضلة إذا كنت بحاجة إلى تسجيل بعض عمليات رد الاتصال المخصصة في مكان ما بدلاً من شحن التنفيذ الخاص بك. المتصل فقط لديه حق الوصول إلى DeferredCancellation
ويمكنه إلغاء العملية باستخدام DeferredCancellation::cancel()
.
$ deferredCancellation = new Amp DeferredCancellation ();
// Register some custom callback somewhere
onSomeEvent ( fn () => $ deferredCancellation -> cancel ());
request ( " ... " , $ deferredCancellation -> getCancellation ());
لن يتم إلغاء NullCancellation
أبدًا. غالبًا ما يكون الإلغاء اختياريًا، ويتم تنفيذه عادةً عن طريق جعل المعلمة فارغة. لتجنب الحراس مثل if ($cancellation)
، يمكن استخدام NullCancellation
بدلاً من ذلك.
$ cancellation ??= new NullCancellationToken ();
يجمع CompositeCancellation
بين عدة كائنات إلغاء مستقلة. إذا تم إلغاء أي من عمليات الإلغاء هذه، فسيتم إلغاء CompositeCancellation
نفسه.
يتبع amphp/amp
مواصفات الإصدار الدلالي semver مثل جميع حزم amphp
الأخرى.
يجب أن تستخدم الحزم المتوافقة موضوع amphp
على GitHub.
إذا اكتشفت أي مشكلات متعلقة بالأمان، فيرجى إرسال بريد إلكتروني [email protected]
بدلاً من استخدام أداة تعقب المشكلات.
رخصة معهد ماساتشوستس للتكنولوجيا (MIT). يرجى الاطلاع على LICENSE
لمزيد من المعلومات.