الوظائف المتقدمة

لقد أتقنت أساسيات الدوال. يتيح لك هذا الدرس كتابة دوال أكثر مرونة وحداثة وتتماشى مع أسلوب PHP 8 — مثل المعلمات المتغيرة العدد، والحجج المسماة، والدوال السهمية — وكلها نتائج للتطور المستمر للغة PHP.

1. المعلمات المتغيرة العدد (...$args)

لا تعرف عدد المعلمات التي سيتم تمريرها؟ استخدم ... لتجميعها جميعًا في مصفوفة:

PHP
<?php
function sum(...$numbers): int {
    return array_sum($numbers);
}

echo sum(1, 2);           // 3
echo sum(1, 2, 3, 4, 5);  // 15
echo sum();               // 0

// You can also have fixed parameters before the variadic ones
function sendMessage(string $to, string ...$messages): void {
    foreach ($messages as $msg) {
        echo "Sent to {$to}: {$msg}<br>";
    }
}

sendMessage("John", "Good morning", "How's lunch?", "Good night");
// Sent to John: Good morning
// Sent to John: How's lunch?
// Sent to John: Good night
?>

▶ مثال: ... لتوزيع مصفوفة على المعلمات

PHP
<?php
function add(int $a, int $b, int $c): int {
    return $a + $b + $c;
}

$nums = [1, 2, 3];
echo add(...$nums);  // 6 (the array is spread into 3 separate arguments)

// Commonly used with array_push and similar functions
$stack = ["a", "b"];
array_push($stack, ...["c", "d", "e"]);
print_r($stack);  // ["a", "b", "c", "d", "e"]
?>
▶ جرّب الكود

تعمل ... في كلا الاتجاهين — ففي تعريف الدالة، تقوم بتجميع المعلمات في صفيف؛ أما في موقع الاستدعاء، فتقوم بتوزيع الصفيف على المعلمات.


2. المعلمات المسماة (PHP 8.0)

لا داعي لحفظ ترتيب المعلمات — قم بتمرير الحجج بالاسم:

▶ مثال: الحجج المسماة

PHP
<?php
function createUser(
    string $name,
    int $age,
    string $email = "",
    bool $isAdmin = false
): array {
    return compact('name', 'age', 'email', 'isAdmin');
}

// Traditional: must follow parameter order
$u1 = createUser("John", 25, "john@example.com", true);

// Named arguments: order doesn't matter — skip parameters with defaults
$u2 = createUser(
    name: "Jane",
    age: 22,
    isAdmin: true
    // email uses the default
);

// When a function has many parameters, named arguments dramatically improve readability
setcookie(
    name: "theme",
    value: "dark",
    expires_or_options: time() + 3600,
    httponly: true
);
?>
▶ جرّب الكود
💡 نصيحة: تُعد الحجج المسماة مفيدة بشكل خاص في الحالات التالية: (1) عندما تحتوي الدالة على العديد من المعلمات، ومعظمها بقيم افتراضية؛ (2) عندما ترغب في تمرير الحجج القليلة الأخيرة فقط؛ (3) عندما ترغب في كتابة كود ذاتي التوثيق — حيث يصبح createUser(name: "John", age: 25) واضحًا على الفور.


3. التمرير بالمرجع (&$var)

بشكل افتراضي، تقوم لغة PHP بتمرير معلمات الدالة بالقيمة — أي أن تعديل معلمة داخل الدالة لا يؤثر على المتغير الخارجي. أضف & للتمرير بالمرجع:

PHP
<?php
// Pass by value (default): internal modification doesn't affect the outside
function addTen(int $n): void {
    $n += 10;
}
$x = 5;
addTen($x);
echo $x;  // 5 (unchanged)

