تطبيق خفيف الوزن لـ CommonJS Promises/A لـ PHP.
Promise هي مكتبة تنفذ CommonJS Promises/A لـ PHP.
كما يوفر أيضًا العديد من المفاهيم المفيدة الأخرى المتعلقة بالوعود، مثل الانضمام إلى الوعود المتعددة ورسم الخرائط وتقليل مجموعات الوعود.
إذا لم تسمع عن الوعود من قبل، فاقرأ هذا أولاً.
يمثل المؤجل عملية حسابية أو وحدة عمل ربما لم تكتمل بعد. عادةً (ولكن ليس دائمًا)، ستكون هذه العملية الحسابية شيئًا يتم تنفيذه بشكل غير متزامن ويكتمل في مرحلة ما في المستقبل.
في حين أن المؤجل يمثل الحساب نفسه، فإن الوعد يمثل نتيجة هذا الحساب. وبالتالي، فإن كل مؤجل له وعد يعمل كعنصر نائب لنتيجته الفعلية.
يمثل المؤجل عملية لا يزال حلها معلقًا. لديها أجزاء منفصلة للوعد والمحلل.
$ deferred = new React Promise Deferred ();
$ promise = $ deferred -> promise ();
$ deferred -> resolve (mixed $ value );
$ deferred -> reject (Throwable $ reason );
طريقة promise
ترجع الوعد المؤجل.
تتحكم طريقتا resolve
reject
في حالة المؤجل.
يقبل مُنشئ Deferred
الوسيطة الاختيارية $canceller
. انظر الوعد لمزيد من المعلومات.
$ promise = $ deferred -> promise ();
إرجاع الوعد المؤجل، والذي يمكنك تسليمه للآخرين مع الاحتفاظ بصلاحية تعديل حالته لنفسك.
$ deferred -> resolve (mixed $ value );
يحل الوعد الذي تم إرجاعه بواسطة promise()
. يتم إخطار جميع المستهلكين من خلال استدعاء $onFulfilled
(الذي قاموا بتسجيله عبر $promise->then()
) باستخدام $value
.
إذا كانت $value
بحد ذاتها وعدًا، فسينتقل الوعد إلى حالة هذا الوعد بمجرد حله.
انظر أيضًا وظيفة resolve()
.
$ deferred -> reject (Throwable $ reason );
يرفض الوعد الذي تم إرجاعه بواسطة promise()
، مما يشير إلى فشل حساب المؤجل. يتم إخطار جميع المستهلكين من خلال استدعاء $onRejected
(الذي قاموا بتسجيله عبر $promise->then()
) باستخدام $reason
.
انظر أيضًا وظيفة reject()
.
توفر واجهة الوعد واجهة مشتركة لجميع عمليات تنفيذ الوعد. راجع الوعد للتنفيذ العام الوحيد الذي كشفته هذه الحزمة.
يمثل الوعد نتيجة نهائية، والتي تكون إما الوفاء (النجاح) وقيمة مرتبطة به، أو الرفض (الفشل) والسبب المرتبط به.
بمجرد وصول الوعد إلى حالة الوفاء أو الرفض، يصبح غير قابل للتغيير. لا يمكن تعديل حالته ولا نتيجته (أو خطأه).
$ transformedPromise = $ promise -> then (callable $ onFulfilled = null , callable $ onRejected = null );
يحول قيمة الوعد عن طريق تطبيق دالة على قيمة الوفاء بالوعد أو رفضه. إرجاع وعد جديد للنتيجة المحولة.
يسجل التابع then()
معالجات جديدة تم تنفيذها ومعالجات الرفض مع وعد (جميع المعلمات اختيارية):
$onFulfilled
بمجرد الوفاء بالوعد وتمرير النتيجة كوسيطة أولى.$onRejected
بمجرد رفض الوعد وتمرير السبب باعتباره الوسيط الأول. تقوم بإرجاع وعد جديد سيتم الوفاء به بقيمة الإرجاع $onFulfilled
أو $onRejected
، أيهما يتم استدعاؤه، أو سيتم رفضه مع الاستثناء الذي تم طرحه في حالة رمي أي منهما.
يقدم الوعد الضمانات التالية بشأن المعالجات المسجلة في نفس الاستدعاء لـ then()
:
$onFulfilled
أو $onRejected
، وليس كليهما أبدًا.$onFulfilled
و $onRejected
أكثر من مرة. $ promise -> catch (callable $ onRejected );
يسجل معالج الرفض للوعد. وهو اختصار لـ:
$ promise -> then ( null , $ onRejected );
بالإضافة إلى ذلك، يمكنك كتابة تلميح للوسيطة $reason
الخاصة بـ $onRejected
لرصد أخطاء محددة فقط.
$ promise
-> catch ( function ( RuntimeException $ reason ) {
// Only catch RuntimeException instances
// All other types of errors will propagate automatically
})
-> catch ( function ( Throwable $ reason ) {
// Catch other errors
});
$ newPromise = $ promise -> finally (callable $ onFulfilledOrRejected );
يسمح لك بتنفيذ مهام من نوع "التنظيف" في سلسلة الوعد.
يقوم بترتيب استدعاء $onFulfilledOrRejected
، بدون وسائط، عند الوفاء بالوعد أو رفضه.
$promise
، وتم إرجاع $onFulfilledOrRejected
بنجاح، فسيتم تنفيذ $newPromise
بنفس قيمة $promise
.$promise
، وألقى $onFulfilledOrRejected
وعدًا مرفوضًا أو أعاده، فسيرفض $newPromise
مع الاستثناء المطروح أو سبب رفض الوعد.$promise
، وتم إرجاع $onFulfilledOrRejected
بنجاح، فسيتم رفض $newPromise
لنفس سبب $promise
.$promise
، وألقى $onFulfilledOrRejected
وعدًا مرفوضًا أو أعاده، فسيرفض $newPromise
مع الاستثناء المطروح أو سبب رفض الوعد. finally()
يتصرف بشكل مشابه للبيان المتزامن أخيرًا. عند دمجها مع catch()
، تسمح لك finally()
بكتابة تعليمة برمجية مشابهة للزوج المتزامن المألوف Catch/finally.
خذ بعين الاعتبار التعليمات البرمجية المتزامنة التالية:
try {
return doSomething ();
} catch ( Throwable $ e ) {
return handleError ( $ e );
} finally {
cleanup ();
}
يمكن كتابة تعليمات برمجية غير متزامنة مماثلة (باستخدام doSomething()
التي تُرجع وعدًا):
return doSomething ()
-> catch ( ' handleError ' )
-> finally ( ' cleanup ' );
$ promise -> cancel ();
تُعلم طريقة cancel()
منشئ الوعد بعدم وجود اهتمام آخر بنتائج العملية.
بمجرد تسوية الوعد (سواء تم الوفاء به أو رفضه)، فإن استدعاء cancel()
على الوعد ليس له أي تأثير.
تم إهماله منذ الإصدار 3.0.0، راجع
catch()
بدلاً من ذلك.
يقوم otherwise()
بتسجيل معالج رفض الوعد.
تستمر هذه الطريقة في الوجود فقط لأسباب تتعلق بالإصدار BC ولتسهيل الترقية بين الإصدارات. وهو اسم مستعار لـ:
$ promise -> catch ( $ onRejected );
تم إهماله منذ الإصدار 3.0.0، راجع
finally()
بدلاً من ذلك.
تتيح لك الطريقة always()
تنفيذ مهام من نوع "التنظيف" في سلسلة الوعد.
تستمر هذه الطريقة في الوجود فقط لأسباب تتعلق بالإصدار BC ولتسهيل الترقية بين الإصدارات. وهو اسم مستعار لـ:
$ promise -> finally ( $ onFulfilledOrRejected );
ينشئ وعدًا يتم التحكم في حالته من خلال الوظائف التي تم تمريرها إلى $resolver
.
$ resolver = function ( callable $ resolve , callable $ reject ) {
// Do some work, possibly asynchronously, and then
// resolve or reject.
$ resolve ( $ awesomeResult );
// or throw new Exception('Promise rejected');
// or $resolve($anotherPromise);
// or $reject($nastyError);
};
$ canceller = function () {
// Cancel/abort any running operations like network connections, streams etc.
// Reject promise by throwing an exception
throw new Exception ( ' Promise cancelled ' );
};
$ promise = new React Promise Promise ( $ resolver , $ canceller );
يتلقى منشئ الوعد وظيفة محلل ووظيفة إلغاء اختيارية سيتم استدعاؤهما باستخدام وسيطتين:
$resolve($value)
- الوظيفة الأساسية التي تحدد مصير الوعد الذي تم إرجاعه. يقبل إما قيمة غير واعدة، أو وعدًا آخر. عند استدعائه بقيمة غير وعدية، فإنه يفي بالوعد بهذه القيمة. عند استدعائه مع وعد آخر، على سبيل المثال $resolve($otherPromise)
، فإن مصير الوعد سيكون معادلاً لمصير $otherPromise
.$reject($reason)
- الدالة التي ترفض الوعد. يوصى برمي استثناء بدلاً من استخدام $reject()
.إذا طرح المُحلل أو المُلغي استثناءً، فسيتم رفض الوعد مع طرح هذا الاستثناء كسبب للرفض.
سيتم استدعاء وظيفة المحلل على الفور، وسيتم استدعاء وظيفة الإلغاء فقط عندما يقوم جميع المستهلكين باستدعاء طريقة cancel()
للوعد.
وظائف مفيدة لإنشاء مجموعات الوعود والانضمام إليها.
جميع الدوال التي تعمل على مجموعات الوعد (مثل all()
و race()
وما إلى ذلك) تدعم الإلغاء. هذا يعني أنه إذا قمت باستدعاء cancel()
على الوعد الذي تم إرجاعه، فسيتم إلغاء جميع الوعود الموجودة في المجموعة.
$ promise = React Promise resolve (mixed $ promiseOrValue );
ينشئ وعدًا للقيمة $promiseOrValue
المتوفرة.
إذا كانت $promiseOrValue
قيمة، فستكون قيمة دقة الوعد الذي تم إرجاعه.
إذا كان $promiseOrValue
قابلاً للقابلية (أي كائن يوفر طريقة then()
)، فسيتم إرجاع الوعد الموثوق الذي يتبع حالة القابلية.
إذا كان $promiseOrValue
عبارة عن وعد، فسيتم إرجاعه كما هو.
ينفذ $promise
الناتج واجهة PromiseInterface
ويمكن استهلاكه مثل أي وعد آخر:
$ promise = React Promise resolve ( 42 );
$ promise -> then ( function ( int $ result ): void {
var_dump ( $ result );
}, function ( Throwable $ e ): void {
echo ' Error: ' . $ e -> getMessage () . PHP_EOL ;
});
$ promise = React Promise reject (Throwable $ reason );
ينشئ وعدًا مرفوضًا $reason
المقدم.
لاحظ أن الواجهة Throwable
المقدمة في PHP 7 تغطي كلاً من أخطاء PHP الداخلية Exception
الخاصة بأرض المستخدم و Error
. من خلال فرض Throwable
كسبب لرفض الوعد، يمكن استخدام أي خطأ لغوي أو استثناء لأرض المستخدم لرفض الوعد.
ينفذ $promise
الناتج واجهة PromiseInterface
ويمكن استهلاكه مثل أي وعد آخر:
$ promise = React Promise reject ( new RuntimeException ( ' Request failed ' ));
$ promise -> then ( function ( int $ result ): void {
var_dump ( $ result );
}, function ( Throwable $ e ): void {
echo ' Error: ' . $ e -> getMessage () . PHP_EOL ;
});
لاحظ أنه يجب دائمًا التعامل مع الوعود المرفوضة بنفس الطريقة التي يجب بها دائمًا اكتشاف أي استثناءات في كتلة try
+ catch
. إذا قمت بإزالة المرجع الأخير إلى وعد مرفوض لم تتم معالجته، فسيتم الإبلاغ عن رفض وعد غير معالج:
function incorrect (): int
{
$ promise = React Promise reject ( new RuntimeException ( ' Request failed ' ));
// Commented out: No rejection handler registered here.
// $promise->then(null, function (Throwable $e): void { /* ignore */ });
// Returning from a function will remove all local variable references, hence why
// this will report an unhandled promise rejection here.
return 42 ;
}
// Calling this function will log an error message plus its stack trace:
// Unhandled promise rejection with RuntimeException: Request failed in example.php:10
incorrect ();
سيتم اعتبار الوعد المرفوض "تم التعامل معه" إذا عرفت سبب الرفض إما باستخدام طريقة then()
أو طريقة catch()
أو طريقة finally()
. لاحظ أن كل طريقة من هذه الطرق تُرجع وعدًا جديدًا قد يتم رفضه مرة أخرى إذا قمت بإعادة طرح استثناء.
سيتم أيضًا اعتبار الوعد المرفوض "تم التعامل معه" إذا قمت بإلغاء العملية باستخدام طريقة cancel()
(والتي بدورها سترفض الوعد إذا كان لا يزال معلقًا).
راجع أيضًا وظيفة set_rejection_handler()
.
$ promise = React Promise all (iterable $ promisesOrValues );
يُرجع وعدًا سيتم حله فقط بمجرد حل جميع العناصر الموجودة في $promisesOrValues
. ستكون قيمة دقة الوعد الذي تم إرجاعه عبارة عن مصفوفة تحتوي على قيم دقة كل عنصر في $promisesOrValues
.
$ promise = React Promise race (iterable $ promisesOrValues );
يبدأ سباقًا تنافسيًا يسمح بفائز واحد. إرجاع وعد تم حله بنفس الطريقة التي تم بها حل الوعد الأول.
سيصبح الوعد الذي تم إرجاعه معلقًا إلى ما لا نهاية إذا كان $promisesOrValues
يحتوي على 0 عنصر.
$ promise = React Promise any (iterable $ promisesOrValues );
تُرجع وعدًا سيتم حله عند حل أي عنصر من العناصر الموجودة في $promisesOrValues
. ستكون قيمة دقة الوعد الذي تم إرجاعه هي قيمة دقة عنصر التشغيل.
لن يتم رفض الوعد الذي تم إرجاعه إلا إذا تم رفض جميع العناصر الموجودة في $promisesOrValues
. ستكون قيمة الرفض هي ReactPromiseExceptionCompositeException
والتي تحمل جميع أسباب الرفض. يمكن الحصول على أسباب الرفض باستخدام CompositeException::getThrowables()
.
سيتم أيضًا رفض الوعد الذي تم إرجاعه باستخدام ReactPromiseExceptionLengthException
إذا كان $promisesOrValues
يحتوي على 0 عنصر.
React Promise set_rejection_handler(?callable $ callback ): ?callable;
يضبط معالج الرفض الشامل لحالات رفض الوعد غير المعالجة.
لاحظ أنه يجب دائمًا التعامل مع الوعود المرفوضة بنفس الطريقة التي يجب بها دائمًا اكتشاف أي استثناءات في كتلة try
+ catch
. إذا قمت بإزالة المرجع الأخير إلى وعد مرفوض لم تتم معالجته، فسيتم الإبلاغ عن رفض الوعد غير المعالج. راجع أيضًا وظيفة reject()
لمزيد من التفاصيل.
يجب أن تكون وسيطة ?callable $callback
وظيفة رد اتصال صالحة تقبل وسيطة Throwable
واحدة أو قيمة null
لاستعادة معالج رفض الوعد الافتراضي. سيتم تجاهل القيمة المرجعة لوظيفة رد الاتصال ولن يكون لها أي تأثير، لذا يجب عليك إرجاع قيمة void
. يجب ألا يتم طرح وظيفة رد الاتصال وإلا سيتم إنهاء البرنامج بسبب خطأ فادح.
تقوم الدالة بإرجاع معالج الرفض السابق أو null
في حالة استخدام معالج رفض الوعد الافتراضي.
سيقوم معالج رفض الوعد الافتراضي بتسجيل رسالة خطأ بالإضافة إلى تتبع المكدس الخاص بها:
// Unhandled promise rejection with RuntimeException: Unhandled in example.php:2
React Promise reject ( new RuntimeException ( ' Unhandled ' ));
يمكن استخدام معالج رفض الوعد لاستخدام تخصيص رسالة السجل أو الكتابة إلى أهداف السجل المخصصة. كقاعدة عامة، يجب استخدام هذه الوظيفة فقط كملاذ أخير ومن الأفضل التعامل مع حالات رفض الوعد إما باستخدام طريقة then()
، أو طريقة catch()
، أو طريقة finally()
. راجع أيضًا وظيفة reject()
لمزيد من التفاصيل.
function getAwesomeResultPromise ()
{
$ deferred = new React Promise Deferred ();
// Execute a Node.js-style function using the callback pattern
computeAwesomeResultAsynchronously ( function ( Throwable $ error , $ result ) use ( $ deferred ) {
if ( $ error ) {
$ deferred -> reject ( $ error );
} else {
$ deferred -> resolve ( $ result );
}
});
// Return the promise
return $ deferred -> promise ();
}
getAwesomeResultPromise ()
-> then (
function ( $ value ) {
// Deferred resolved, do something with $value
},
function ( Throwable $ reason ) {
// Deferred rejected, do something with $reason
}
);
بعض الأمثلة البسيطة لإظهار كيفية عمل آليات إعادة توجيه الوعود/A. هذه الأمثلة مفتعلة بالطبع، وفي الاستخدام الحقيقي، عادةً ما يتم نشر سلاسل الوعد عبر عدة استدعاءات وظيفية، أو حتى عدة مستويات من بنية التطبيق الخاص بك.
الوعود التي تم حلها تعيد توجيه قيم الدقة إلى الوعد التالي. سيتم حل الوعد الأول، $deferred->promise()
، بالقيمة التي تم تمريرها إلى $deferred->resolve()
أدناه.
يُرجع كل استدعاء لـ then()
وعدًا جديدًا سيتم حله باستخدام القيمة المرجعة للمعالج السابق. وهذا يخلق "خط أنابيب" الوعد.
$ deferred = new React Promise Deferred ();
$ deferred -> promise ()
-> then ( function ( $ x ) {
// $x will be the value passed to $deferred->resolve() below
// and returns a *new promise* for $x + 1
return $ x + 1 ;
})
-> then ( function ( $ x ) {
// $x === 2
// This handler receives the return value of the
// previous handler.
return $ x + 1 ;
})
-> then ( function ( $ x ) {
// $x === 3
// This handler receives the return value of the
// previous handler.
return $ x + 1 ;
})
-> then ( function ( $ x ) {
// $x === 4
// This handler receives the return value of the
// previous handler.
echo ' Resolve ' . $ x ;
});
$ deferred -> resolve ( 1 ); // Prints "Resolve 4"
تتصرف الوعود المرفوضة بشكل مشابه، وتعمل أيضًا بشكل مشابه للمحاولة/الالتقاط: عندما تكتشف استثناءً، يجب عليك إعادة طرحه حتى يتم نشره.
وبالمثل، عندما تتعامل مع وعد مرفوض، لنشر الرفض، "أعده" إما عن طريق إرجاع الوعد المرفوض، أو الرمي فعليًا (نظرًا لأن الوعد يترجم الاستثناءات المقذوفة إلى رفض)
$ deferred = new React Promise Deferred ();
$ deferred -> promise ()
-> then ( function ( $ x ) {
throw new Exception ( $ x + 1 );
})
-> catch ( function ( Exception $ x ) {
// Propagate the rejection
throw $ x ;
})
-> catch ( function ( Exception $ x ) {
// Can also propagate by returning another rejection
return React Promise reject (
new Exception ( $ x -> getMessage () + 1 )
);
})
-> catch ( function ( $ x ) {
echo ' Reject ' . $ x -> getMessage (); // 3
});
$ deferred -> resolve ( 1 ); // Prints "Reject 3"
تمامًا مثل المحاولة/الالتقاط، يمكنك اختيار النشر أم لا. سيؤدي خلط القرارات وحالات الرفض إلى إعادة توجيه نتائج المعالج بطريقة يمكن التنبؤ بها.
$ deferred = new React Promise Deferred ();
$ deferred -> promise ()
-> then ( function ( $ x ) {
return $ x + 1 ;
})
-> then ( function ( $ x ) {
throw new Exception ( $ x + 1 );
})
-> catch ( function ( Exception $ x ) {
// Handle the rejection, and don't propagate.
// This is like catch without a rethrow
return $ x -> getMessage () + 1 ;
})
-> then ( function ( $ x ) {
echo ' Mixed ' . $ x ; // 4
});
$ deferred -> resolve ( 1 ); // Prints "Mixed 4"
الطريقة الموصى بها لتثبيت هذه المكتبة هي من خلال Composer. جديد للملحن؟
يتبع هذا المشروع SemVer. سيؤدي هذا إلى تثبيت أحدث إصدار مدعوم من هذا الفرع:
composer require react/promise:^3.2
راجع أيضًا سجل التغيير للحصول على تفاصيل حول ترقيات الإصدار.
يهدف هذا المشروع إلى التشغيل على أي نظام أساسي وبالتالي لا يتطلب أي امتدادات PHP ويدعم التشغيل على PHP 7.1 حتى PHP 8+ الحالي. يوصى بشدة باستخدام أحدث إصدار PHP مدعوم لهذا المشروع.
نحن ملتزمون بتوفير خيارات الدعم طويل الأمد (LTS) وتوفير مسار ترقية سلس. إذا كنت تستخدم إصدار PHP أقدم، فيمكنك استخدام الفرع 2.x
(PHP 5.4+) أو الفرع 1.x
(PHP 5.3+) وكلاهما يوفر واجهة برمجة تطبيقات متوافقة ولكن لا يستفيدان من ميزات اللغة الأحدث. يمكنك استهداف إصدارات متعددة في نفس الوقت لدعم نطاق أوسع من إصدارات PHP مثل هذا:
composer require " react/promise:^3 || ^2 || ^1 "
لتشغيل مجموعة الاختبار، تحتاج أولاً إلى استنساخ هذا الريبو ثم تثبيت جميع التبعيات من خلال Composer:
composer install
لتشغيل مجموعة الاختبار، انتقل إلى جذر المشروع وقم بتشغيل:
vendor/bin/phpunit
علاوة على ذلك، نستخدم PHPStan على المستوى الأقصى لضمان سلامة الكتابة عبر المشروع:
vendor/bin/phpstan
الوعد هو منفذ لـ When.js من تأليف Brian Cavalier.
أيضًا، تم نقل أجزاء كبيرة من الوثائق من موقع When.js Wiki ومستندات API.
تم إصداره بموجب ترخيص MIT.