Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

المولدات والمكررات

جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 62 من 91.

توفر Generators طريقة بسيطة لإنشاء مكررات دون تنفيذ واجهة Iterator بالكامل. تستخدم دالة المولد yield بدلًا من return لإنتاج القيم واحدة تلو الأخرى، مع إيقاف التنفيذ مؤقتًا بين كل قيمة وأخرى.

إليك مولّدًا أساسيًا ينتج سلسلة من الأرقام:

<?php
function countTo(int $max): Generator {
    for ($i = 1; $i <= $max; $i++) {
        yield $i;
    }
}

foreach (countTo(3) as $number) {
    echo "$number\n";
}

المخرجات:

1
2
3

الميزة الرئيسية هي كفاءة الذاكرة. بدلاً من إنشاء array كاملة في الذاكرة، تنتج generator القيم عند الطلب. وهذا أمر بالغ الأهمية عند العمل مع مجموعات بيانات كبيرة - إذ يستخدم generator الذي يعالج مليون سجل الذاكرة نفسها التي يستخدمها generator يعالج عشرة سجلات.

يمكن لـ Generators أيضًا إنتاج أزواج من المفتاح والقيمة:

<?php
function userGenerator(): Generator {
    yield 'admin' => 'Alice';
    yield 'editor' => 'Bob';
}

foreach (userGenerator() as $role => $name) {
    echo "$role: $name\n";
}

المخرجات:

admin: Alice
editor: Bob

لجعل فئاتك الخاصة قابلة للتكرار باستخدام foreach، طبّق الواجهة Iterator. ومع ذلك، في معظم الحالات، توفّر المولّدات حلاً أوضح مع قدر أقل من الشيفرة التمهيدية. وهي مفيدة بشكل خاص في OOP عندما تحتاج فئة إلى إتاحة collection من العناصر دون تحميلها كلها في الذاكرة دفعةً واحدة.

challenge icon

التحدي

سهل

لنُنشئ نظام جرد للمنتجات يستخدم generators للتكرار بكفاءة عبر المجموعات الكبيرة دون تحميل كل شيء إلى الذاكرة دفعةً واحدة. ستنشئ class تخزّن المنتجات وتوفّر أساليب generator للتكرار خلالها بطرق مختلفة.

ستنظّم الشيفرة عبر ثلاثة ملفات:

  • Product.php: أنشئ class باسم Product يمثّل عنصراً في الجرد. استخدم ترقية constructor لتعريف $sku عاماً (من النوع string)، و$name عاماً (من النوع string)، و$price عاماً (من النوع float).
  • Inventory.php: أنشئ class باسم Inventory يدير collection من المنتجات ويوفّر أساليب generator للتكرار خلالها. ضمّن ملف Product. يجب أن تقوم class بما يلي:
    • امتلاك خاصية array خاصة لتخزين المنتجات
    • امتلاك method باسم addProduct(Product $product) تضيف منتجاً إلى collection
    • امتلاك method من نوع generator باسم allProducts(): Generator تُرجع كل منتج واحداً تلو الآخر
    • امتلاك method من نوع generator باسم expensiveProducts(float $minPrice): Generator تُرجع فقط المنتجات التي يكون سعرها أكبر من الحد الأدنى المعطى أو مساوياً له، مع إرجاع أزواج key-value حيث يكون المفتاح هو SKU والقيمة هي المنتج
  • main.php: ضمّن ملف Inventory. ستتلقى input واحداً: حدّاً أدنى لسعر المنتجات.

    أنشئ Inventory وأضف هذه المنتجات الثلاثة:

    • SKU: "A001"، الاسم: "Laptop"، السعر: 999.99
    • SKU: "A002"، الاسم: "Mouse"، السعر: 29.99
    • SKU: "A003"، الاسم: "Monitor"، السعر: 349.99

    أولاً، نفّذ Iterate عبر allProducts() واطبع اسم كل منتج في سطر مستقل.

    ثم اطبع سطراً فارغاً، وبعد ذلك نفّذ Iterate عبر expensiveProducts() باستخدام الحدّ المدخل. لكل منتج باهظ الثمن، اطبع "[sku]: [name] - $[price]" في سطر مستقل. نسّق السعر باستخدام منزلتين عشريتين exactly.

يوضح هذا التحدي كيف تتيح لك generators إجراء Iterate عبر المجموعات بشكل كسول: إذ لا يتم إرجاع كل منتج إلا عند طلبه، مما يجعل هذا الأسلوب فعّالاً من حيث الذاكرة بالنسبة إلى عمليات الجرد الكبيرة. يوضح إرجاع key-value في expensiveProducts() كيف يمكن لـ generators توفير بيانات Iterate منظّمة.

جرّب بنفسك

<?php

require_once 'Inventory.php';

// اقرأ المدخلات
$minPrice = floatval(trim(fgets(STDIN)));

// TODO: أنشئ مثيلاً من Inventory

// TODO: أضف ثلاثة منتجات:
// SKU: "A001", Name: "Laptop", Price: 999.99
// SKU: "A002", Name: "Mouse", Price: 29.99
// SKU: "A003", Name: "Monitor", Price: 349.99

// TODO: كرّر عبر allProducts() واطبع اسم كل منتج

// TODO: اطبع سطراً فارغاً

// TODO: كرّر عبر expensiveProducts() باستخدام $minPrice
// لكل منتج باهظ الثمن، اطبع "[sku]: [name] - $[price]"
// نسّق السعر بمنزلتين عشريتين بالضبط

?>
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم PHP عبر الإنترنت