thecodingmachine /
funky
使用注解轻松编写服务提供者
Requires
- php: >=7.1
- container-interop/service-provider: ^0.4
- doctrine/annotations: ^1.5.0
- mindplay/composer-locator: ^2.1.3
- thecodingmachine/common-factories: ^0.4
Requires (Dev)
- bamarni/composer-bin-plugin: ^1.1
- mnapoli/simplex: ^0.4.1
- phpstan/phpstan: ^0.9
- phpunit/phpunit: ^6.4.3
- satooshi/php-coveralls: ^1.0.1
- thecodingmachine/phpstan-strict-rules: ^0.9
This package is auto-updated.
Last update: 2024-09-06 18:47:50 UTC
README
工作正在进行中,尚未发布稳定版本
thecodingmachine/funky
Funky 是一个工具,可以帮助您编写与 container-interop/service-provider 兼容的服务提供者。
没有 Funky
当前的流行趋势是在编写服务提供者时直接实现 ServiceProviderInterface
。
例如
class WhoopsMiddlewareServiceProvider implements ServiceProviderInterface { public function getFactories() { return [ Middleware::class => [self::class,'createMiddleware'], ]; } public function getExtensions() { return [ MiddlewareListServiceProvider::MIDDLEWARES_QUEUE => [self::class,'updatePriorityQueue'] ] } public static function createMiddleware() : WhoopsMiddleware { return new WhoopsMiddleware(); } public static function updatePriorityQueue(ContainerInterface $container, \SplPriorityQueue $queue) : \SplPriorityQueue { $queue->insert($container->get(Middleware::class), MiddlewareOrder::EXCEPTION_EARLY); return $queue; } }
使用 Funky
class WhoopsMiddlewareServiceProvider extends Funky\ServiceProvider { /** * @Factory( * tags={@Tag(name="middlewares.queue", priority=MiddlewareOrder::EXCEPTION_EARLY)} * ) */ public static function createMiddleware() : WhoopsMiddleware { return new WhoopsMiddleware(); } }
Funky 实现了 getFactories
和 getExtensions
方法。
您的类只需扩展 TheCodingMachine\Funky\ServiceProvider
。Funky 将扫描您的类以查找 @Factory
和 @Extension
注解。
安装
只需从您的服务提供者包中 require thecodingmachine/funky
。
$ composer require thecodingmachine/funky
用法
您可以通过扩展 TheCodingMachine\Funky\ServiceProvider
类,而不是创建一个实现 Interop\Container\ServiceProviderInterface
的类。
@Factory 注解
默认命名
默认情况下,@Factory
使用返回类型作为容器条目的名称
/** * @Factory */ public static function createMiddleware() : WhoopsMiddleware { return new WhoopsMiddleware(); }
$container->get(WhoopsMiddleware::class) // will return the service
指定名称
您可以使用 @Factory
注解的 "name" 属性来指定名称
/** * @Factory(name="whoops") */ public static function createMiddleware() : WhoopsMiddleware { return new WhoopsMiddleware(); }
$container->get('whoops') // will return the service
使用方法名
您可以使用 @Factory
注解的 "nameFromMethodName" 属性来告诉 Funky 使用方法名作为标识符
/** * @Factory(nameFromMethodName=true) */ public static function whoopsMiddleware() : WhoopsMiddleware { return new WhoopsMiddleware(); }
$container->get('whoopsMiddleware') // will return the service
自动装配
Funky 支持自动装配!您只需在工厂中添加适当的类型提示参数,Funky 将在容器中查找这些依赖项。
/** * @Factory() */ public static function myService(LoggerInterface $logger) : MyService { // $logger is fetched from the container using "$container->get(LoggerInterface::class)" return new MyService($logger); }
如果您没有添加类型提示(或使用标量类型提示),Funky 将尝试使用参数名称获取依赖项。
/** * @Factory() */ public static function myService(string $ROOT_PATH) : MyService { // $ROOT_PATH is fetched from the container using "$container->get('ROOT_PATH')" return new MyService(string $ROOT_PATH); }
最后,您还可以在任何时候注入整个容器以从其中获取任何依赖项
/** * @Factory() */ public static function myService(ContainerInterface $container) : MyService { return new MyService($container->get('ROOT_PATH')); }
扩展条目
您可以使用 Extension
注解从容器中扩展条目。
您应该将要扩展的条目作为第一个参数传递。其余参数可以像处理工厂一样自动装配。
/** * @Extension() */ public static function registerTwigExtension(\Twig_Environment $twig, MyTwigExtension $extension) : \Twig_Environment { $twig->register($extension); return $twig; }
指定名称
您可以使用 @Extension
注解的 "name" 或 "nameFromMethodName" 属性来指定要扩展的条目的名称
@Extension(name="twig") // The extended entry is named "twig"
/** * The extended entry is named twig because the method name is "twig" * @Extension(name="twig") */ public static function registerExtension(\Twig_Environment $twig, MyTwigExtension $extension) : \Twig_Environment { // ... }
/** * The extended entry is named "twig" because the method name is "twig" * @Extension(nameFromMethodName=true) */ public static function twig(\Twig_Environment $twig, MyTwigExtension $extension) : \Twig_Environment { // ... }
标签
开箱即用的 container-interop/service-provider 没有标签的概念。但是,您可以在容器中构建实际上是一组服务的条目。这些数组可以被视为 "标签"。
Funky 提供了一种简单的方法来标记服务,使用 @Tag
注解。这是一种移除大量样板代码的好方法!
以下是一个示例
/** * @Factory( * tags={@Tag(name="twigExtensions")} * ) */ public static function myTwigExtension(\Twig_Environment $twig, MyTwigExtension $extension) : \MyTwigExtension { // ... }
此代码段声明了一个 \MyTwigExtension
条目,并将其添加到 twigExtensions
标签中。
之后,您可以轻松地从容器中获取标记的服务。
例如
/** * Here, the tag "twigExtensions" used in the function above is injected using auto-wiring. * @Factory() */ public static function twig(iterable $twigExtensions) : \Twig_Environment { // ... $twig = new Twig_Environement(...); foreach ($twigExtensions as $twigExtension) { $twig->register($twigExtension); } }
您可以为每个标记服务指定一个可选的优先级级别
/** * @Factory( * tags={@Tag(name="my.tag", priority=42.1)} * ) */
优先级较低的条目将首先显示。优先级较高的条目将最后显示。
注意:在底层,标记的服务实际上是 \SplPriorityQueue
对象。这些是可迭代的,但不是 PHP 数组。如果需要数组,可以使用 iterator_to_array
PHP 函数将它们转换为数组。
常见问题解答
为什么这个名字?
因为PHP生态系统热爱音乐(你注意到了吗?Composer, Symfony, ...)。并且因为Funky的根源和灵感来源于bitexpert/disco,一个遵循PSR-11规范的容器,它也依赖于注解!
为什么工厂需要是静态的?
在服务提供者的上下文中,工厂是一个基于传入参数构建服务的函数。如果你做过一些函数式编程,你应该认为工厂是一个纯函数。
给定一组参数,它总是会生成相同的结果。因此,工厂不需要任何对象状态(状态包含在传入的容器中)。
此外,编译过的容器(如Symfony或PHP-DI)可以利用工厂是公共静态的事实来极大地优化它们与服务提供者交互的方式。通过缓存getFactories
和getExtensions
方法返回的结果,编译过的容器可以将使用Funky的开销几乎降到0。
故障排除
提示某些文件无法创建
Funky需要生成一些PHP文件以实现快速。这些文件将被写入Funky的'generated'目录(所以大多数情况下,在vendor/thecodingmachine/funky/generated
)。如果Funky从Apache调用,Apache可能没有权限在此目录中写入文件。你必须更改此目录的权限,以允许Apache写入。
某些xxxHelper类无法自动加载
如上所述,Funky需要生成一些PHP文件以实现快速。这些类被写入Funky的'generated'目录。如果你使用了Composer的权威类映射(例如使用--classmap-authoritative
选项),Composer将扫描你的项目中的所有类来构建类映射。问题:Funky的类尚未写入!因此,当使用Funky时,你不应该使用--classmap-authoritative
选项。