اذهب إلى المحتوى

قائمة الوسطاء ذات الطول المتغير

النسخة 5.6 وما بعدها:

قدم الإصدار PHP 5.6 لائحة وسطاء ذات طول متغير (وتعرف أيضًا باسم العدد المتغيّر للوسائط (varargs) أو الوسائط المتغيّرة) بإضافة ... قبل اسم الوسيط للإشارة إلى أنّ المعامل متغيّر، أي أنّها مصفوفة فيها كل المعاملات بدءًا من هذا الوسيط.

function variadic_func($nonVariadic, ...$variadic) {
    echo json_encode($variadic);
}

variadic_func(1, 2, 3, 4); 
// [2,3,4]

يمكن أن يُضاف اسم النوع قبل ...:

function foo(Bar ...$bars) {}

يمكن أن يُضاف عامل المرجعية & قبل ... وبعد اسم النوع (إن وجد)، مثال:

class Foo{}
function a(Foo &...$foos){
    $i = 0;
    foreach($a as &$foo){
        $foo = $i++;
    }
}

$a = new Foo;
$c = new Foo;
$b =& $c;
a($a, $b);
var_dump($a, $b, $c);

/*
int(0)
int(1)
int(1)
*/

من ناحية أخرى يمكن تفريغ مصفوفة الوسائط لتُمرر إلى دالة بصيغة لائحة وسطاء:

var_dump(...hash_algos());

/*
string(3) "md2"
string(3) "md4"
string(3) "md5"
...
*/

وازن الخرج السابق مع خرج الشيفرة التالية:

var_dump(hash_algos());

/*
array(46) {
    [0]=> string(3) "md2"
    [1]=> string(3) "md4"
...
}
*/

لذا يمكن إعادة توجيه الدوال إلى دوال متعددة الوسائط بسهولة، مثال:

public function formatQuery($query, ...$args){
    return sprintf($query, ...array_map([$mysqli, "real_escape_string"], $args));
}

يمكن أيضًا استخدام واجهات Traversable مثل Iterator (خاصةً العديد من أصنافها الفرعية من مكتبة SPL)، مثال:

$iterator = new LimitIterator(new ArrayIterator([0, 1, 2, 3, 4, 5, 6]), 2, 3);
echo bin2hex(pack("c*", ...$it)); 
// 020304

إذا كان المُكرِّر يكرر عددًا غير محدود من المرات، مثال:

$iterator = new InfiniteIterator(new ArrayIterator([0, 1, 2, 3, 4]));
var_dump(...$iterator);

ستسلك الإصدارات المختلفة من PHP سلوكًا مختلفًا:

  • من الإصدار PHP 7.0.0 وحتى الإصدار PHP 7.1.0 (بيتا 1): سيحدث خطأ تجزئة (segmentation fault) وسيتوقف التنفيذ مع الرمز 139.
  • في PHP 5.6: سيُعرَض خطأً فادحًا بسبب استهلاك الذاكرة ("Allowed memory size of %d bytes exhausted") وسيتوقف التنفيذ مع الرمز 255.

ملاحظة: لا تدعم الآلة الافتراضية هيب هوب HHVM (الإصدار 3.10 وحتى 3.12) تفريغ واجهات Traversable وستظهر رسالة تحذير "Only containers may be unpacked" عند المحاولة.

المعاملات الاختيارية

يمكن أن يكون للدوال معاملات اختيارية، مثال:

function hello($name, $style = 'Formal')
{
    switch ($style) {
        case 'Formal':
            print "Good Day $name";
            break;

        case 'Informal':
            print "Hi $name";
            break;

        case 'Australian':
            print "G'day $name";
            break;

        default:
            print "Hello $name";
            break;
    }
}

hello('Alice');
// Good Day Alice

hello('Alice', 'Australian');
// G'day Alice

تمرير الوسائط بالمرجعية

يمكن تمرير وسائط الدالة بالمرجعية (By Reference) مما يسمح للدالة بتعديل المتغير خارج الدالة:

function pluralize(&$word)
{
    if (substr($word, -1) == 'y') {
        $word = substr($word, 0, -1) . 'ies';
    } else {
    $word .= 's';
    }
}

