يا هلا بيك يا بشمهندس! تعال نتكلم شوية عن كابوس كل مبرمج ويب بيشتغل بلغة بي إتش بي (PHP). أكيد في يوم من الأيام كنت شغال على مشروع، وطلبت من قاعدة البيانات (Database) تجيبلك ملايين السجلات (Records)، وفجأة لقيت الشاشة بقت بيضا، وطلعلك الرسالة الشهيرة اللي بتسد النفس:
Fatal error: Allowed memory size exhausted
الوجع ده كلنا عشناه! بتروح تزود الـ Memory Limit في ملف الـ php.ini وبعدين ترجع تتقرص تاني لما حجم البيانات يزيد. الطريقة التقليدية اللي بنعمل بيها لوب (Loop) ونحط البيانات كلها جوة مصفوفة (Array) واحدة في الرامات (RAM) دي بقت موضة قديمة وبتدمر السيرفر (Server). الحل السحري هنا اللي هيوفر عليك وقت وفلوس وسيرفرات هو استخدام مولدات البيانات أو ما يسمى بـ PHP Generators (Yield).
Table of contents [Show]
إيه هي أصلاً الـ PHP Generators وليه بنستخدمها؟
عشان نفهم الموضوع ببساطة، تخيل إنك رايح مطعم بوفيه مفتوح. الطريقة التقليدية (Arrays) عاملة زي إنك بتدخل المطعم، وتقولهم هاتوا كل الأكل اللي في المطعم حطوه في طبقي مرة واحدة! ده طبعاً هيوقع الطبق وهيغرق الدنيا (استهلاك عالي للـ RAM). أما الـ PHP Generators فهي عاملة زي الجرسون اللي بيجيبلك طبق طبق على قد ما تاكل بالظبط، تخلص الطبق، يجيبلك اللي بعده.
بالبلدي كده، الـ Generator عبارة عن دالة (Function) عادية خالص، بس بدل ما ترجع البيانات كلها دفعة واحدة باستخدام كلمة return، هي بتستخدم كلمة yield. الكلمة دي بتخلي الدالة ترجع قيمة واحدة وتوقف تنفيذها مؤقتاً لحد ما تطلب القيمة اللي بعدها. بالطريقة دي، استهلاك الـ RAM بيكون شبه معدوم (تقريباً صفر) مهما كان عدد السجلات ملايين!
المشكلة في الكود التقليدي (Arrays)
تعال نبص بصرة على الكود اللي بيجيب السكر في الدم. لو عندك جدول فيه مليون يوزر وعملت كده:
function getUsers($pdo) {
$stmt = $pdo->query("SELECT * FROM users");
return $stmt->fetchAll(PDO::FETCH_ASSOC); // هنا المصيبة حصلت!
}
foreach (getUsers($pdo) as $user) {
echo $user['name'] . "<br>";
}
في الكود ده، دالة fetchAll بتجيب المليون سجل وتحطهم في مصفوفة في الرامات دفعة واحدة. لو السجل الواحد حجمه كيلو بايت واحد، انت بتتكلم في ميجابايتس كتير جداً ممكن توقع السيرفر لو كذا حد فتح الموقع في نفس الوقت.
الحل الذكي باستخدام PHP Generators و Yield
تعال نعدل الكود ونستخدم الـ Generators عشان نشوف الفرق الرهيب في الأداء واستهلاك الـ Memory Management:
function get 지र्सGenerators($pdo) {
$stmt = $pdo->query("SELECT * FROM users");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
yield $row; // هنا السحر بيحصل، بنسلم سجل سجل
}
}
foreach (get 지र्सGenerators($pdo) as $user) {
echo $user['name'] . "<br>";
}
الفرق هنا جوهري وعظيم! الدالة مش بتخزن حاجة في الرمات، هي بتجيب سجل من الداتابيز، ترميه للـ foreach، وتنساه، وتدخل على السجل اللي بعده. الـ Memory Footprint بيفضل ثاب وثابت جداً مهما حجم الـ Dataset كان ضخم.
فهم الـ Iterators و Behind the Scenes
تقدر تعتبر الـ Generator إنه بيطبق واجهة الـ Iterator في الـ Object-Oriented Programming (OOP) بس من غير ما تكتب كلاسات طويلة ومعقدة. لغة الـ PHP نفسها بتعمل إدارة للـ State بتاعة الدالة، فبتعرف هي وقفت فين ورايحة فين من غير ما تستهلك موارد تذكر.
الميزة العظيمة هنا مش بس في قراءة الداتابيز، دي بتنفع جداً لو بتعمل File Processing لملفات لوج (Log Files) حجمها بيعدي الـ Gigabytes، أو لو بتعمل Data Export / Import لملفات CSV ضخمة جداً.
نصيحة من أخوك لتطوير مهاراتك البرمجية
يا بشمهندس، المبرمج الشاطر مش هو اللي بيعرف يكتب كود بيشتغل وبس، المبرمج المحترف هو اللي بيعرف يكتب كود (Scalable) يقدر يستحمل الضغط وما يوقعش السيرفر في نص الليل وتجيلك مكالمة طوارئ. ميزات زي الـ Generators في الـ PHP أحياناً كتير ناس بتجهلها وبتعتبرها حاجات متقدمة، وهي في الواقع أدوات أساسية لحل مشاكل يومية بتقابلك في الشغل الحقيقي.
درب نفسك تستخدم yield في أي وقت تكون بتتعامل فيه مع كميات بيانات مفتوحة أو غير معروف حجمها مسبقاً. اتعلم دايماً تفكر في الموارد (Resources) وإزاي تحافظ عليها، وصدقني ده اللي هيفرق مستواك ويخليك تنط لفل تاني خالص في سوق العمل.