連載
» 2017年10月11日 05時00分 公開

PHPの少し変わった関数の使い方――引数のデフォルト値、可変長引数、可変関数、コールバック関数、クロージャWeb業界で働くためのPHP入門(11)(2/2 ページ)

[齊藤新三(著)/山田祥寛(監修),WINGSプロジェクト]
前のページへ 1|2       

コールバック関数

 少し話題を変えて、ここからさらに関数の変わった使い方を紹介します。その1つ目がコールバック関数です。

関数を変数のように扱える可変関数

 コールバック関数を紹介する前に、関数が変数のように扱える仕組みである可変関数を紹介しておきましょう。以下のuseVariableFunction.phpを作成し、実行してください。

<?php
function concatenateSpace(string $firstName, string $lastName): string
{
    return $lastName." ".$firstName;
}
 
function concatenateDot(string $firstName, string $lastName): string
{
    return $lastName."・".$firstName;
}
 
$lName = "齊藤";
$fName = "新三";
$funcNameList = ["concatenateSpace", "concatenateDot"];
foreach($funcNameList as $funcName) {
    $name = $funcName($fName, $lName);
    print($funcName."関数での結合結果: ".$name."<br>");
}
リスト5 phplesson/chap11/useVariableFunction.php

 実行結果は以下の通りです。

concatenateSpace関数での結合結果: 齊藤 新三
concatenateDot関数での結合結果: 齊藤・新三

 関数定義部分に関しては、特に新しいことはありません。ここでは2個の関数を用意しています。その関数名をそのまま配列にしているのが14行目です。$funcNameListの各要素は関数名です。それをループさせているのが15行目のforeachです。各要素は変数$funcNameに格納されています。ということは、$funcNameは関数名を表す文字列として理解できます。事実、17行目ではそれを出力しています。実行結果もそのようになっています。

 問題は、16行目です。文字列変数であるはずの$funcNameの後ろに()を付けています。これは、関数のように見えます。事実、()内には引数として$fNameと$lNameが渡され、さらには戻り値を格納する$nameまで用意されています。

図2 リスト5の$funcNameの使われ方

 このように、関数名を表す変数の後ろに「()」を付けて関数として扱うことがPHPでは可能であり、これを「可変関数」といいます。可変関数は、関数を変数化できますので、リスト5のように関数名の配列を用意し次々呼び出すといったことが可能となるのです。

関数を引数にしてしまうコールバック関数

 可変関数で関数が変数化できることを知りました。この延長で、PHPでは関数を引数化してしまうことが可能です。これを「コールバック関数」といいます。例を見てみましょう。以下のuseCallbackFunction.phpを作成し、実行してください。

<?php
function concatenateSpace(string $firstName, string $lastName): string
{
    return $lastName." ".$firstName;
}
 
function useConcatenate(array $name, callable $func)
{
    $concatName = $func(...$name);
    print($func."関数での結合結果: ".$concatName."<br>");
}
$nameParam = ["新三", "齊藤"];
useConcatenate($nameParam, "concatenateSpace");
リスト6 phplesson/chap11/useCallbackFunction.php

 実行結果は以下の通りです。

concatenateSpace関数での結合結果: 齊藤 新三

 1つ目の関数concatenateSpace()は問題ないでしょう。問題は2つ目の関数useConcatenate()です。特に第2引数$funcの扱いです。関数内の9行目では、$funcを以下のように関数として扱っています。

$func(...$name)

 つまりuseConcatenate()関数では、そもそも第2引数として変数化された関数が渡されることを想定しているのです。このような関数の使い方を「コールバック関数」といいます。コールバック関数が使われている関数を利用するには、14行目のようにその引数部分に関数名を渡します。

図3 リスト6の$funcの使われ方

コールバック関数を利用するメリット

 ここで紹介したコールバック関数ですが、これを利用すると効率良いコードを記述できる場合があります。一例を挙げましょう。

 PHPに用意されている関数に「array_map()」というのがあります。「関数リファレンス」を参照すると、第1引数にコールバック関数を渡すことができ、配列の各要素にその関数を適用してくれるというものです。このコールバック関数に、例えば「trim()」関数を適用すると、以下のような処理が可能となります。useArrayMapAndTrim.phpを作成し、実行してください。

<?php
$params = ["  齊藤 ", "  新三 ", " プログラマ  "];
print("<pre>");
var_dump($params);
print("</pre>");
$trimedParams = array_map("trim", $params);
print("<pre>");
var_dump($trimedParams);
print("</pre>");
リスト7 phplesson/chap11/useArrayMapAndTrim.php

 実行結果は以下の通りです。

