المفاهيم المتقدمة للدوال
بعد إتقان الدوال الأساسية، حان الوقت لاستكشاف المفاهيم المتقدمة التي ستجعل كود PHP الخاص بك أكثر قوة ومرونة وأناقة. هذه التقنيات مستخدمة على نطاق واسع في أطر عمل ومكتبات PHP الحديثة.
الدوال المتغيرة
الدوال المتغيرة تقبل عدداً متغيراً من الوسائط باستخدام معامل ...:
<?php
// قبول وسائط غير محدودة:
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $num) {
$total += $num;
}
return $total;
}
echo sum(1, 2, 3); // 6
echo sum(1, 2, 3, 4, 5); // 15
echo sum(10); // 10
// الجمع بين المعاملات العادية والمتغيرة:
function greet($greeting, ...$names) {
foreach ($names as $name) {
echo "$greeting، $name!<br>";
}
}
greet("مرحباً", "أحمد", "فاطمة", "محمد");
// الناتج:
// مرحباً، أحمد!
// مرحباً، فاطمة!
// مرحباً، محمد!
?>
نصيحة: يجب أن تكون المعاملات المتغيرة آخر معامل في توقيع الدالة. يمكنك أن يكون لديك معاملات عادية قبلها، ولكن ليس بعدها.
فك حزم الوسائط
يمكن أيضاً لمعامل النشر (...) فك حزم المصفوفات إلى وسائط دالة:
<?php
function calculateTotal($price, $quantity, $taxRate) {
return $price * $quantity * (1 + $taxRate);
}
// فك حزم المصفوفة إلى وسائط:
$orderData = [100, 2, 0.1];
$total = calculateTotal(...$orderData);
echo $total; // 220
// مفيد مع array_values:
$product = [
'price' => 50,
'quantity' => 3,
'tax' => 0.15
];
$total = calculateTotal(...array_values($product));
// دمج المصفوفات:
$arr1 = [1, 2, 3];
$arr2 = [4, 5, 6];
$combined = [...$arr1, ...$arr2];
print_r($combined); // [1, 2, 3, 4, 5, 6]
?>
الوسائط المسماة (PHP 8.0+)
تسمح لك الوسائط المسماة بتمرير الوسائط حسب اسم المعامل بدلاً من الموضع:
<?php
function createUser($name, $email, $age = null, $city = null) {
return [
'name' => $name,
'email' => $email,
'age' => $age,
'city' => $city
];
}
// الوسائط الموضعية التقليدية:
$user1 = createUser("أحمد", "ahmad@example.com", 30, "الرياض");
// الوسائط المسماة (يمكن تخطي المعاملات الاختيارية):
$user2 = createUser(
name: "فاطمة",
email: "fatima@example.com",
city: "جدة" // تخطي العمر
);
// الوسائط المسماة بأي ترتيب:
$user3 = createUser(
email: "mohamed@example.com",
name: "محمد",
age: 25
);
?>
نصيحة: تجعل الوسائط المسماة استدعاءات الدوال أكثر قابلية للقراءة وتسمح لك بتخطي المعاملات الاختيارية في المنتصف دون تمرير قيم null.
الدوال التكرارية
الدالة التكرارية هي التي تستدعي نفسها:
<?php
// حساب العاملي:
function factorial($n) {
if ($n <= 1) {
return 1; // الحالة الأساسية
}
return $n * factorial($n - 1); // الحالة التكرارية
}
echo factorial(5); // 120 (5 * 4 * 3 * 2 * 1)
// تسلسل فيبوناتشي:
function fibonacci($n) {
if ($n <= 1) {
return $n;
}
return fibonacci($n - 1) + fibonacci($n - 2);
}
echo fibonacci(10); // 55
// اجتياز الدليل:
function listFiles($dir, $indent = 0) {
$files = scandir($dir);
foreach ($files as $file) {
if ($file === '.' || $file === '..') continue;
echo str_repeat(' ', $indent) . "$file<br>";
$path = $dir . '/' . $file;
if (is_dir($path)) {
listFiles($path, $indent + 2); // استدعاء تكراري
}
}
}
?>
تحذير: قم دائماً بتضمين حالة أساسية لمنع التكرار اللانهائي. لدى PHP حد تكرار سيسبب خطأ فادحاً إذا تم تجاوزه.
دوال الاستدعاء العكسي
يمكن تمرير الدوال كوسائط إلى دوال أخرى:
<?php
// دالة تقبل استدعاء عكسي:
function processArray($array, $callback) {
$result = [];
foreach ($array as $item) {
$result[] = $callback($item);
}
return $result;
}
// استخدام دالة مسماة كاستدعاء عكسي:
function double($n) {
return $n * 2;
}
$numbers = [1, 2, 3, 4, 5];
$doubled = processArray($numbers, 'double');
print_r($doubled); // [2, 4, 6, 8, 10]
// استخدام دالة مجهولة كاستدعاء عكسي:
$squared = processArray($numbers, function($n) {
return $n * $n;
});
print_r($squared); // [1, 4, 9, 16, 25]
// استخدام دالة سهم:
$tripled = processArray($numbers, fn($n) => $n * 3);
print_r($tripled); // [3, 6, 9, 12, 15]
?>
الدوال عالية الرتبة
الدوال التي تعمل على دوال أخرى (تقبل أو ترجع دوال):
<?php
// دالة ترجع دالة:
function multiplier($factor) {
return function($number) use ($factor) {
return $number * $factor;
};
}
$double = multiplier(2);
$triple = multiplier(3);
echo $double(5); // 10
echo $triple(5); // 15
// دالة تعدّل السلوك:
function createValidator($minLength) {
return function($value) use ($minLength) {
return strlen($value) >= $minLength;
};
}
$validateUsername = createValidator(3);
$validatePassword = createValidator(8);
var_dump($validateUsername("ab")); // false
var_dump($validatePassword("secret123")); // true
?>
تركيب الدوال
دمج دوال متعددة لإنشاء وظائف جديدة:
<?php
// دالة مساعدة للتركيب:
function compose(...$functions) {
return function($input) use ($functions) {
return array_reduce(
array_reverse($functions),
fn($carry, $fn) => $fn($carry),
$input
);
};
}
// دوال فردية:
function trim_text($text) {
return trim($text);
}
function uppercase($text) {
return strtoupper($text);
}
function add_exclamation($text) {
return $text . '!';
}
// قم بتركيبها:
$transform = compose(
'trim_text',
'uppercase',
'add_exclamation'
);
echo $transform(" مرحبا بالعالم "); // مرحبا بالعالم!
?>
التخزين المؤقت
تخزين نتائج الدالة مؤقتاً لتحسين الأداء:
<?php
// بدون تخزين مؤقت (بطيء للمدخلات الكبيرة):
function fibonacci_slow($n) {
if ($n <= 1) return $n;
return fibonacci_slow($n - 1) + fibonacci_slow($n - 2);
}
// مع التخزين المؤقت (أسرع بكثير):
function fibonacci_memo($n, &$cache = []) {
if (isset($cache[$n])) {
return $cache[$n]; // إرجاع النتيجة المخزنة مؤقتاً
}
if ($n <= 1) {
return $n;
}
$cache[$n] = fibonacci_memo($n - 1, $cache) +
fibonacci_memo($n - 2, $cache);
return $cache[$n];
}
// بديل: إنشاء غلاف للتخزين المؤقت:
function memoize($func) {
return function(...$args) use ($func) {
static $cache = [];
$key = serialize($args);
if (!isset($cache[$key])) {
$cache[$key] = $func(...$args);
}
return $cache[$key];
};
}
$expensiveOperation = function($n) {
sleep(1); // محاكاة عملية مكلفة
return $n * 2;
};
$memoized = memoize($expensiveOperation);
echo $memoized(5); // يستغرق ثانية واحدة
echo $memoized(5); // يُرجع على الفور من ذاكرة التخزين المؤقت
?>
دوال المولد
تسمح لك المولدات بالتكرار عبر البيانات دون تحميل كل شيء في الذاكرة:
<?php
// بدون مولد (يستخدم الكثير من الذاكرة):
function getNumbersArray($max) {
$numbers = [];
for ($i = 1; $i <= $max; $i++) {
$numbers[] = $i;
}
return $numbers;
}
// مع مولد (كفاءة الذاكرة):
function getNumbersGenerator($max) {
for ($i = 1; $i <= $max; $i++) {
yield $i; // يولد قيمة واحدة في كل مرة
}
}
// الاستخدام:
foreach (getNumbersGenerator(1000000) as $number) {
echo $number . " ";
if ($number >= 10) break;
}
// مولد مع المفاتيح:
function getUsers() {
yield 'ahmad' => ['name' => 'أحمد', 'age' => 30];
yield 'fatima' => ['name' => 'فاطمة', 'age' => 25];
yield 'mohamed' => ['name' => 'محمد', 'age' => 35];
}
foreach (getUsers() as $username => $user) {
echo "$username: {$user['name']}<br>";
}
// قراءة ملفات كبيرة مع المولدات:
function readLargeFile($filename) {
$file = fopen($filename, 'r');
while (!feof($file)) {
yield fgets($file);
}
fclose($file);
}
?>
نصيحة: استخدم المولدات عند العمل مع مجموعات بيانات كبيرة أو تسلسلات لا نهائية. توفر كفاءة أفضل للذاكرة من خلال توليد القيم عند الطلب.
الدوال من الدرجة الأولى
تعامل PHP الدوال كمواطنين من الدرجة الأولى، مما يسمح بأنماط قوية:
<?php
// تخزين الدوال في المصفوفات:
$operations = [
'add' => fn($a, $b) => $a + $b,
'subtract' => fn($a, $b) => $a - $b,
'multiply' => fn($a, $b) => $a * $b,
'divide' => fn($a, $b) => $b != 0 ? $a / $b : null
];
$op = 'multiply';
echo $operations[$op](5, 3); // 15
// إرجاع الدوال من الدوال:
function getOperation($type) {
$ops = [
'add' => fn($a, $b) => $a + $b,
'multiply' => fn($a, $b) => $a * $b
];
return $ops[$type] ?? fn($a, $b) => null;
}
$operation = getOperation('add');
echo $operation(10, 5); // 15
?>
التطبيق الجزئي
إنشاء دوال متخصصة عن طريق ملء بعض الوسائط مسبقاً:
<?php
// مساعد التطبيق الجزئي:
function partial($func, ...$boundArgs) {
return function(...$args) use ($func, $boundArgs) {
return $func(...$boundArgs, ...$args);
};
}
// الدالة الأساسية:
function greetUser($greeting, $punctuation, $name) {
return "$greeting، $name$punctuation";
}
// إنشاء دوال متخصصة:
$sayHello = partial('greetUser', 'مرحباً', '!');
$sayGoodbye = partial('greetUser', 'وداعاً', '.');
echo $sayHello('أحمد'); // مرحباً، أحمد!
echo $sayGoodbye('فاطمة'); // وداعاً، فاطمة.
// مثال عملي:
function multiply($a, $b) {
return $a * $b;
}
$double = partial('multiply', 2);
$triple = partial('multiply', 3);
echo $double(5); // 10
echo $triple(5); // 15
?>
الكَرِّي
تحويل دالة بوسائط متعددة إلى تسلسل من الدوال:
<?php
// دالة عادية:
function add($a, $b, $c) {
return $a + $b + $c;
}
// نسخة كَرِّي:
function add_curried($a) {
return function($b) use ($a) {
return function($c) use ($a, $b) {
return $a + $b + $c;
};
};
}
// الاستخدام:
echo add_curried(1)(2)(3); // 6
$add1 = add_curried(1);
$add1and2 = $add1(2);
echo $add1and2(3); // 6
?>
أمثلة متقدمة عملية
مثال 1: معالجة خط الأنابيب
<?php
function pipeline($input, ...$functions) {
return array_reduce($functions, function($carry, $func) {
return $func($carry);
}, $input);
}
// خطوات المعالجة:
$trim = fn($s) => trim($s);
$lowercase = fn($s) => strtolower($s);
$removeSpaces = fn($s) => str_replace(' ', '-', $s);
$slug = pipeline(
" مرحبا بالعالم ",
$trim,
$lowercase,
$removeSpaces
);
echo $slug; // مرحبا-بالعالم
?>
تمرين تطبيقي
المهمة: بناء مكتبة دوال متقدمة:
- أنشئ دالة
pipe() تؤلف الدوال من اليسار إلى اليمين
- أنشئ دالة
retry() تعيد محاولة استدعاء عكسي عند الفشل حتى N مرة
- أنشئ محاكي دالة
debounce() التي تؤخر التنفيذ
- أنشئ دالة
once() تضمن تشغيل استدعاء عكسي مرة واحدة فقط
- إضافة: أنشئ دالة
memoizeWith() تقبل مولد مفتاح ذاكرة تخزين مؤقت مخصص
- إضافي: ابنِ نظام أحداث بسيط باستخدام الاستدعاءات العكسية
- تحدي: نفذ نمط يشبه Promise باستخدام الاستدعاءات العكسية والمولدات
أفضل الممارسات للدوال المتقدمة
- استخدم الدوال المتغيرة لـ APIs مرنة، لكن وثق أنواع الوسائط المتوقعة
- فضّل الوسائط المسماة للدوال ذات المعاملات الاختيارية الكثيرة
- قم دائماً بتضمين الحالات الأساسية في الدوال التكرارية
- استخدم المولدات للتكرار الفعال للذاكرة عبر مجموعات البيانات الكبيرة
- خزّن الدوال النقية المكلفة مؤقتاً (بدون آثار جانبية)
- استخدم الدوال عالية الرتبة لإنشاء كود قابل لإعادة الاستخدام والتركيب
- وثّق توقيعات الاستدعاء العكسي وأنواع الإرجاع
- كن حذراً مع عمق التكرار - فكر في البدائل التكرارية
- استخدم تلميحات النوع وأنواع الإرجاع حتى مع الأنماط المتقدمة
الملخص
تمكّن مفاهيم الدوال المتقدمة أنماط برمجة قوية:
- الدوال المتغيرة: قبول وسائط غير محدودة باستخدام
...
- الوسائط المسماة: تمرير الوسائط حسب الاسم للوضوح
- التكرار: الدوال التي تستدعي نفسها
- الاستدعاءات العكسية: تمرير الدوال كوسائط
- الدوال عالية الرتبة: الدوال التي تعمل على الدوال
- المولدات: التكرار الفعال للذاكرة باستخدام
yield
- التخزين المؤقت: تخزين النتائج مؤقتاً للأداء
- التركيب: دمج الدوال البسيطة في معقدة
- التطبيق الجزئي: ملء وسائط الدالة مسبقاً
- الكَرِّي: تحويل الدوال متعددة الوسائط