// Pass by reference: internal modification directly affects the outer variable
function addTenRef(int &$n): void {
    $n += 10;
}
addTenRef($x);
echo $x;  // 15 (changed!)
?>
التمرير بالقيمة التمرير بالمرجع
بناء الجملة function f($x) function f(&$x)
تعديل داخلي لا يؤثر على المتغير الخارجي يؤثر على المتغير الخارجي
الأنسب لـ معظم الحالات عندما تحتاج إلى «إرجاع» قيم متعددة
⚠️ تحذير: التمرير بالمرجع يجعل سلوك الدالة «غامضًا» — حيث لا يدرك المستدعي أن متغيره قد تغير. وعادةً ما يكون استخدام قيمة الإرجاع أكثر وضوحًا. لا تستخدم المراجع إلا عندما يكون عليك تعديل المصفوفة الأصلية في مكانها (على سبيل المثال، sort(&$arr)).


4. نطاق المتغيرات

في لغة PHP، يُعد الجزء الداخلي للدالة والجزء الخارجي عالمين منفصلين:

PHP
<?php
$globalVar = "I'm outside";

function test(): void {
    // echo $globalVar;  // Warning: Undefined variable
    // Functions can't directly access outer variables

    $localVar = "I'm inside";
    echo $localVar;  // OK
}

test();
// echo $localVar;  // Warning: can't access the function's inner variable either
?>

(1) استخدام «global» لكسر الحاجز

PHP
<?php
$counter = 0;

function increment(): void {
    global $counter;  // Declare: I want to use the global variable
    $counter++;
}

increment();
increment();
echo $counter;  // 2
?>

(2) المصفوفة الفائقة $GLOBALS

PHP
<?php
$name = "John";

function showName(): void {
    echo $GLOBALS['name'];  // John
}
showName();
?>
💡 نصيحة: الكلمة الرئيسية global هي «شر لا بد منه» — اعلم بوجودها، لكن تجنب استخدامها كلما أمكن ذلك. فالمتغيرات العالمية تجعل اختبار الكود وصيانته أمرًا صعبًا. وفي معظم الأحيان، قم بتمرير البيانات إلى الدوال كمعلمات.


5. المتغيرات الثابتة

المتغير static داخل الدالة يحتفظ بقيمته بين عمليات الاستدعاء:

PHP
<?php
function getNextId(): int {
    static $id = 0;
    $id++;
    return $id;
}

echo getNextId();  // 1
echo getNextId();  // 2
echo getNextId();  // 3

// A static variable is only initialized once — on the first call
// Subsequent calls preserve the previous value
?>
💡 نصيحة: تُعد المتغيرات الثابتة مفيدة لتخزين النتائج المحسوبة مؤقتًا (لتجنب إعادة الحساب) أو لإنشاء معرّفات فريدة. لكن لا تستخدمها كبديل لخصائص الفئة — فهذا هو الغرض من البرمجة الموجهة للكائنات.


6. الدوال المجهولة (الإغلاقات)

لا تحتاج الدوال إلى أسماء — يمكن تعيينها إلى متغيرات وتمريرها كمعاملات:

PHP
<?php
// Anonymous function assigned to a variable
$greet = function(string $name): string {
    return "Hello, {$name}!";
};

echo $greet("John");  // Hello, John!

// Passed as an argument to another function (callback)
$numbers = [1, 2, 3, 4, 5];
$doubled = array_map(function($n) {
    return $n * 2;
}, $numbers);
print_r($doubled);  // [2, 4, 6, 8, 10]
?>

(1) استخدام الكلمة الرئيسية في الدوال المجهولة

إذا احتاجت دالة مجهولة إلى متغير خارجي، فيجب استيراده باستخدام use:

PHP
<?php
$prefix = "User";

// ❌ Wrong: $prefix is not visible inside the anonymous function
// $greet = function($name) {
//     return "{$prefix}: {$name}";
// };

// ✅ Correct: import the outer variable with use
$greet = function($name) use ($prefix): string {
    return "{$prefix}: {$name}";
};

echo $greet("John");  // User: John