array(3) {
  [0]=>
  string(9) "  齊藤 "
  [1]=>
  string(9) "  新三 "
  [2]=>
  string(18) " プログラマ  "
}
array(3) {
  [0]=>
  string(6) "齊藤"
  [1]=>
  string(6) "新三"
  [2]=>
  string(15) "プログラマ"
}

 6行目が該当処理です。前後に空白が含まれた文字列配列から空白が除去されていますね。ここでは、こちらで用意した配列ですが、例えば、連載第8回で紹介した$_POSTにこれを適用すると、Webフォームの入力値全てから前後の空白を一挙に除去することが可能です。

クロージャ

 PHPでは、関数が変数のように扱われることを解説してきました。いよいよ最後です。

引数に直接関数定義を記述するクロージャ

 リスト6では引数として関数を渡しました。そこでは、関数を別に定義し、その関数名を渡すという方法でした。PHPでは、その関数定義を直接引数の中で記述することが可能です。例を見てみましょう。以下のuseClosure.phpを作成し、実行してください。

<?php
function useConcatenate(array $name, callable $func)
{
    $concatName = $func(...$name);
    print("無名関数での結合結果: ".$concatName."<br>");
}
 
$nameParam = ["新三", "齊藤"];
useConcatenate($nameParam, function(string $firstName, string $lastName): string
    {
        return $lastName." ".$firstName;
    }
);
リスト8 phplesson/chap11/useClosure.php

 実行結果は以下の通りです。

無名関数での結合結果: 齊藤 新三

 useConcatenate()関数はリスト6とほぼ同じです。違うのはprint()で関数名表記の部分に、引数を使っていないところぐらいです。

 ここでのポイントは、この関数を呼び出している9行目です。リスト6ではこの第2引数に関数名を渡していました。一方、リスト8の9行目ではその部分に直接関数定義を記述しています。

図4 リスト8では関数定義を引数内に直接記述

 このように、引数に定義が直接記述された関数のことを「クロージャ」、あるいは「無名関数」といいます。図4を見てもらえれば、この関数に関数名がないということが分かります。ここから「無名関数」という名称が付けられています。

無名関数をそのまま変数化

 リスト8では引数に関数定義を直接記述しました。ということは、関数定義そのものを変数として扱うことが可能なのではないかと思えてきます。これは正しく、PHPでは可能です。例を見てみましょう。以下のuseClosureAndUse.phpを作成し、実行してください。

<?php
$lName = "齊藤";
$fName = "新三";
 
$concat1 = function(string $firstName, string $lastName): string
{
        return $lastName." ".$firstName;
};
 
$concatName1 = $concat1($fName, $lName);
print("無名関数の変数を使っての結合結果: ".$concatName1."<br>");
$concat2 = function() use ($fName, $lName): string
{
        return $lName." ".$fName;
};
 
$concatName2 = $concat2();
print("無名関数とuseを使っての結合結果: ".$concatName2."<br>");
リスト9 phplesson/chap11/useClosureAndUse.php

 実行結果は以下の通りです。

無名関数の変数を使っての結合結果: 齊藤 新三
無名関数とuseを使っての結合結果: 齊藤 新三

 5〜8行目が該当します。ここでは、変数$concat1に関数定義をそのまま代入しています。まさしく関数の変数化です。その上で、10行目でこの関数変数を呼び出しています。

グローバル変数を関数内に持ち込むuse

 では、13行目の記述は何を表すのでしょうか。そこで、前回記事の変数のスコープについて思い出してください。関数内の変数はローカルスコープであり、関数内でしか使えませんし、逆に、関数外の変数はグローバルスコープであり、関数内では使えません。これは無名関数でも同じです。実際に、7行目を以下のように記述するとエラーとなります。

return $lName." ".$fName;

 グローバルスコープである$lNameや$fNameを関数内で利用しようとするからです。ところが、そのグローバルスコープの変数を無名関数内に持ち込むことができる方法があります。それが「use」です。13行目のようにfunction()の後にuse()を記述し、()内に関数内に持ち込む変数を記述します。そうすることで、15行目のようにグローバルスコープの変数を利用できます。

次回は関数のジェネレーターついて

 次回はジェネレーターを扱います。

今回のサンプルコード

 今回のサンプルコードはこちらからダウンロードできます。

前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。