knj/revelation

使非公开方法可访问的最简单方法

1.1.0 2019-01-07 10:13 UTC

This package is auto-updated.

Last update: 2024-09-05 17:37:24 UTC


README

Build Status codecov

当使用PHPUnit等测试工具测试受保护或私有方法时,测试非常困难。使用Revelation,您可以通过使用简单函数来访问所有方法和属性。

支持的版本

  • PHP 7.1或更高版本

安装

使用Composer

composer require --dev knj/revelation

快速开始

尝试以下代码(带自动加载)

<?php

use function Wazly\Revelation\reveal;

$obj = new class {
    private function do() {
        return 'You called private method successfully!';
    }
};

echo reveal($obj)->do();

使用方法

Revelation对象

创建一个Revelation对象,以便您可以访问非公开方法和属性。

<?php

class Stuff
{
    private $privateProperty;

    public function __construct($a, $b)
    {
        $this->privateProperty = $a + $b;
    }

    private function privateMethod($x, $y)
    {
        $this->privateProperty = $x + $y;

        return $this;
    }
}

使用Stuff对象创建它,请使用reveal()辅助函数。

<?php

use function Wazly\Revelation\reveal;

$stuff = new Stuff(1, 2);
$stuff = reveal($stuff);       // now $stuff is a Revelation object
echo $stuff->privateProperty;  // 3
$stuff->privateMethod(1, 100);
echo $stuff->privateProperty;  // 101

创建Revelation对象的方法

// use function Wazly\Revelation\reveal;
reveal($stuff);
reveal(Stuff::class, 1, 2);
reveal(function ($a, $b) { return new Stuff($a, $b); }, 1, 2);

// use Wazly\Revelation;
Revelation::wrap($stuff);
Revelation::wrap(Stuff::class, 1, 2);
Revelation::wrap(function ($a, $b) { return new Stuff($a, $b); }, 1, 2);

获取原始对象

getOriginal()检索对原始对象的引用。

$stuff = reveal($stuff);
echo get_class($stuff);                // Wazly\Revelation
echo get_class($stuff->getOriginal()); // Stuff

原始对象的引用

使用相同对象创建的Revelation对象具有对原始对象的相同引用。

$rev1 = reveal($stuff);
$rev1->privateMethod(1, 2);

$rev2 = reveal($stuff);
$rev2->privateMethod(3, 4);

echo $rev1->privateProperty; // 7 not 3
echo $rev2->privateProperty; // 7

如果您想不同的Revelation对象不具有相同的引用,请使用Revelation::clone()

$rev1 = reveal($stuff);
$rev1->privateMethod(1, 2);

$rev2 = Revelation::clone($stuff);
$rev2->privateMethod(3, 4);

echo $rev1->privateProperty; // 3
echo $rev2->privateProperty; // 7

方法链

return $this从不返回原始对象本身,这样Revelation对象就可以链式调用方法。

reveal($stuff)->privateMethod(1, 2)->privateMethod(3, 4);

静态方法和属性

getStatic()callStatic()可用。

class A
{
    protected static $staticProperty = 'static';

    protected static function className()
    {
        return __CLASS__;
    }

    protected static function selfName()
    {
        return self::className();
    }

    protected static function staticName()
    {
        return static::className();
    }
}

class B extends A
{
    protected static function className()
    {
        return __CLASS__;
    }
}

echo reveal(B::class)->getStatic('staticProperty'); // static
echo reveal(B::class)->callStatic('className');     // B
echo reveal(B::class)->callStatic('selfName');      // A
echo reveal(B::class)->callStatic('staticName');    // B

通过引用传递变量

从Revelation对象调用的方法无法接收变量引用,因为__call()无法这样做。这个问题可以通过自己创建闭包来解决。

class X
{
    private function callPassByReferenceMethod($val, &$ref)
    {
        static::passByReference($val, $ref);
    }

    private static function passByReference($val, &$ref)
    {
        $ref = $ref ?? 1;
        $ref += $val;
    }
}

$closure1 = reveal(X::class)->bind(function ($val, &$ref) {
    $this->callPassByReferenceMethod($val, $ref);
});

$closure2 = reveal(X::class)->bind(function ($val, &$ref) {
    static::passByReference($val, $ref);
});

$closure1(99, $ref);
echo $ref; // 100
$closure2(100, $ref);
echo $ref; // 200