jasny/reflection-factory

PHP Reflection 抽象工厂

v1.1.2 2022-06-10 23:23 UTC

This package is auto-updated.

Last update: 2024-09-11 04:16:16 UTC


README

PHP Scrutinizer Code Quality Code Coverage Packagist Stable Version Packagist License

当使用 PHP Reflection 进行依赖注入时使用的工厂。

为什么使用工厂而不是直接使用 new ReflectionClass()

依赖注入可能看起来使事情变得过于复杂。然而,它是构建可维护(和可测试)代码的关键组件。

在类中使用 new 会创建类之间的强耦合。这使得编写单元测试变得更加困难,因为没有机会模拟反射。在实践中,这意味着使用 ReflectionClass 的类只能用真实存在的类进行测试。

使用 ReflectionFactory 和依赖注入,您可以注入工厂的模拟版本。这进而允许您创建模拟的反射对象,用于非存在的类、函数、属性等。

安装

composer require jasny/reflection-factory

使用方法

use Jasny\ReflectionFactory\ReflectionFactory;

$factory = new ReflectionFactory();
$reflection = $factory->reflectClass(\DateTime::class);

示例用例

不使用依赖注入

use Jasny\ReflectionFactory\ReflectionFactory;

class SomeTool
{
    public function foo(string $class)
    {
        $reflection = new ReflectionClass($class);
        
        return $reflection->getConstant('FOO');
    }
}

但是编写测试很困难,因为它不允许模拟。相反,我们需要创建一个 SomeToolTestFooSupport 类来仅为此功能进行测试。

class SomeToolTestFooSupport
{
    const FOO = 10;
}

在单元测试中,我们进行以下操作

use PHPUnit\Framework\TestCase;

class SomeToolTest extends TestCase
{
    public function testFoo()
    {
        $tool = new SomeTool();
        
        $this->assertEquals(10, $tool->foo("SomeToolTestFooSupport"));
    }
}

添加一个测试类并不那么糟糕。但是考虑我们需要为每个测试添加一个,很快就会变得混乱。

使用依赖注入

依赖注入会给类添加一点开销,因为我们需要将反射工厂传递给 SomeTool

use Jasny\ReflectionFactory\ReflectionFactoryInterface;

class SomeTool
{
    protected $reflectionFactory;
    
    public function __construct(ReflectionFactoryInterface $reflectionFactory)
    {
        $this->reflectionFactory = $reflectionFactory;    
    }
    
    public function foo(string $class)
    {
        return $this->reflectionFactory->reflectClass($class)->getConstant('FOO');
    }
}

在单元测试中,我们模拟 ReflectionClassReflectionFactory。测试类 FakeClass 不需要存在。

use PHPUnit\Framework\TestCase;
use Jasny\ReflectionFactory\ReflectionFactoryInterface;

class SomeToolTest extends TestCase
{
    public function testFoo()
    {
        $mockReflection = $this->createMock(\ReflectionClass::class);
        $mockReflection->expects($this->once())->method('getConstant')
            ->with('FOO')->willReturn(10);
            
        $mockFactory = $this->createMock(ReflectionFactoryInterface::class);
        $mockFactory->expects($this->once())->method('reflectClass')
            ->with('FakeClass')->willReturn($mockReflection);
            
        $tool = new SomeTool($mockFactory);
        
        $this->assertEquals(10, $tool->foo("FakeClass"));
    }
}

方法

一些 PHP 函数已被包装,因此可以进行模拟