【PHP】コールバック関数の使い方・サンプル(レポート出力するデータを後から差し替える)
-
カテゴリ:
- PHP
PHP勉強時、コールバック関数を使ったのでここにメモしておきます。
コールバック関数をうまく使えるといろいろと幸せになれると思います。
コールバック関数とは
コールバック関数は、使う側が後から関数を差し替えるための仮の関数だと理解してます。
可変関数とも呼ばれており関数を変数のように扱えるため、効率的な処理を書くには不可欠なものです。
PHPでコールバック関数を使う方法は大きく分けて2つあります。
一つは文字列から関数を呼び出す方法です。サンプルを作りました。
<?php
// コールバック関数
function hello(){
echo "おはよう、太郎さん";
}
// コールバック関数を呼び出す関数
function callback($method){
// 引数として渡された文字列をもとにコールバック関数を読み出す
echo $method();
}
// 実行
callback("hello");
// 結果:
// おはよう、太郎さん
ただし上記の方法だと、コールバック関数内で引数を渡すことができないので不便です。引数を渡したいときは次の方法で実装しましょう。
2つ目は、「call_user_func()」や「call_user_func_array()」を使う方法です。
使い方としては以下の通りです。
// 文法
call_user_func ( コールバック関数名, 引数 );
// 例
function hello($val){
echo "おはよう、{$val}さん";
}
call_user_func ( 'hello', '太郎' );
// 結果:おはよう、太郎さん
また、コールバック関数に渡す引数が配列の場合は「call_user_func_array()」を使います
// 文法
call_user_func_array ( コールバック関数名, パラメータの配列 );
// 例
function hello($list){
foreach ($list as $val) {
echo "おはよう、{$val}さん\r\n";
}
}
call_user_func_array ( 'hello', array('太郎', '花子') );
// 結果:
// おはよう、太郎さん
// おはよう、花子さん
さらにさらに、第一引数を配列にすることで、クラスの中の特定の関数を実行することも可能です。
// 文法
call_user_func_array ( [クラスオブジェクト、関数名], パラメータの配列 );
// 例
Class Hello {
function call($list){
foreach ($list as $val) {
echo "おはよう、{$val}さん\r\n";
}
}
}
$hello = new Hello();
call_user_func_array ( array($hello, 'call'), array('太郎', '花子') );
// 結果:
// おはよう、太郎さん
// おはよう、花子さん
すごい便利!!
コールバック関数のサンプル
コールバック関数を利用したサンプルとして、レポート出力をする際データ元を取得する処理をコールバック関数にして、後から使う側がレポートに出力したいデータを自由に変更できるようにしました。
<?php
// コールバック関数を持ったクラスオブジェクト
Class Report {
private $callback;
private $params;
/**
* コンストラクタ
*
*/
private function __construct($method, $dao=NULL, $params=NULL) {
if(empty($method)) {
// エラー処理
}
if(empty($dao)) {
$this->callback = $method;
} else {
$this->callback = array($dao, $method);
}
$this->params = $params;
}
/**
* レポート出力
*/
public function export($file_path) {
if(empty($file_path)) {
// エラー処理
}
try {
/************
* データを取得(※コールバック関数を使って配列データを取得する)
************/
if(empty($this->params)) {
$list_data = call_user_func($this->callback);
} else {
$list_data = call_user_func_array($this->callback, $this->params);
}
// レポート出力
if(!is_null($list_data) ) {
$fp = fopen($file_path, 'w');
//foreachでライン毎にfputcsvでCSV出力
foreach($list_data as $line){
fputcsv($fp, $line);
}
fclose($fp);
}
} catch (Exception $ex) {
// エラー処理
}
}
}
38~42行目にコールバック関数の「call_user_func()」と「call_user_func_array()」を使っています。
レポートに出力するデータを取得する処理自体をコールバック関数にすれば、様々な種類のデータをレポートに出力することが可能です。
例えば出力するデータをデータベースから取得する際は以下のような感じになると思います。検索条件にはプレースホルダもなければ、等号条件しか使えないですが、そこは目をつむってくださいW。
Class SqlDao {
private $sql;
private $params;
public createSql($params) {
$sql = <<<EOT
SELECT
name
, age
, blood_type
FROM
table
WHERE
delete_flag = 0
EOT;
$where = '';
foreach ($params as $key => $val) {
$where .= " AND {$key} = {$val} ";
}
return $sql . $where;
}
public search($params)
$pdo = new PDO('mysql:host=localhost;dbname=db;charset=utf8', 'username', 'password');
// SQL文を作成
$sql = createSql($params);
// SQLを実行
$result = $pdo->query($sql);
return $result
}
}
// 使う側
$dao = new SqlDao();
$params = array('name'=>'太郎');
$report = new Report('search', $dao, $params);
$report->export('./output.csv');
他にもデータセットをCSVから読み込むような作りにすると、以下の通りになります。
Class CsvDao {
private $sql;
private $params;
public load($file_path){
$csv_data = array();
$data = file($file_path);
foreach ($data as $line_num => $line) {
array_push($csv_data ,$line);
}
return $csv_data;
}
}
// 使う側
$dao = new CsvDao();
$params = array('./sample.csv');
$report = new Report('load', $dao, $params);
$report->export('./output.csv');
一時的なデータセットを使いたい場合はクロージャ(無名関数)を使うことも可能です。
// 無名関数
$method = funciton(){
$list = array();
$list[0] = '名前,年齢,血液型';
$list[1] = '太郎,30,A';
$list[2] = '花子,10,B';
$list[3] = '次郎,25,AB';
return $list;
}
// 使う側
$report = new Report($method);
$report->export('./output.csv');
駆け足でソースを書いてきましたが、結局何が言いたかというと、使う側からしてデータセットを取得するクラス($dao)が配列を返す仕様であれば、Reportクラスのレポート出力処理はほとんど同じ書き方で行けます(引数の数の違いはあるが、、、)。
3つの例ではほとんどが、
$report = new Report(メソッド, クラス, パラメータ);
$report->export(出力先);
上記の書き方になっているかと思います。違いはデータセット($dao)の中身が違うだけです。
まとめ
コールバック関数てちょっと取っ付きにくいところがありますよね。自分の記事を書いていて少し混乱してきましたw。
ただ上記のように、あらかじめ使い回せるようにコールバック関数を構えて作っておくと、後々いろんな処理に流用できるので効率的にプログラミングができるかと思います。