PHP / متقدم: النماذج الموثوقة وأدلة التشغيل / الدرس 14
المولدات والتدفقات وعمر الموارد
استهلك البيانات المحدودة تدريجياً واربط اكتساب المورد بتحريره. ميّز التسلسل الكسول من المجموعة القابلة للإعادة وافحص التدفقات بذاكرة مملوكة فقط.
في هذه الصفحة
أهداف الدرس
- تتبع تعليق المولد واستهلاكه.
- معالجة فشل التدفق وإغلاق المورد.
- شرح حدود ادعاءات ذاكرة الكسل.
ينتج المولد القيم عند الطلب
تعيد دالة yield مولداً لا مصفوفة جاهزة، ويتقدم التنفيذ مع الطلب ويحفظ حالته. قد يتجنب قائمة كاملة ولا يضمن قلة الذاكرة إن حمل المصدر كله أو جمعه المستهلك. المولد ذو حالة ومرور واحد عادة، فتحتاج الإعادة دعوة جديدة. حدّد المدخل ومالك الاستهلاك قبل ادعاء تحسين التدفق؛ فالكسل يغير التوقيت والملكية أيضاً.
استهلاك تسلسل محدود عمداً
يولد المثال ثلاث صفحات ويجمعها دون قائمة وسيطة، مستقلاً عن قاعدة وملف. تجمع iterator_to_array التسلسل عمداً فتغير عقد الذاكرة وقد تناسب الصغير. لا تتوقع بدء المولد المستهلك؛ تنشئ pages حالة جديدة. اختبر الفارغ والواحد والأقصى وحدد هل يرفض الحد الخاطئ أو يعني تسلسلاً فارغاً.
<?php
declare(strict_types=1);
function pages(int $limit): Generator {
for ($page = 1; $page <= $limit; $page++) { yield $page; }
}
$total = 0;
foreach (pages(3) as $page) { $total += $page; }
echo $total, PHP_EOL;
echo implode(",", iterator_to_array(pages(3))), PHP_EOL;
6
1,2,3
يحفظ الأول المجموع فقط ويبني الثاني مصفوفة من مولد جديد.
قد يفشل التدفق قبل توفر بيانات
افحص فشل fopen قبل القراءة والكتابة. لا يثبت الفتح اكتمال كل كتابة أو سجل. تبقى البنية ونهايات السطور والحد والترميز مسؤوليات. لا تحول المدخل لمسار أو مغلف غير مقيد. يستخدم المثال php://memory فلا يفتح خدمة أو ملف تطبيق، وللحقيقي سياسة مسار أو نقطة ضيقة ومعالجة ملائمة.
إغلاق ما فتحه هذا النطاق
توضح try/finally التنظيف. يكتب المثال سطرين صغيرين ويعيد المؤشر ويقرأ ويغلق حتى مع استثناء. يفحص القيم لا معرف مورد متغيراً. يعتمد تحرير مورد يملكه مولد على عمره ومراجعه؛ فلا يثبت break تدمير كل مولد ما زال مشاراً إليه. حدّد الملكية والعمر عند لزوم التحرير العاجل ولا تعتمد على نهاية الطلب بالعامل الطويل.
<?php
declare(strict_types=1);
$stream = fopen("php://memory", "w+");
if ($stream === false) { throw new RuntimeException("memory stream unavailable"); }
try {
fwrite($stream, "PHP
PDO
");
rewind($stream);
$lines = [];
while (($line = fgets($stream)) !== false) { $lines[] = rtrim($line, "
"); }
echo implode(",", $lines), PHP_EOL;
} finally { fclose($stream); }
PHP,PDO
لا مسار أو خدمة حقيقية، وتحذف rtrim بايتات نهاية السطر المحددة فقط.
يحتاج التدفق ضغطاً عكسياً وسجلات محدودة
يفيد التدريج إذا لم يطبر المستهلك بلا حد أو يحتفظ بالجميع. حدّ الدفعات والسجل والتزامن. قد يخزن مشغل القاعدة النتائج رغم foreach؛ فلا تثبت الحلقة تدفق الخادم. قس الذاكرة والإنتاجية بمعزل مع الصحة. حدّد للتصدير الجزئي الإهمال أو الوسم أو الاستئناف؛ فقد لا يمكن استبدال استجابة أرسلت جزئياً بخطأ نظيف.
الأخطاء الشائعة وإصلاحها
وصف المولد بذاكرة ثابتة تلقائياً
- لماذا يحدث؟
- إغفال التخزين الأولي واحتفاظ المستهلك.
- كيف تصلحه؟
- فحص الطرفين وقياس حالات محدودة.
- كيف تتجنب تكراره؟
- توثيق الجمع الكامل وحدود الدفعات.
استخدام فتح فاشل كمورد
- لماذا يحدث؟
- غياب فحص عقد الفشل.
- كيف تصلحه؟
- فحص المعاد قبل العمل.
- كيف تتجنب تكراره؟
- اختبار فشل الاكتساب منفصلاً من المعالجة.
اعتماد عامل طويل على نهاية الطلب للتنظيف
- لماذا يحدث؟
- افتراض عمر طلب قصير.
- كيف تصلحه؟
- تحرير الموارد عند حدود مملوكة صريحة.
- كيف تتجنب تكراره؟
- اختبار التكرار ومخارج الإنهاء المبكر.
اختبر فهمك
1. هل تحفظ iterator_to_array ميزة ذاكرة مولد ضخم؟
اعرض الحل المشروح
تجمع القيم فتزداد الذاكرة بالمخرج. استخدم التجميع التدريجي أو دفعات إن سمح العمل؛ يظل المولد مفيداً للإنتاج لكن المستهلك يقرر الاحتفاظ.
2. لماذا تحذف CR وLF فقط لا تشذب السطر كله؟
اعرض الحل المشروح
قد تكون المسافات بيانات مشروعة. تزيل rtrim المحددة الإطار دون تغييرها. عرّف سياسة السجل والمسافة بدلاً من trim واسعة بالعادة.
أهم ما تعلمته
- لا يلغي الكسل كلفة المصدر والمستهلك والمورد.
- ينتمي الاكتساب والفشل والتحرير لعمر صريح واحد.