$word = 'Bannana';
pluralize($word);
print $word;
// Bannanas

تُمرَّر وسائط الكائن دائمًا بالمرجعية:

function addOneDay($date)
{
    $date->modify('+1 day');
}

$date = new DateTime('2014-02-28');
addOneDay($date);
print $date->format('Y-m-d');
// 2014-03-01

يجب نسخ الكائن لتجنّب تمريره بالمرجعية بشكلٍ ضمني، ويمكن أن يُستخدم التمرير بالمرجعية كطريقة بديلة لإرجاع معاملات، مثل الدالة socket_getpeername:

bool socket_getpeername ( resource $socket , string &$address [, int &$port ] )

يهدف هذا التابع لإرجاع عنوان ومنفذ النظير (peer) ولكن بما أنّه يوجد قيمتين للإرجاع فهو يختار معاملات المرجعية، يمكن أن يُستدعى كالتالي:

if(!socket_getpeername($socket, $address, $port)) {
    throw new RuntimeException(socket_last_error());
}
echo "Peer: $address:$port\n";

لا تحتاج المتغيرات ‎$address و‎$port للتعريف مسبقًا فهي ستُعرَّف على أنّها null في البداية ثمّ تُمرَّر إلى الدالة بالقيمة null وتُعدَّل داخل الدالة ثمّ تُعرَّف في النهاية على أنّها العنوان والمنفذ في المحتوى المُستدعي.

استخدام الدالة البسيط

تُعرّف الدالة وتُنفَّذ بشكلها البسيط كما يلي:

function hello($name)
{
    print "Hello $name";
}

hello("Alice");

نطاق الدالة

المتغيرات داخل الدوال هي متغيرات معرَّفة داخل نطاق محلي، مثال:

$number = 5
function foo(){
    $number = 10
    return $number
}

foo();

خرج الشيفرة السابقة هو 10 لأنّ المتغير المعرَّف داخل الدالة ذو نطاق محلي.

البرمجة الوظيفية (functional programming)

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

الدوال المغلِّفة (Closures)

الدالة المغلِّفة هي دالة مجهولة (anonymous function) لا يمكن الوصول إليها من خارج النطاق، عندما تعرّف دالة مجهولة فأنت تنشئ فضاء اسم لهذه الدالة، يمكن لها حاليًا أن تصل إلى فضاء الاسم هذا فقط.

$externalVariable = "Hello";
$secondExternalVariable = "Foo";

$myFunction = function() {
    var_dump($externalVariable, $secondExternalVariable); 
    // تُرجع ملاحظتي خطأ بما أنّ المتغيرات لم تُعرَّف
}

لا يمكن لهذه الدالة الوصول إلى أيّ متغيرات خارجية ولتمنح فضاء الاسم هذا إمكانية الإذن بالوصول فأنت تحتاج للتعريف عن ذلك باستخدام use()‎.

$myFunction = function() use($externalVariable, $secondExternalVariable) {
    var_dump($externalVariable, $secondExternalVariable); 
    // Hello Foo
}

يعود هذا بشكل كبير إلى نطاق المتغير الضيق في PHP، فإذا لم يكن المتغير معرّفًا ضمن النطاق أو لم يُجلب باستخدام الكلمة المفتاحية global فإنّه غير موجود.

لاحظ أيضًا أنّ وراثة المتغيرات من النطاق الأب ليس مثل استخدام المتغيرات العامة، توجد المتغيرات العامة في النطاق العام وهو نفسه بغض النظر عن الدالة التي تُنفَّذ، أما النطاق الأب لدالة مغلِّفة هو الدالة التي عُرِّفت ضمنها الدالة المغلِّفة (وليس بالضرورة الدالة التي اُستدعيت منها).

تستخدم الدوال المغلِّفة منهجية الربط المبكر (early-binding)، أي أنّ المتغيرات التي تُمرَّر إلى فضاء اسم الدالة المغلِّفة باستخدام الكلمة المفتاحية use سيكون لها نفس القيم عند تعريف الدالة المغلِّفة ولتغيير هذا السلوك يجب تمرير القيمة بالمرجعية.

