聊聊這三個東西在 php 裡的差異好了

callback

callback,是指任何可以 callable 的程式片段,無論是 php 原生的 function,自己寫的 function、class 的 method,亦或是 closure,只要他是 callable 的就好,for example:

echoString 是一個 callable 的 function, 所以為 callback

1
2
3
4
5
funcion echoString ($string)
{
echo $string . PHP_EOL;
}
call_user_func('echoString', 'php awesome !');

myEcho::string() 是一個 callable 的 method, 所以他也是 callback

1
2
3
4
5
6
7
8
class myEcho
{
public static function string($string)
{
echo $string . PHP_EOL;
}
}
call_user_func(['myEcho', 'string'], 'php awesome !');

也可以使用 php 原生的 function,只要是 callable 的,就是 callback

1
2
3
4
stringList = ['php','awesome'];
$result = array_filter($stringList, 'is_int');

var_export($result);

在這個例子裡, $echoString 是個 callable 的 closure,所以也可以稱它為 callback

1
2
3
4
5
6
$stringList = ['php','awesome'];
$echoString = function ($string) {
echo $string . PHP_EOL;
};

array_walk($stringList, $echoString);

anonymous function

php 5.3 後引入的東西,他讓你可以讓函示隨意指派給某個變數,只是最後還是用 closure 來實作,for example

1
2
3
4
5
6
7
8
9
10
11
12
13
// anonymous function, 沒有 function name
$isInteger = function ($integer) {
return is_int($integer);
};

// 會發現到他是 closure 的 instance
var_export($isInteger);

// 因為他是 callable 的,所以都可以做到 callback 可以做的事情
$stringList = ['php','awesome'];
$result = array_filter($stringList, $isInteger);

var_export($result);

closure

這就是匿名函式的實作方法,也就是為了要做到 anonymous function,就必須要有 closure 來幫你處理這件事,他有幾個特性:

  1. 變數的 scope 的限制, 所以下面的例子裡, $stock 會被暫存下來

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $setStock = function ($default) {
    $stock = $default;
    return function () use (&$stock) {
    $stock --;
    if (1 > $stock) {
    echo 'out of stock !';
    return;
    }
    return $stock;
    };
    };

    $sold = $setStock(3);
    echo $sold() . PHP_EOL;
    echo $sold() . PHP_EOL;
    echo $sold() . PHP_EOL;
    echo $sold() . PHP_EOL;
  2. currying

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $currying = function ($default) {
    return function ($integer) use ($default) {
    return $default += $integer;
    };
    };

    // 這看起來有沒有很像 js 的感覺?
    echo $currying(1)(1) . PHP_EOL;
    echo $currying(2)(1) . PHP_EOL;
    echo $currying(3)(1) . PHP_EOL;

所以需要注意 type hint

由上面的例子可以知道,anonymous function 的 type hint 有兩種,分別為 Closure 以及 Callble,而 php 原生的 function 只有 Callable,可以看看下面的例子會因為 type hint 的差異而有所不同

如果是 callable 的話,就不會有問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyString
{
// 注意這邊的 type hint 為 callable
public function test($string, Callable $function)
{
$isString = $function($string);
echo ($isString) ? 'yes' : 'no';
echo PHP_EOL;
}
}

$myString = new MyString;

// 使用 php 原生 function
$myString->test('I_AM_STRING', 'is_string');

// 或者 anonymous function
$myString->test('I_AM_STRING', function($string) {
return is_string($string);
});

如果是 Closure 的話,就要注意 php 原生 function 的問題了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyString
{
// 注意這邊的 type hint 為 callable
public function test($string, Closure $function)
{
$isString = $function($string);
echo ($isString) ? 'yes' : 'no';
echo PHP_EOL;
}
}

$myString = new MyString;

// 使用 php 原生 function,你會得到 type hint 錯誤的訊息
// $myString->test('I_AM_STRING', 'is_string');

// 但是還是可以修改成這樣,只是 php 7.1 以上限定 XD
// $myString->test('I_AM_STRING', Closure::fromCallable('is_string'));

// 所以如果遇到 type hint 為 closure 的話,還是把 `is_string` 用 anonymous function 包起來吧
$myString->test('I_AM_STRING', function($string) {
return is_string($string);
});