【2019年07月21日】

PHP5では、通称メソッドチェーンと呼ばれる仕組みが利用できる。
メソッドチェーンとは、メソッドをアロー演算子で複数つなぎ、複数のメソッドを一度に行うこと。

通常はメソッドの戻り値を一旦変数に格納し、その変数からまた次のメソッドを実行する。
理屈的には、メソッドの戻り値がなんらかのクラスインスタンスなら、そのインスタンスの持つメソッドを一旦変数に入れるのではなく、そのまま後ろに続けて記述することで実行が可能ということ。

「メソッドチェーン」というと何か特別な事のように聞こえるが、普通のオブジェクト指向言語なら当たり前の仕組みである。しかしPHP4まではなぜかこれが出来なかった。
なのでPHP5でこれが可能になり、やっと本来のオブジェクト指向に近づいたというところである。


ということでまずは例を見てみよう。

<?php

// 商品クラス
class Product
{
    private $price = 100;

    // 価格取得
    public function getPrice()
    {
        return $this->price;
    }
}

// カートクラス
class Cart
{
    private $products = array();

    // 商品追加
    public function add($product)
    {
        $this->products[] = $product;
    }
    // 商品取得
    public function get($index)
    {
        return $this->products[$index];
    }
}

// カートに商品を追加
$cart = new Cart();
$product = new Product();
$cart->add($product);

// カートの商品の価格を取得(通常)
$prd = $cart->get(0);
$price = $prd->getPrice();

// カートの商品の価格を取得(メソッドチェーン)
$price = $cart->get(0)->getPrice();

?>

カートクラスと商品クラスを使い、カートに商品を入れるという動作を想定する。
通常、カート内の商品の価格を知りたい場合、カートより一旦商品のインスタンスを取得して変数に入れ、その変数より商品のメソッドを実行する。

しかし上の例のように、一旦変数に入れずに連続してメソッドの実行が可能なのである。

この仕組みを利用すると面白いことができる。

<?php

class Date
{
    private $_timestamp;
    
    public function __construct()
    {
        $this->_timestamp = time();
    }
    
    // 日を加算
    public function addDay($days)
    {        
        if (true == preg_match('/^[0-9]+$/', $days)) {
            $str = sprintf('+%s day', $days);
            $this->_timestamp = strtotime($str, $this->_timestamp);
        }
    }

    // 指定の書式の日付文字列を取得
    public function format($format)
    {
        return date($format, $this->_timestamp);
    }    
}

?>

Dateと称して日付処理クラスを作ってみた。
コンストラクタで現在のタイムスタンプをフィールドに保持。
addDayメソッドでタイムスタンプに指定の日数分、加算。
formatメソッドでタイムスタンプを指定の書式の文字列に変換して返す。

ちゃんとした日付処理クラスを作るならもっともっとたくさんの機能が必要だが、今回は実験用ということで簡易クラス。

これを使って処理をやってみるとこんな感じ。

<?php

$date = new Date();
$date->addDay(3);
$fmt = $date->format('Y/m/d H:i');
var_dump($fmt);

?>
string(16) "2010/01/16 22:33" 

日付のインスタンスを生成し、addDayメソッドで日付を3日加算し、指定の形式で出力。

一つ一つのメソッドの戻り値を一旦変数に入れている。
ウザいのでこれを1行で全てやってしまいたい!と思ったときに、それを可能にするのがメソッドチェーンである。

つまり、

$date->addDay(3)->format('Y/m/d H:i');

みたいな感じ。
これを実現するにはaddDayメソッドの戻り値として、インスタンス自身を返す。

<?php

    public function addDay($days)
    {        
        if (true == preg_match('^[0-9]+$/', $days)) {
            $str = sprintf('+%s day', $days);
            $this->_timestamp = strtotime($str, $this->_timestamp);
        }
        return $this;
    }

?>
<?php

$date = new Date();
$fmt = $date->addDay(3)->format('Y/m/d H:i');
var_dump($fmt);

?>
string(16) "2010/01/16 22:47" 

更にインスタンス生成も、直接newするのではなくstaticメソッドを通して行う事で、こんな事も出来る。

<?php

    public static function now()
    {
        return new Date();
    }

?>
<?php

$fmt = Date::now()->addDay(3)->format('Y/m/d H:i');
var_dump($fmt);

?>
string(16) "2010/01/16 22:54" 

見事に1行。

ポイントは、Sメソッドの戻り値に自身のインスタンスを返す事。

インスタンス自身の状態を変化させる系のメソッドの場合、通常は戻り値なしって事が多いので、とりあえず自分自身を返しておく。そうするとメソッドチェーンを利用して、状態を変化させた結果をそのまま出力、というように他のメソッドを実行するなんて事が出来るわけである。