Fibers في PHP 8.1
أوقف التنفيذ واستأنفه باستخدام Fibers الأصلية.
Fibers في PHP 8.1 درس مجاني في PHP Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PHP Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PHP Academy 4 دروس في المجموع.
ما الذي توفره Fibers
أضافت PHP 8.1 ميزة Fibers: وهي بدائية أساسية تتيح لكم إيقاف مكدس استدعاءات قيد التشغيل مؤقتًا ثم استئنافه لاحقًا من مكان مختلف تمامًا. إن Fiber مكدس كامل ومستقل يمكن تعليقه عند نقطة عشوائية ومتابعته من دون تفكيكه. وهذه هي القطعة المفقودة التي تتيح للمكتبات استبدال سلاسل الوعود بـ await ذي المظهر المتزامن — وهو الأساس وراء واجهات ReactPHP وAmp الحديثة.
أول Fiber لكم
أنشئوا Fiber باستخدام دالة استدعاء، ثم شغّلوه باستخدام start() ودعوه يعلّق نفسه باستخدام suspend(). تعود السيطرة إلى المستدعي عند نقطة التعليق؛ ويواصل resume() التنفيذ من تلك النقطة بالضبط.
<?php
$fiber = new Fiber(function (): void {
echo "A: inside fiber\n";
$received = Fiber::suspend('paused');
echo "C: resumed with '$received'\n";
});
$value = $fiber->start(); // prints A, returns 'paused'
echo "B: fiber suspended, got '$value'\n";
$fiber->resume('hello'); // prints C
echo "D: fiber done\n";الأساليب الأساسية الأربعة
واجهة برمجة التطبيقات صغيرة:
$fiber->start(...$args)— يبدأ التنفيذ؛ ويعيد أول قيمة تعليق.Fiber::suspend($value)— يوقف Fiber الحالي مؤقتًا، ويسلّم$valueإلى من يستأنفه.$fiber->resume($value)— يواصل التنفيذ؛ وتصبح$valueقيمة الإرجاع منsuspend().$fiber->getReturn()— يعيد قيمة إرجاع دالة الاستدعاء بعد انتهاء Fiber.
تنتقل القيم في الاتجاهين عبر حدود التعليق.
تمرير القيم في الاتجاهين
يمكن لـ Fiber إخراج طلب واستقبال استجابة في المقابل — وهذا هو جوهر الطريقة التي تُدخل بها حلقة الأحداث نتائج عمليات الإدخال والإخراج إلى المهام المعلّقة.
<?php
$fiber = new Fiber(function (): int {
$a = Fiber::suspend('need first number');
$b = Fiber::suspend('need second number');
return $a + $b;
});
$fiber->start(); // suspends: 'need first number'
$fiber->resume(10); // a = 10, suspends: 'need second number'
$fiber->resume(32); // b = 32, fiber returns
echo $fiber->getReturn(), PHP_EOL; // 42حالة Fiber والاستبطان
يمكنكم الاستعلام عن دورة حياة Fiber. يؤدي استدعاء resume() على Fiber منتهٍ إلى إطلاق FiberError، لذا استخدموا هذه الفحوصات كحماية عند بناء مجدول.
<?php
$fiber = new Fiber(function () { Fiber::suspend(); });
var_dump($fiber->isStarted()); // false
$fiber->start();
var_dump($fiber->isStarted()); // true
var_dump($fiber->isSuspended()); // true
var_dump($fiber->isRunning()); // false (we're outside it)
$fiber->resume();
var_dump($fiber->isTerminated()); // trueبناء مجدول صغير
تساوي Fibers مضافًا إليها قائمة انتظار مجدولًا تعاونيًا. تعلّق كل مهمة تنفيذها لتسلّم التحكم؛ ويواصل المجدول التناوب بين المهام حتى تنتهي جميعها. هذا هو الهيكل الأساسي الموجود داخل كل بيئة تشغيل غير متزامنة.
<?php
$tasks = [];
foreach (['X', 'Y', 'Z'] as $name) {
$tasks[] = new Fiber(function () use ($name) {
for ($i = 1; $i <= 2; $i++) {
echo "$name step $i\n";
Fiber::suspend(); // give others a turn
}
});
}
foreach ($tasks as $t) { $t->start(); }
while ($tasks) {
foreach ($tasks as $k => $t) {
if ($t->isTerminated()) { unset($tasks[$k]); continue; }
$t->resume();
}
}الاستثناءات عبر الحدود
تُدخل throwInto() (وتُسمى throw() في واجهة برمجة التطبيقات عند استخدامها بصيغة $fiber->throw()) استثناءً عند نقطة التعليق — وهي الطريقة التي تُبلغ بها الحلقة المهمة المنتظرة بفشل عملية إدخال وإخراج. يظهر الاستثناء تحديدًا في الموضع الذي أُوقف فيه Fiber مؤقتًا.
<?php
$fiber = new Fiber(function () {
try {
Fiber::suspend();
} catch (RuntimeException $e) {
echo 'caught: ' . $e->getMessage() . PHP_EOL;
}
});
$fiber->start();
$fiber->throw(new RuntimeException('io failed')); // prints: caught: io failedFibers مقابل المولدات
تعلّق المولدات (yield) التنفيذ أيضًا — لكن للدالة العلوية فقط. ولانتظار نتيجة من مساعد متداخل باستخدام المولدات، يجب تمرير yield from صعودًا عبر المكدس بأكمله، مما يفرض تغييرًا على كل دالة. أما Fibers فتعلّق المكدس بأكمله من أي عمق، لذلك يمكن لاستدعاء متداخل بعمق استخدام Fiber::suspend() من دون علم الدوال المستدعية. ولهذا يمكن أن يكون await استدعاء دالة عاديًا بدلًا من yield.
<?php
function deep(): string {
// No yield needed in the call chain — suspends the whole fiber
return Fiber::suspend('awaiting from deep');
}
$f = new Fiber(fn() => print(deep() . PHP_EOL));
$f->start();
$f->resume('value injected at depth');قيد التعليق
هناك قاعدة صارمة واحدة: لا يمكنكم استخدام Fiber::suspend() إلا من داخل Fiber. يؤدي التعليق من الخيط الرئيسي، أو عبر دالة استدعاء على مستوى C لا تدعم Fibers (مثل بعض دوال المقارنة القديمة في usort وبعض دوال الاستدعاء الداخلية)، إلى إطلاق FiberError: Cannot suspend outside of a fiber. تخفي المكتبات هذه التفاصيل بضمان تشغيل كل شيفرة المستخدم داخل Fiber تقوده الحلقة.
<?php
try {
Fiber::suspend(); // main thread — illegal
} catch (FiberError $e) {
echo $e->getMessage(), PHP_EOL; // Cannot suspend outside of a fiber
}كيف تستخدم المكتبات Fibers
نادرًا ما تكتبون Fibers خامًا في شيفرة التطبيق. إذ تغلفها أطر مثل Amp v3 وReactPHP (via fibers): يستدعي await($promise) داخليًا Fiber::suspend()، ويسجّل متابعة مع حلقة الأحداث، ثم يستأنف Fiber باستخدام resume() عند اكتمال عملية الإدخال والإخراج. تبدو شيفرتكم متزامنة؛ بينما تتولى بيئة التشغيل إيقافها وجدولتها.
<?php
// Conceptual: what an await() helper does under the hood
function await(Promise $p): mixed {
$fiber = Fiber::getCurrent() ?? throw new Error('await() outside fiber');
$p->then(fn($v) => $fiber->resume($v),
fn($e) => $fiber->throw($e));
return Fiber::suspend(); // parked until the promise settles
}اعتبارات الذاكرة ودورة الحياة
يخصص كل Fiber مكدسه الخاص، لذا فإن إنشاء مئات الآلاف منها له تكلفة على الذاكرة (مع أنها أخف بكثير من خيوط نظام التشغيل). لا يُجمع Fiber المعلّق إلى الأبد بواسطة جامع القمامة إذا ظل شيء ما يشير إليه — وهذا تسرب دقيق في الخوادم طويلة التشغيل. كذلك، فإن دالة الاستدعاء الخاصة بـ Fiber إذا أطلقت استثناءً غير معالج فسوف ينتقل الاستثناء إلى خارج استدعاء resume()/start()، لذلك يجب على المجدول إحاطة عمليات الاستئناف بـ try/catch.
<?php
$fiber = new Fiber(function () {
throw new RuntimeException('boom inside fiber');
});
try {
$fiber->start(); // exception surfaces here, at the resumer
} catch (RuntimeException $e) {
echo 'scheduler caught: ' . $e->getMessage() . PHP_EOL;
}تحقق سريع
ما الميزة الأساسية التي تتمتع بها Fibers مقارنةً بالمولدات في البرمجة غير المتزامنة؟
مراجعة
تُعد Fibers بدائية PHP منخفضة المستوى للتزامن:
- توقف
start()وFiber::suspend()وresume()وgetReturn()مكدس الاستدعاءات الكامل وتواصله، مع تمرير القيم في الاتجاهين. - تتيح أساليب الحالة (
isSuspendedوisTerminated) وthrow()بناء مجدول تعاوني وإدخال أخطاء عمليات الإدخال والإخراج. - على خلاف المولدات، يعلّق Fiber التنفيذ من أي عمق — مما يتيح استخدام
await()بشفافية. - لا يمكن التعليق إلا من داخل Fiber؛ وتغلف أطر مثل Amp وReactPHP Fibers لكي تُقرأ شيفرتكم غير المتزامنة كما لو كانت متزامنة.
الأسئلة الشائعة
هل درس «Fibers في PHP 8.1» مجاني؟
نعم — نص درس «Fibers في PHP 8.1» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PHP Academy، انتقل إلى CoddyKit PRO. تتضمن دورة PHP Academy 4 دروس في المجموع.
ماذا ستتعلم في «Fibers في PHP 8.1»؟
أوقف التنفيذ واستأنفه باستخدام Fibers الأصلية. تتمرن على PHP Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ PHP Academy؟
لا تُشترط خبرة سابقة. PHP Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «Fibers في PHP 8.1»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس PHP Academy هذا؟
نعم. كل درس في PHP Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- نموذج التزامن في PHP
- Fibers في PHP 8.1
- حلقات الأحداث باستخدام ReactPHP
- خوادم عالية الأداء باستخدام Swoole