🛠️
PHPでもパイプで処理したい
bashやelixirにはパイプがある
phpでもそうしたい
bash
例えばbash
こういうcsvがあったとして
cat sample.csv
title,value,impression,release_date
Rustプログラミング入門,4000,微妙,2021
詳解Rustプログラミング,3600,良い,2021
並行プログラミング入門,3200,かなり良い、難しい,2021
プログラミングRust第二版,4800,かなり良い,2021
valueが4000以下の二つのデータの合計値に10%加算したものを算出しようと思うと
one line
sed 1d sample.csv|awk -F ',' '$2<4000'|awk -F ',' '{print $0}'|awk -F ',' '{print $2*1.1}'|xargs |sed 's/\ /+/g'|bc
7480
のようなコマンドを打つだけでOK
※awkを二回打っているのはこの後のコードに寄せる為なので気にしないように
php
しかしphpにパイプはない
そこでパイプっぽく書けるようにしてみる
パイプ用のクラスを作る
pipe.php
<?php
class Pipe{
private function __construct(private $resource){
}
public static function resource($resource){
return new self($resource);
}
public function then($fn) {
return new self( $fn($this->resource));
}
public function __call($name, $arguments){
return new self (call_user_func($name, $this->resource, ...$arguments));
}
public function fin(){
return $this->resource;
}
}
__call はオマケで用意したがマジックメソッドなので無い方が良い、しかし今回は例という事で用意した
例1
example01.php
$source = 'abcdefgABCDEFG';
$calc = Pipe::resource($source)
->then(fn($resource)=>substr($resource, 6))
->then(fn($resource) => substr($resource,1))
->then(function($resource){
return strtolower($resource);
})->strtoupper()
->substr(0,2)
->fin();
var_dump($calc);
string(2) "AB"
処理内容は
- パイプのソースとして
abcdefgABCDEFG
を渡す - substr6番目以降の文字を切り出す
- substrで1番目以降の文字を切り出す
- strtolowerで全部小文字にする
- strtoupperで全部大文字にする
- substrで0番目から2場面の文字を切り出す
- 結果を取得する
__callを実装しているから->substr(0,2)
のような書き方も出来るけど、全てthen
で書く方が良いだろう
例2
bashの例で使ったcsvに対する処理の例
example02.php
$csv = new SplFileObject("sample.csv");
$csv->setFlags(SplFileObject::READ_CSV|SplFileObject::SKIP_EMPTY|SplFileObject::DROP_NEW_LINE);
$result = Pipe::resource($csv)
->then(function($spl){
//arrayにする
$ret=[];
foreach($spl as $row){
$ret[] = $row;
}
return $ret;
})->then(function($v){
//headerの除去
array_shift($v);
return $v;
})->array_filter(fn($v)=>$v[1]<4000) //4000円以下
->array_values()//indexの降り直し
->then(fn($v)=>array_map(fn($row)=>$row[1]*1.1, $v))//怒りの10%追加
->array_sum()//金額の合計
->fin();
echo $result;
7480
※こっちはソース中に処理の説明を書きました
__call が使えない理由のひとつは、組み込み関数array_map
とarray_filter
の引数の順番の違いのようなものがあるから
全ての関数の一番目の引数はリソースとなる値が来るようにして欲しいし、そうじゃない関数はなんでそうしなかったんだろうかと不思議で仕方がない
あとがき
例のthen
の書き方だけで良いから全php界で使われると良いなと思う
処理の意図を追いやすいし、技術力や経験に関わらずスコープを意識させずにスコープを意識したコードを強制出来るから
Discussion