48
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHP7から private や private static のテストがクソ簡単に書けるぞ!

Last updated at Posted at 2016-02-29

はじめに

cURLの非同期処理をJavaScriptみたいに器用にやってくれるやつ書いてたんですが,privateフィールドが多くてテストごとにリフレクション書くのが面倒くさい!試行錯誤しているうちに,PHP7からの新機能である無名クラスを活用する方法をひらめきました.

簡易実装

private static なメソッドをテストする例です.

Example.php
<?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 なメソッドのテストの例を示しましたが,いろんなものに対応させたものをライブラリとして公開しました.

使い方

Codeceptionでの例
<?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() での妥協が必要になります…

(何かあんまり意味のないエントリになってしまって申し訳ない)

48
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
48
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?