// Import by reference (allows modifying the outer variable)
$count = 0;
$inc = function() use (&$count): void {
    $count++;
};
$inc(); $inc();
echo $count;  // 2
?>

7. دوال السهم (PHP 7.4+)

الدوال السهمية fn() => هي النسخة الموجزة للغاية من الدوال المجهولة ذات التعبير الواحد. فهي تلتقط المتغيرات الخارجية تلقائيًا (دون الحاجة إلى use):

PHP
<?php
$factor = 3;

// Anonymous function syntax
$anon = function($n) use ($factor) {
    return $n * $factor;
};

// Arrow function syntax (automatically captures $factor)
$arrow = fn($n) => $n * $factor;

echo $anon(5);   // 15
echo $arrow(5);  // 15

// The golden pair with array_map
$nums = [1, 2, 3, 4, 5];
$tripled = array_map(fn($n) => $n * $factor, $nums);
print_r($tripled);  // [3, 6, 9, 12, 15]
?>
💡 نصيحة: لا يمكن أن تحتوي دوال السهم إلا على تعبير واحد (لا يمكن استخدام منطق متعدد الأسطر). وهي تلتقط المتغيرات الخارجية تلقائيًا بالقيمة. وهي مثالية لعمليات الاستدعاء البسيطة map / filter / usort.


8. الدوال المرتدة ونوع «callable»

يتيح لك لغة PHP استخدام سلاسل أسماء الدوال، والدوال المجهولة، وطرق الكائنات كدوال استدعاء:

PHP
<?php
// Method 1: function name as a string
function double(int $n): int {
    return $n * 2;
}
$result = array_map('double', [1, 2, 3]);
// 'double' (with quotes) — the function name string is passed to array_map

// Method 2: anonymous function
$result = array_map(fn($n) => $n * 3, [1, 2, 3]);

// Method 3: callable type declaration
function apply(callable $fn, array $data): array {
    return array_map($fn, $data);
}

$result = apply('double', [1, 2, 3]);          // ✅
$result = apply(fn($n) => $n * 2, [1, 2, 3]);  // ✅
// apply(123, [1, 2, 3]);  // TypeError!
?>

❓ أسئلة شائعة

س كم عدد المعاني التي يحملها ... في لغة PHP؟
ج اثنان. (1) في تعريف الدالة، يقوم ...$args بتجميع الحجج المتبقية في صفيف. (2) عند استدعاء الدالة، يقوم ...$arr بتوزيع الصفيف إلى حجج فردية. sum(...$nums) تعني التوزيع؛ function sum(...$nums) تعني التجميع.
س هل تؤثر الحجج المسماة على ترتيب الحجج؟ هل يمكنني المزج بين الحجج الموضعية والحجج المسماة؟
ج يمكنك المزج بينهما. القاعدة هي: يجب أن تأتي الحجج المسماة بعد الحجج الموضعية. foo(1, c: 3, b: 2) صحيحة؛ أما foo(a: 1, 2, 3) فهي غير صحيحة.
س متى يجب عليّ استخدام الدوال المجهولة مقابل الدوال السهمية؟
ج استخدم الدوال السهمية fn($x) => $x * 2 للتعبيرات المفردة. واستخدم الدوال المجهولة function($x) { ... } للمنطق متعدد الأسطر. الدوال السهمية أكثر إيجازًا وتقوم بالتقاط المتغيرات الخارجية تلقائيًا (دون الحاجة إلى use).

📖 ملخص

📝 تمارين

  1. اكتب دالة average(...$numbers): float تقبل أي عدد من المعاملات العددية وتُرجع متوسطها.
  2. استخدم array_map ودالة السهم لتحويل كل سلسلة في المصفوفة إلى أحرف كبيرة.
  3. استخدم متغيرًا ثابتًا لكتابة دالة hitCounter() تُرجع عدد الزيارات المتزايد عند كل استدعاء.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%