كيف تعمل Zend Engine
تتبّع PHP من المصدر إلى opcodes ثم التنفيذ
كيف تعمل Zend Engine درس مجاني في PHP Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PHP Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PHP Academy 4 دروس في المجموع.
داخل المحرّك
لا تُفسَّر PHP سطرًا بسطر انطلاقًا من المصدر. إذ يترجم Zend Engine البرنامج النصي إلى تمثيل وسيط يُسمّى opcodes، ثم تنفّذها آلة افتراضية. يوضّح فهم هذا المسار الأداء وOPcache وJIT.
يستعرض هذا الدرس المسار: المصدر → الرموز → AST → opcodes → تنفيذ الآلة الافتراضية.
مسار المعالجة
يمر كل طلب إلى ملف PHP بأربع مراحل:
- التحليل المعجمي — النص المصدر → الرموز (ماسح مبني على re2c)
- التحليل النحوي — الرموز → شجرة بناء الجملة المجرّدة (قواعد Bison النحوية)
- الترجمة — AST → مصفوفة opcodes (op_array)
- التنفيذ — تتنقّل Zend VM بين تعليمات opcodes
من دون OPcache، تتكرر المراحل الثلاث الأولى في كل طلب.
تحويل النص إلى رموز
يحوّل المحلّل المعجمي الأحرف إلى رموز مثل T_VARIABLE وT_ECHO وT_STRING. وتتيح PHP الوصول إلى هذه المرحلة عبر token_get_all() / PhpToken، وهي المرحلة نفسها التي تستخدمها أدوات مثل PHP-CS-Fixer.
<?php
$src = '<?php $x = 1 + 2; echo $x;';
foreach (PhpToken::tokenize($src) as $tok) {
if ($tok->isIgnorable()) continue; // skip whitespace
printf("%-12s %s\n", $tok->getTokenName(), trim($tok->text));
}
?>شجرة AST
تُحلَّل الرموز إلى شجرة من العقد — عقدة إسناد يكون ابناها متغيرًا وتعبيرًا لعملية ثنائية. تبني PHP شجرة AST هذه داخليًا، بينما تعيد nikic/php-parser بناء شجرة مكافئة في مساحة المستخدم لأدوات التحليل الساكن مثل PHPStan.
<?php
// Conceptual AST for: $x = 1 + 2;
//
// AST_ASSIGN
// ├── AST_VAR ($x)
// └── AST_BINARY_OP (+)
// ├── 1
// └── 2
//
// At compile time PHP folds 1 + 2 into a literal 3
// (constant folding) before generating opcodes.
echo "AST drives opcode generation\n";
?>تعليمات opcodes
يُصدر المصرّف op_array: قائمة مسطّحة من تعليمات opcodes. لكل opcode رقم opcode (مثل ZEND_ADD وZEND_ECHO وZEND_ASSIGN)، وما يصل إلى معاملين بالإضافة إلى نتيجة، ويكون كل منها متغيرًا مترجمًا (CV) أو قيمة مؤقتة (TMP) أو ثابتًا.
<?php
// Opcodes for: $x = 1 + 2; echo $x;
//
// line op operands result
// --- ------------ ----------------- -------
// 1 ADD 1, 2 ~0
// 1 ASSIGN $x, ~0
// 1 ECHO $x
// 1 RETURN 1
//
// ~0 is a temporary; $x is a compiled variable (CV).
echo "op_array is what OPcache stores\n";
?>فحص تعليمات opcodes
يمكنكم تفريغ تعليمات opcodes التي جرى توليدها باستخدام الامتداد VLD أو باستخدام opcache.opt_debug_level في OPcache. يكشف ذلك طيّ الثوابت، وإزالة التعليمات البرمجية الميتة، وكيف يتحول تدفق التحكم إلى تعليمات opcodes مثل JMP/JMPZ.
# Dump opcodes with VLD
php -d vld.active=1 -d vld.execute=0 script.php
# Or via OPcache optimizer debug (pre/post optimization)
php -d opcache.opt_debug_level=0x10000 script.php # before opt
php -d opcache.opt_debug_level=0x20000 script.php # after optالمتغيرات المترجمة (CVs)
لا تُنفَّذ عمليات البحث عن المتغيرات المحلية في جداول التجزئة ضمن الشيفرة المترجمة، بل يعيّن المصرّف لكل متغير خانة CV مرقّمة. ويكون الوصول إلى $x في المرة الثانية فهرسةً لمصفوفة، لا بحثًا في جدول الرموز. وهذا سبب رئيسي لسرعة المتغيرات المحلية.
<?php
// Each named local gets a fixed CV slot at compile time:
// $a -> CV0 $b -> CV1 $sum -> CV2
function add(int $a, int $b): int {
$sum = $a + $b; // ADD CV0, CV1 -> CV2
return $sum; // RETURN CV2
}
echo add(2, 3) . PHP_EOL;
?>حلقة تنفيذ الآلة الافتراضية
يتنقّل المنفّذ (execute_ex) بين عناصر op_array. ويرتبط كل opcode بدالة معالجة؛ ويمكن لـPHP بناء آلية التوزيع هذه باستخدام switch ضخم، أو قفزات محسوبة، أو نهج هجين (وهو الإعداد الافتراضي والأسرع في المصرّفات الداعمة). يتقدّم مؤشر opline، بينما تحرّكه تعليمات JMP لتنفيذ تدفق التحكم.
<?php
// if ($n > 0) echo 'pos'; compiles roughly to:
//
// IS_SMALLER 0, $n -> ~T
// JMPZ ~T, ->L1 ; if false, skip
// ECHO 'pos'
// L1:
// RETURN 1
//
// The VM follows opline; JMPZ rewrites it conditionally.
$n = 5;
if ($n > 0) echo "pos\n";
?>موضع OPcache في المسار
يخزّن OPcache op_array المترجمة في الذاكرة المشتركة، متجاوزًا التحليل المعجمي والتحليل النحوي والترجمة في الطلبات اللاحقة. كما ينفّذ مرحلة تحسين (طيّ الثوابت، وإزالة التعليمات البرمجية الميتة، ودمج تعليمات opcodes). ومع ذلك، تنفّذ الآلة الافتراضية تعليمات opcodes المخزّنة مؤقتًا في كل طلب — وهنا يأتي دور JIT لاحقًا.
تكلفة استدعاء الدوال
تدفع الاستدعاءات إطارًا جديدًا إلى مكدس الآلة الافتراضية: تعليمات opcodes مثل INIT_FCALL، ثم SEND_VAL/SEND_VAR لكل وسيط، ثم DO_FCALL. يفسّر فهم ذلك سبب وجود تكلفة قابلة للقياس للاستدعاءات المفرطة للدوال الصغيرة، وأهمية التضمين وJIT في الحلقات الساخنة.
<?php
// square($x) compiles to a call sequence:
// INIT_FCALL 'square'
// SEND_VAR $x
// DO_FCALL -> ~R
// ASSIGN $y, ~R
function square(int $x): int { return $x * $x; }
$total = 0;
for ($i = 1; $i <= 5; $i++) {
$total += square($i); // one call sequence per iteration
}
echo $total . PHP_EOL; // 1+4+9+16+25 = 55
?>إيقاف الطلب
بعد التنفيذ، تفكك PHP الطلب: تُحرَّر المتغيرات قيد التشغيل، ويُفرَّغ الإخراج، ثم يعيد المحرّك ضبط ساحة المخصّص الخاصة به. وفي نموذج FPM ذي البنية التي لا تشارك الذاكرة، يبدأ كل طلب في حالة نظيفة؛ لذلك لا يمكن لخطأ فادح في طلب أن يفسد طلبًا آخر. تبقى op_arrays المشتركة في OPcache بين الطلبات، أما حالة المنفّذ فلا تبقى.
تحقّق سريع
ما الذي يخزّنه OPcache تحديدًا لتجاوز إعادة الترجمة؟
مراجعة
مسار Zend هو: تحليل معجمي ← تحليل نحوي ← ترجمة ← تنفيذ. يتحول المصدر إلى رموز، ثم إلى AST، ثم إلى op_array من تعليمات opcodes تنفّذها حلقة المنفّذ في الآلة الافتراضية، مع تخزين المتغيرات المحلية في خانات CV مرقّمة ودفع الاستدعاءات لإطارات إلى المكدس. يخزّن OPcache op_array (بالإضافة إلى مرحلة تحسين)، ولذلك لا يتكرر في كل طلب سوى التنفيذ.
تعلم PHP مع معلم ذكاء اصطناعي — مجانًا
اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.
- الدورات
- 49
- الدروس
- 195
الأسئلة الشائعة
هل درس «كيف تعمل Zend Engine» مجاني؟
نعم — نص درس «كيف تعمل Zend Engine» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PHP Academy، انتقل إلى CoddyKit PRO. تتضمن دورة PHP Academy 4 دروس في المجموع.
ماذا ستتعلم في «كيف تعمل Zend Engine»؟
تتبّع PHP من المصدر إلى opcodes ثم التنفيذ تتمرن على PHP Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ PHP Academy؟
لا تُشترط خبرة سابقة. PHP Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «كيف تعمل Zend Engine»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس PHP Academy هذا؟
نعم. كل درس في PHP Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- كيف تعمل Zend Engine
- إدارة الذاكرة وجمع البيانات المهملة
- ترجمة OPcache وJIT
- كتابة إضافة PHP أساسية بلغة C