geeks4change / composable-inheritance
通过魔法类加载器提供可组合继承。
Requires
- php: >=7
This package is auto-updated.
Last update: 2024-08-29 22:12:16 UTC
README
问题所在
通过装饰来增强服务是一种常见的做法。然而,存在一大类类,虽然容易扩展,但实际上无法进行装饰。例如,具有受保护方法或/和紧密自我耦合(方法大量调用其他方法或使用受保护/私有状态)的类,增强一个方法通常会导致大量复制粘贴其他方法,或者复制内部状态,从根本上违背了代码重用的初衷。
我们可以继承这样的类,但这不是可组合的:在面向组件的架构中,组件A和组件B无法同时继承服务类而不相互紧密耦合。
一个示例用例
假设(例如在DreamHooks中),你想通过重写ModuleHandler::buildImplementationInfo来增强Drupal的ModuleHandler。不管怎样,它总是受保护的。但如果它是公共的,在一个装饰器中,我们不得不复制所有调用方法。
所以,我们可以通过symfony 服务装饰来子类化它并交换实现。但这很脆弱(不可组合):如果另一个模块也这样做,只有一个可以获胜。
解决方案
可组合继承通过在运行时创建扩展类来解决此问题,这样多个扩展就可以在不相互耦合的情况下继承相同的服务类。
如何使用
对于上述示例,我们使用Drupal的ServiceProvider机制来改变容器
use Drupal\dreamhooks\LegacyBridge\ModuleHandlerExtensionTrait';
use Drupal\dreamhooks\LegacyBridge\ModuleHandlerExtensionInterface';
use geeks4change\composable_inheritance\ComposableInheritance;
class DreamhooksServiceProvider extends ServiceProviderBase {
public function alter(ContainerBuilder $container) {
$definition = $container->getDefinition('module_handler');
$class = $definition->getClass();
// Or alternatively use ComposableInheritance::alter($class).
$newClass = ComposableInheritance::create($class)
->useTrait(ModuleHandlerExtensionTrait::class)
->implementsInterface(ModuleHandlerExtensionInterface::class)
->class();
$definition->setClass($newClass);
}
}
然后,魔法类名将在运行时解析为扩展基类(无论其名称如何)的类,并使用特里特\Drupal\dreamhooks\LegacyBridge\ModuleHandlerExtensionTrait
,并且还实现了接口\Drupal\dreamhooks\LegacyBridge\ModuleHandlerExtensionInterface
。
在纯Symfony中,你可以使用合适的编译器通过。
Opcache
如果你想要为动态生成的类提供Opcache支持,设置一个名为COMPOSABLE_INHERITANCE_CLASS_STORAGE_DIRECTORY
的环境变量到一个空目录(在webroot之外,以避免打开上传文件的远程代码执行攻击向量)。
注意事项
当然,如果逻辑不兼容,组合扩展或装饰可能会失败,并且可能可以通过优先级排序来修复。
可组合继承只能在一般意义上使子类扩展组合成为可能。