skip to content

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

callback

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

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

funcion echoString ($string)
{
echo $string . PHP_EOL;
}
call_user_func('echoString', 'php awesome !');

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

class myEcho
{
public static function string($string)
{
echo $string . PHP_EOL;
}
}
call_user_func(['myEcho', 'string'], 'php awesome !');

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

stringList = ['php','awesome'];
$result = array_filter($stringList, 'is_int');
var_export($result);

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

$stringList = ['php','awesome'];
$echoString = function ($string) {
echo $string . PHP_EOL;
};
array_walk($stringList, $echoString);

anonymous function

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

// 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 來幫你處理這件事,他有幾個特性:

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

$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;

currying

$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 的話,就不會有問題

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 的問題了

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);
});