$rate = .05;

// تصدير متغير إلى نطاق الدالة المغلِّفة
$calculateTax = function ($value) use ($rate) {
    return $value * $rate;
};

$rate = .1;
print $calculateTax(100); // 5

$rate = .05;
// تصدير متغير إلى نطاق الدالة المغلِّفة
$calculateTax = function ($value) use (&$rate) { // (1)
    return $value * $rate;
};

$rate = .1;
print $calculateTax(100); // 10

لاحظ في الموضع (1) استخدام & قبل ‎$rate.

إنّ الوسطاء الافتراضيين غير مطلوبين ضمنيًّا عند تعريف دوال مجهولة مع/دون دوال مغلِّفة.

$message = 'Im yelling at you';

$yell = function() use($message) {
    echo strtoupper($message);
};

$yell(); 
// IM YELLING AT YOU

الإسناد إلى متغيرات

يمكن أن تُسنَد الدوال المجهولة إلى متغيرات للاستخدام كمعاملات حيث يُتوقّع رد النداء.

$uppercase = function($data) {
    return strtoupper($data);
};

$mixedCase = ["Hello", "World"];
$uppercased = array_map($uppercase, $mixedCase);
print_r($uppercased);

يمكن استخدام هذه المتغيرات مثلما نستدعي دالة مستقلة:

echo $uppercase("Hello world!"); 
// HELLO WORLD!

الكائنات كدالة

class SomeClass {
    public function __invoke($param1, $param2) {
        // أضف الشيفرة هنا
    }
}

$instance = new SomeClass();

// __invoke() استدعاء التابع
$instance('First', 'Second'); 

يمكن استخدام كائن مع التابع ‎__invoke‎ تمامًا مثل أي دالة أخرى، سيكون بإمكان التابع ‎__invoke‎ الوصول إلى كل خاصيّات الكائن واستدعاء أيّ تابع.

استخدام المتغيرات الخارجية

تُستخدم البنية use لاستيراد المتغيرات إلى داخل نطاق الدالة المجهولة:

$divisor = 2332;
$myfunction = function($number) use ($divisor) {
    return $number / $divisor;
};

echo $myfunction(81620); // 35

يمكن أيضًا استيراد المتغيرات بالمرجع:

$collection = [];
$additem = function($item) use (&$collection) {
    $collection[] = $item;
};

$additem(1);
$additem(2);
//$collection = [1,2]

الدالة المجهولة

الدالة المجهولة هي دالة دون اسم، مثال:

function() {
    return "Hello World!";
};

تُعامل الدالة المجهولة في PHP كأنها تعبير لذا يجب أن تنتهي بفاصلة منقوطة ;، ويجب أن تُسند الدالة المجهولة إلى متغير:

// دالة مجهولة مُسندة إلى متغير
$sayHello = function($name) {
    return "Hello $name!";
};

print $sayHello('John'); 
// Hello John

أو يجب أن تُمرَّر كمعامل دالة أخرى.

$users = [
    ['name' => 'Alice', 'age' => 20],
    ['name' => 'Bobby', 'age' => 22],
    ['name' => 'Carol', 'age' => 17]
];

// تنفيذ دالة مجهولة على عناصر المصفوفة
$userName = array_map(function($user) {
    return $user['name'];
}, $users);

print_r($usersName); 
// ['Alice', 'Bobby', 'Carol']

أو تُرجَع من دالة أخرى.

التنفيذ الذاتي للدوال المجهولة:

// PHP 7.x
(function () {
    echo "Hello world!";
})();

// PHP 5.x
call_user_func(function () {
    echo "Hello world!";
});

تمرير معامل إلى الدوال المجهولة أثناء التنفيذ الذاتي:

// PHP 7.x
(function ($name) {
    echo "Hello $name!";
})('John');

// PHP 5.x
call_user_func(function ($name) {
    echo "Hello $name!";
}, 'John');

الدوال النقية (Pure functions)

الدالة النقية هي الدالة التي تعطي نفس الخرج دومًا مهما كان الدخل وخالية من التأثير الجانبي.

