موقع PHP الخاص بك بطيء؟ حان الوقت للنظر تحت الغطاء
\n\nهل سبق لك أن انتظرت تحميل صفحة لفترة أطول من شرب قهوة الصباح؟ ألم مألوف. PHP لغة قوية، لكن بدون إعدادات صحيحة يمكن أن تتحول إلى سلحفاة. في هذه المقالة، لن نلقي محاضرات مملة. سنأخذ كودًا حقيقيًا، ونحلله، ونجد نقاط الاختناق، ونسرعه حتى يشكرك الخادم.
\n\nسنتحدث عن أداء PHP — من القياسات البسيطة إلى التحليل العميق. ستتعلم كيفية استخدام Xdebug و Blackfire للعثور على الأجزاء الإشكالية، وأي طرق التحسين تعمل حقًا. هيا بنا!
\n\nالخطوة 1. قياس كل شيء: من الميكروثانية إلى الميغابايت
\n\nقبل تحسين أي شيء، تحتاج إلى فهم أين يحدث البطء. الحدس غالبًا ما يخدع. دعنا نستخدم الأدوات.
\n\nأبسط طريقة: microtime(true)
\n\nللتحقق السريع من جزء من الكود، يمكن استخدام microtime() القديم الجيد. ليس أنيقًا، لكنه يعمل.
<?php\n$start = microtime(true);\n\n// الكود البطيء الخاص بك\n$data = [];\nfor ($i = 0; $i < 100000; $i++) {\n $data[] = md5($i . 'salt');\n}\n\n$end = microtime(true);\necho 'وقت التنفيذ: ' . round(($end - $start) * 1000, 2) . ' مللي ثانية';\n?>\n\nهذه الطريقة جيدة للتقييم التقريبي. لكن عندما يكبر المشروع، تحتاج إلى أدوات جادة.
\n\nXdebug — محققك الشخصي
\n\nXdebug هو المعيار الفعلي لـ تحليل PHP. يقوم بإنشاء ملفات cachegrind التي يمكن فتحها في KCachegrind (لينكس) أو QCachegrind (ماك/ويندوز).
\n\nالتثبيت عبر pecl:
\npecl install xdebug\n\nلنقم بتفعيل التحليل في php.ini:
\nxdebug.mode = profile\nxdebug.output_dir = /tmp/profiling\n\nبعد تنفيذ السكريبت، سيظهر ملف في المجلد. افتحه في QCachegrind — سترى أي دالة استهلكت أكبر وقت. غالبًا ما تكون المفاجأة: ليست قاعدة البيانات، بل معالجة بسيطة للنصوص في حلقة.
\n\nBlackfire — لمن يحبون لوحات المعلومات الجميلة
\n\nBlackfire من SensioLabs (مبتكري Symfony) هو محلل سحابي. لا يظهر الوقت فقط، بل أيضًا عدد الاستدعاءات، الذاكرة، الإدخال/الإخراج. يتكامل مع PHPUnit، Symfony، Laravel.
\n\nمثال للاستخدام من سطر الأوامر:
\nblackfire run php myscript.php\n\nستحصل على رابط لتقرير ويب مع تصور. مناسب للبحث عن نقاط اختناق أداء PHP.
\n\nالخطوة 2. المشاكل النموذجية وحلولها
\n\nالآن بعد أن تعلمنا التحليل، دعنا نناقش المشاكل الشائعة.
\n\nالمشكلة 1: استعلامات N+1 في الحلقة
\n\nكلاسيكي. تحصل على قائمة المستخدمين، ثم في حلقة تقوم باستعلام لقاعدة البيانات لكل مستخدم.
\n\nالكود البطيء:
\n<?php\n$users = $db->query('SELECT id FROM users LIMIT 100');\nforeach ($users as $user) {\n $orders = $db->query("SELECT * FROM orders WHERE user_id = {$user['id']}");\n // ...\n}\n?>\n\nالتحسين:
\n<?php\n$users = $db->query('SELECT id FROM users LIMIT 100');\n$ids = array_column($users, 'id');\n$orders = $db->query('SELECT * FROM orders WHERE user_id IN (' . implode(',', $ids) . ')');\n// تجميع حسب user_id\n$grouped = [];\nforeach ($orders as $order) {\n $grouped[$order['user_id']][] = $order;\n}\n?>\n\nالنتيجة: 100 استعلام تحولت إلى 2. أداء تطبيق PHP يزيد أضعافًا.
\n\nالمشكلة 2: الحلقات غير الفعالة والتعامل مع النصوص
\n\nدمج النصوص في حلقة هو شر. استخدم المصفوفات و implode().
<?php\n// سيء\n$result = '';\nfor ($i = 0; $i < 10000; $i++) {\n $result .= 'item' . $i . ',';\n}\n\n// جيد\n$parts = [];\nfor ($i =