̃Gg[͂ĂȃubN}[Nɒlj

PHP :: コールバック関数



PHP では、配列の値や正規表現にマッチした値に対してビルトイン関数やユーザが自由に定義した関数を適用する仕組みがあります。
これを「コールバック関数」といいます。

コールバック関数のメリット

配列のすべての値や正規表現にマッチした文字列すべてに対して特定の処理を行いたい場合、foreach 等を使って反復処理を書かなくていいので、コードがすっきりするメリットがあります。

コールバック関数を使えるPHPのビルトイン関数

関数名説明
array_map指定した配列の要素にコールバック関数を適用する
array_filterコールバック関数を使用する配列要素フィルタ(例えば、キーが奇数の要素のみ取り出すとか)
array_reduceコールバック関数を用いて配列を普通の値に変更することにより、 配列を再帰的に減らす(例えば、全要素の積を取得するとか)
array_walk配列の全ての要素にユーザ関数を適用する
array_walk_recursive配列の全ての要素に、ユーザー関数を再帰的に適用する
preg_replace_callback正規表現検索を行い、コールバック関数を使用して置換を行う

ビルトイン関数を適用したコールバック関数の例

配列のすべての値にビルトイン関数「trim」を適用してみます。

$arr[0] = ' 000 ';
$arr[1] = ' 111 ';
$arr[2] = ' 222 ';
 
$arr = array_map('trim', $arr);
var_dump($arr);
 
# 結果
array(3) {
  [0]=>
  string(3) "000"
  [1]=>
  string(3) "111"
  [2]=>
  string(3) "222"
}

なお、以下の関数は適用できません。

array(), echo(), empty(), eval(), exit(), isset(), list(), print(), unset() 

ユーザが定義した関数を適用したコールバック関数の例

下記の func1 に引数を与えてコールすると、引数の文字列の [ から ] の中の文字列抜き出し、その文字列を反転させ、[ と ] を除去した文字列を返却する関数です。
サンプルなので、実用性はまったくありませんw
なお、func1 の中で定義されている cb_func1 がコールバック関数になります。

function func1($val)
{
    return  preg_replace_callback('/\[(.+?)\]/', 'cb_func1', $val);
}
 
function cb_func1($array)
{
    return strrev($array[1]);
}
 
$ret = func1('[ABC]-[DEF]-[GHI]');
var_dump($ret);
 
# 結果
string(11) "CBA-FED-IHG"

挙動の説明

  1. func1 関数は、引数に渡された文字列を preg_replace_callback 関数に渡します。
  2. preg_replace_callback 関数は正規表現にマッチした文字列を コールバック関数 cb_func1 に配列で渡します。
  3. cb_func1 は strrev 関数で文字列を反転させます。
  • strrev 関数の引数が $array[1] となっているのは、正規表現のキャプチャの仕様によるものです。
  • この場合、グルーピングの () は1つだけなので、正規表現のキャプチャは、マッチした文字列全体を $array[0]、マッチした文字列自体を $array[1]に格納しています。


※. 説明の為、上記サンプルを示しましたが、コールバック関数を使わなくても書くことができます。一応サンプルを書いておきます。

function func1($val)
{
    return  preg_replace('/\[(.+?)\]/e', 'strrev(\\1)', $val);
}

パターン修飾子の「e オプション」を指定して、PHPコードとして評価させます



クラスでコールバック関数を使う場合

クラス内でコールバック関数を使う場合は、注意点があります。

以下はエラーになります。

class hoge
{
    public function func1($val)
    {
        return  preg_replace_callback('/\[(.+?)\]/', 'cb_func1', $val);
    }
    private function cb_func1($array)
    {
        return strrev($array[1]);
    }
}
 
$hoge = new hoge;
var_dump($hoge->func1('[ABC]-[DEF]-[GHI]'));
 
# 結果
Warning:
preg_replace_callback() [function.preg-replace-callback]: 
Requires argument 2, 'cb_func1', to be a valid callback in /path/to/hoge.php on line 6

コールバック関数をクラス外(グローバルスコープ)に配置すれば正常に動作します。

class hoge
{
    public function func1($val)
    {
        return  preg_replace_callback('/\[(.+?)\]/', 'cb_func1', $val);
    }
}
 
function cb_func1($array)
{
    return strrev($array[1]);
}
 
$hoge = new hoge;
var_dump($hoge->func1('[ABC]-[DEF]-[GHI]'));
 
# 結果
string(11) "CBA-FED-IHG"

しかし、クラス内で定義しているのだから、コールバック関数もクラス内に隠蔽したいはずです。
その場合は、以下のように array($this, 'cb_func1') と配列で定義します。

class hoge
{
    public function func1($val)
    {
        return  preg_replace_callback('/\[(.+?)\]/', array($this, 'cb_func1'), $val);
    }
 
    private function cb_func1($array)
    {
        return strrev($array[1]);
    }
}
 
$hoge = new hoge;
var_dump($hoge->func1('[ABC]-[DEF]-[GHI]'));
 
# 結果
string(11) "CBA-FED-IHG"

クラスのメソッドが、インスタンスメソッドの場合とクラスメソッド(スタティックメソッド)の場合で記述が異なるので注意が必要です。
以下を参照してください。

class hoge
{
    private $val;
    private $arg1;
    private static $arg2;
 
    public function func1($val)
    {
        $this->val  = $val;
        $this->arg1 = 'arg1';
        return  preg_replace_callback('/\[(.+?)\]/', array($this, 'cb_func1'), $this->val);
    }
 
    public static function func2($val)
    {
        self::$arg2 = 'arg2';
        return preg_replace_callback('/\[(.+?)\]/', array(self, 'cb_func2'), $val);
    }
 
    private function cb_func1($array)
    {
        return strrev($array[1]).'@'.$this->arg1;
    }
 
    private static function cb_func2($array)
    {
        return strrev($array[1]).'@'.self::$arg2;
    }
}
 
$hoge = new hoge;
var_dump($hoge->func1('[ABC]-[DEF]-[GHI]'));
var_dump(hoge::func2('[abc]-[def]-[ghi]'));
 
# 結果
string(26) "CBA@arg1-FED@arg1-IHG@arg1"
string(26) "cba@arg2-fed@arg2-ihg@arg2"



programming/php/etc/callback_function.txt