// دالة نقية
function add($a, $b) {
    return $a + $b;
}

تغيّر بعض التأثيرات الجانبية نظام الملفات وتتفاعل مع قواعد البيانات وتطبع على الشاشة.

// دالة غير نقيّة
function add($a, $b) {
    echo "Adding...";
    return $a + $b;
}

توابع وظيفية شائعة في PHP

الربط

تطبيق دالة على جميع عناصر المصفوفة:

array_map('strtoupper', $array);

يجب أن تنتبه أنّ هذا التابع الوحيد الذي يأتي فيه رد النداء أولًا.

الاختزال (أو الطي)

اختزال المصفوفة إلى قيمة واحدة:

$sum = array_reduce($numbers, function ($carry, $number) {
    return $carry + $number;
});

الترشيح

تُرجَع فيه عناصر المصفوفة التي يكون نتيجة رد النداء لها true فقط.

$onlyEven = array_filter($numbers, function ($number) {
    return ($number % 2) === 0;
});

استخدام الدوال المدمجة كردود نداء

يمكن أن تضع سلسلة نصية مع دالة PHP مدمجة في الدوال التي تأخذ معاملًا من النوع callable، من الشائع استخدام trim معاملًا للدالة array_map لإزالة المسافات البيضاء من بداية ونهاية السلاسل النصية في المصفوفة.

$arr = [' one ', 'two ', ' three'];
var_dump(array_map('trim', $arr));

/*
array(3) {
    [0] => string(3) "one"
    [1] => string(3) "two"
    [2] => string(5) "three"
}
*/

النطاق

الدالة المجهولة في PHP لها نطاقها الخاص مثل أيّ دالة أخرى، فيمكن للدالة المجهولة في جافاسكربت مثلًا الوصول إلى متغير خارج النطاق أما في PHP فإنّ هذا غير ممكن.

$name = 'John';

// دالة مجهولة تحاول الوصول إلى متغير خارج النطاق
$sayHello = function() {
    return "Hello $name!";
}
print $sayHello('John'); // Hello !

// إذا كانت الملاحظات ممكّنة Undefined variable $name ستظهر لنا الملاحظة

تمرير دالة رد نداء كمعامل

يوجد العديد من الدوال في PHP تقبل دوال رد نداء (callback) معرَّفة من قبل المستخدم كمعامل مثل الدوال: calluserfunc()‎ وusort()‎ وarray_map‎()‎.

يوجد طرائق مختلفة لتمرير دوال رد النداء المعرَّفة من قبل المستخدم كمعاملات وتختلف هذه الطرائق وفقًا للمكان الذي عُرِّفَت فيه هذه الدوال: النمط الإجرائي:

function square($number)
{
    return $number * $number;
}

$initial_array = [1, 2, 3, 4, 5];
$final_array = array_map('square', $initial_array);

var_dump($final_array); 
// array(5) { [0]=> int(1) [1]=> int(4) [2]=> int(9) [3]=> int(16) [4]=> int(25) }

النمط الكائني التوجه:

class SquareHolder
{
    function square($number)
    {
        return $number * $number;
    }
}

$squaredHolder = new SquareHolder();
$initial_array = [1, 2, 3, 4, 5];
$final_array = array_map([$squaredHolder, 'square'], $initial_array);

var_dump($final_array); 
// array(5) { [0]=> int(1) [1]=> int(4) [2]=> int(9) [3]=> int(16) [4]=> int(25) }

النمط الكائني التوجه باستخدام تابع ساكن:

class StaticSquareHolder
{
    public static function square($number)
    {
        return $number * $number;
    }
}

$initial_array = [1, 2, 3, 4, 5];
$final_array = array_map(['StaticSquareHolder', 'square'], $initial_array);

// أو
$final_array = array_map('StaticSquareHolder::square', $initial_array); 
// PHP >= 5.2.3 في

var_dump($final_array); 
// array(5) { [0]=> int(1) [1]=> int(4) [2]=> int(9) [3]=> int(16) [4]=> int(25) }

ترجمة -وبتصرف- للفصول [Functions - Functional Programming] من كتاب PHP Notes for Professionals book

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...