aimeos/macro

使用闭包自定义类

1.0.1 2022-01-22 11:32 UTC

This package is auto-updated.

Last update: 2024-09-18 09:47:47 UTC


README

PHP宏包通过提供一个可以包含到任何类中的特性,提供了基于闭包的setter依赖注入。

composer req aimeos/macro

此包面向那些希望允许用户自定义其代码行为的应用程序、框架和库的开发者。

为什么使用宏

在为定制而构建的应用程序、框架或库中,允许覆盖现有功能以自定义其行为是必要的。这正是宏非常有用的地方,因为它们可以使用闭包添加自定义代码。

使用PHP宏包,您还可以允许用户在不强制用户扩展这些类的情况下覆盖基类中的方法。PHP宏包使用的是**纯PHP方法**,没有使用反射或其他技巧。

与基于类的依赖注入相比,宏有优点和缺点

优点

  • 编写代码更少,实现简单功能更简单
  • 自定义闭包可以像类方法一样继承和重写

缺点

  • 静态代码分析的可能性有限
  • 匿名函数不能强制实现接口

因此,它不是基于类的依赖注入的替代品,而是在需要使用实现接口的类的完整依赖注入时工作量太大的一些小扩展点上的轻量级补充。

允许定制

如果原始方法检查是否存在宏并使用该宏代替其自己的实现,则可以修改现有方法的输出结果

// original code

class Order
{
    use Aimeos\Macro\Macroable;

    private $id = '123';

    public function getOrderNumber()
    {
        $fcn = static::macro( 'orderNumber' );
        return $fcn ? $fcn( $this->id ) : $this->id;
    }
};

现在,您可以添加自己的自定义orderNumber宏,它将被使用

// user code

Order::macro( 'orderNumber', function( string $id ) {
   return date( 'Y' ) . '-' . $id;
} );

(new Order)->getOrderNumber(); // now returns '2020-123'

因此,您可以生成自己的输出或将不同的结果传递给应用程序中的后续方法。

访问类属性

当宏在对象上下文中调用时,它们也可以访问类属性

// original code

class A
{
    use Aimeos\Macro\Macroable;
    private $name = 'A';
};

在这里,私有属性$name在宏中可用

// user code

A::macro( 'concat', function( array $values ) {
   return $this->name . ':' . implode( '-', $values );
} );

(new A)->concat( ['1', '2', '3'] ); // returns 'A:1-2-3'

宏可以使用该属性作为创建返回值的输入。

使用继承的宏

PHP宏包还允许从父类继承宏。然后,它们可以像常规类方法一样访问子类的类属性

// original code

class A
{
    use Aimeos\Macro\Macroable;
    private $name = 'A';
};

class B extends A
{
    private $name = 'B';
};

添加到父类的宏在子类中也是可用的

// user code

A::macro( 'concat', function( array $values ) {
   return $this->name . ':' . implode( '-', $values );
} );

(new B)->concat( ['1', '2', '3'] ); // returns 'B:1-2-3'

B从类A扩展,但提供了不同的$name属性。现在,从类A继承的宏将使用类B的属性。

覆盖继承的宏

也可以像常规类方法一样覆盖从父类继承的宏

// original code

class A
{
    use Aimeos\Macro\Macroable;

    public function do() {
        return static::macro( 'concat' )( [1, 2, 3] );
    }
};

class B extends A {};

class C extends A {};

现在您可以为父类和其中一个子类添加宏

// user code

A::macro( 'concat', function( array $values ) {
   return implode( ',', $values );
} );

C::macro( 'concat', function( array $values ) {
   return implode( '-', $values );
} );

(new B)->do(); // returns '1,2,3'

(new C)->do(); // returns '1-2-3'

这使得您可以为单个类添加特殊处理,即使所有其他类仍然使用添加到类A的宏。

覆盖受保护的方法

基类通常提供一组方法,这些方法由子类使用。在PHP中,替换基类的方法是不可能的,因此您必须用您自己的实现覆盖每个子类。

为了避免这种情况,原始方法可以使用call()方法而不是直接调用父类的方法

// original code

class A
{
    use Aimeos\Macro\Macroable;

    protected function getName( $prefix )
    {
        return $prefix . 'A';
    }
};

class B extends A
{
    public function do()
    {
        return $this->call( 'getName', 'B-' );
    }
};

这将检查是否存在名为getName的宏,并将调用该宏而不是调用getName()方法

// user code

(new B)->do(); // returns 'B-A'

A::macro( 'getName', function( $prefix ) {
   return $this->getName( $prefix ) . '-123';
} );

(new B)->do(); // returns 'B-A-123'

原始的getName()方法仍然可以在宏中使用。

重置宏

有时,可能需要从对象中删除宏,尤其是在运行自动化测试时。您可以通过以下方式取消宏设置:

class A
{
    use Aimeos\Macro\Macroable;
};

// add macro
A::macro( 'test', function() {
   return 'test';
} );

// remove macro
A::unmacro( 'test' );