はじめに
cURLの非同期処理をJavaScriptみたいに器用にやってくれるやつ書いてたんですが,privateフィールドが多くてテストごとにリフレクション書くのが面倒くさい!試行錯誤しているうちに,PHP7からの新機能である無名クラスを活用する方法をひらめきました.
簡易実装
private static
なメソッドをテストする例です.
<?php
namespace mpyw\StupidExample;
class Example {
private static function hello()
{
return 'Hello';
}
}
<?php
require_once 'Example.php';
use mpyw\StupidExample\Example;
$Example = new Çlass { // ハイライトが未対応なので全角文字で小細工
private $rc;
public static function __callStatic(string $name, array $args)
{
$rm = new \ReflectionMethod(Example::class, $method);
$rm->setAccessible(true);
return $rm->invokeArgs(null, $args);
}
};
assert($Example::hello() === 'Hello');
無名クラスが生きるのはやはりマジックメソッドの活用ですね…!
応用
private static
なメソッドのテストの例を示しましたが,いろんなものに対応させたものをライブラリとして公開しました.
使い方
<?php
require_once 'Example.php';
use mpyw\Privator\Proxy;
use mpyw\StupidExample\Example;
class ExampleTest extends \Codeception\TestCase\Test
{
public function _before()
{
$this->Example = Proxy::get(Example::class);
}
...
}
全てのテストメソッドの前に実行される _before
にこの1行を書くだけです!後は何でもやりたい放題!コンストラクタがprivate
でもOK!
// 静的メソッドのコール
$this->Example::staticMethod();
// コンストラクタ無しのインスタンスの生成
$example = $this->Example::newWithoutConstructor();
// コンストラクタ有りのインスタンスの生成
$example = $this->Example::new();
// インスタンスメソッドのコール
$example->instanceMethod();
// インスタンスプロパティへの代入
$example->instanceProperty = 'value';
// インスタンスプロパティの取得
$value = $example->instanceProperty;
ただ,妥協しなければいけないところも…
// 静的プロパティへの代入
$this->Example::setStatic('staticProperty', 'value');
// 静的プロパティの取得
$value = $this->Example::getStatic('staticProperty');
静的プロパティはマジックメソッドが無いのでどうしようもありませんでした.まあこれでも十分便利だよね,うん!
追記
よく考えたら同等の機能は ClassProxy
InstanceProxy
としてクラスを定義しておいて全部 __call()
__get()
__set()
で解決すればどうにでもなりますね…
$class = new ClassProxy(Example::class);
$class->staticMethod();
$instance = $class->getNew(); // new InstanceProxy(Example::class, []);
$instance->instanceMethod();
__callStatic()
を使うことのメリットは,静的メソッドに対して::
演算子でのアクセスができるので,より本来の形に近いように見えることだと思います.これを実現するにあたって,PHP7であれば複数のクラスの面倒を同時に見ることができます.
$A = Proxy::get(A::class);
$B = Proxy::get(B::class);
$A::staticMethodOfA();
$B::staticMethodOfB();
もし複数クラスを対象としてPHP5でやろうとすると __call()
での妥協が必要になります…
(何かあんまり意味のないエントリになってしまって申し訳ない)