PHPではstreamWrapperを使って特定のプロトコルに対するファイル入出力をフックすることができます。
これを使ってcurl関数をモックしたものがphp-vcrで技術的な詳細は以下の記事に書いてあります。
任意の関数をモックしてみる
今回はこのstreamWrapperを使ってmail関数をモックしてみようと思います。
streamWrapperは特定の関数を持つクラスになります。インターフェースをimplementsとかではなくダックタイプ的な感じで関数を定義していきます。
<?php
class MailMockStreamWrapper
{
private $content;
private $position;
private function register()
{
stream_wrapper_unregister("file");
stream_wrapper_register("file", __CLASS__);
}
public function stream_open($path)
{
stream_wrapper_restore("file");
$content = file_get_contents($path);
$this->content = str_replace("mail", "MailMock::mail", $content);
$this->register();
return true;
}
public function stream_read($count)
{
$ret = substr($this->content, $this->position, $count);
$this->position += strlen($ret);
return $ret;
}
public function stream_stat()
{
return [];
}
public function stream_eof()
{
return $this->position >= strlen($this->content);
}
}
stream_open
でファイルをすべて読みこんでstream_read
で文字列の変数から中身を取り出す、という感じにしています。
(一般的にはopenではファイルをオープンするだけでreadのタイミングで読み込むのが良いと思います)
ファイルを読み込む直前でstream_wrapper_restore
しておかないとfile_get_contents
で再度stream_openが呼ばれ、無限再帰するため最初にrestoreで止めておく必要があります。
あとはモックするクラス・メソッドを適当に定義してstream_wrapper_unregister, registerします。
class MailMock
{
public static function mail($to, $subject)
{
echo "to => $to, subject => $subject";
}
}
stream_wrapper_unregister("file");
stream_wrapper_register("file", "MailMockStreamWrapper");
この状態で適当なコードをrequireすると、requireしたファイルのmail関数がMailMock::mail
に置換されて実行されます。
ただ、このコードだと file_get_contents
などの単純にファイルの中身を読み込む処理に関してもモックされてしまうので、実践投入するにはファイルパス等のチェックを入れる必要があります。