thecodingmachine/funky 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

thecodingmachine/funky

Composer 安装命令:

composer require thecodingmachine/funky

包简介

Write service providers easily using annotations

README 文档

README

Latest Stable Version Total Downloads Latest Unstable Version License Scrutinizer Code Quality Build Status Coverage Status

Work in progress, no stable version yet

thecodingmachine/funky

Funky is tool to help you write service providers compatible with container-interop/service-provider.

Without Funky:

The current trend is to directly implement the ServiceProviderInterface when writing service providers.

For instance:

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;
    }
}

With 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 implements the getFactories and getExtensions methods.

Your class simply extends TheCodingMachine\Funky\ServiceProvider. Funky will scan your class for @Factory and @Extension annotations.

Install

Simply require thecodingmachine/funky from your service provider package.

$ composer require thecodingmachine/funky

Usage

Instead of creating a class that implements Interop\Container\ServiceProviderInterface, you extend the TheCodingMachine\Funky\ServiceProvider class.

The @Factory annotation

Default naming

By default, the @Factory is using the return type as the name of the container entry

/**
 * @Factory
 */
public static function createMiddleware() : WhoopsMiddleware
{
    return new WhoopsMiddleware();
}
$container->get(WhoopsMiddleware::class) // will return the service

Specifying a name

You can use the "name" attribute of the @Factory annotation to specify a name:

/**
 * @Factory(name="whoops")
 */
public static function createMiddleware() : WhoopsMiddleware
{
    return new WhoopsMiddleware();
}
$container->get('whoops') // will return the service

Using the method name

You can use the "nameFromMethodName" attribute of the @Factory annotation to tell Funky to use the method name as an identifier:

/**
 * @Factory(nameFromMethodName=true)
 */
public static function whoopsMiddleware() : WhoopsMiddleware
{
    return new WhoopsMiddleware();
}
$container->get('whoopsMiddleware') // will return the service

Auto-wiring

Funky supports auto-wiring! You can simply add parameters with the appropriate type-hint in your factories and Funky will look for this dependencies in the container.

/**
 * @Factory()
 */
public static function myService(LoggerInterface $logger) : MyService
{
    // $logger is fetched from the container using "$container->get(LoggerInterface::class)"
    return new MyService($logger);
}

If you do not add type-hints (or use a scalar type-hint), Funky will try to fetch the dependency using the parameter name.

/**
 * @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);
}

Finally, at any point, you can also inject the whole container to fetch any dependency from it:

/**
 * @Factory()
 */
public static function myService(ContainerInterface $container) : MyService
{
    return new MyService($container->get('ROOT_PATH'));
}

Extending entries

You can extend entries from the container using the Extension annotation.

You should pass the entry to be extended as the first argument. The remaining arguments can be auto-wired just like you do with the factories.

/**
 * @Extension()
 */
public static function registerTwigExtension(\Twig_Environment $twig, MyTwigExtension $extension) : \Twig_Environment
{
    $twig->register($extension);
    return $twig;
}

Specifying a name

You can use the "name" or "nameFromMethodName" attribute of the @Extension annotation to specify the name of the entry to be extended:

@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
{
    // ...
}

Tags

Out of the box, the container-interop/service-provider does not have a notion of tags. However, you can build entries in your container that are actually an array of services. Those arrays can be regarded as "tags".

Funky offers you an easy way to tag services, using the @Tag annotation. This is a great way to remove a lot of boilerplate code!

Here is an example:

/**
 * @Factory(
 *     tags={@Tag(name="twigExtensions")}
 * ) 
 */
public static function myTwigExtension(\Twig_Environment $twig, MyTwigExtension $extension) : \MyTwigExtension
{
    // ...
}

This piece of code declares a \MyTwigExtension entry, and adds this entry to the twigExtensions tag.

Thereafter, you can fetch the tagged services from the container easily.

For instance:

/**
 * 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);
    }
}

You can specify an optional priority level for each tagged service:

/**
 * @Factory(
 *     tags={@Tag(name="my.tag", priority=42.1)}
 * ) 
 */

Low priority items will appear first. High priority items will appear last.

Note: under the hood, the tagged services are actually \SplPriorityQueue objects. Those are iterables, but are not PHP arrays. If you need arrays, you can use the iterator_to_array PHP function to cast those in arrays.

FAQ

Why the name?

Because the PHP ecosystem loves music (did you notice? Composer, Symfony, ...) And because Funky takes its roots and inspiration from bitexpert/disco, a PSR-11 compliant container that also relies on annotations!

Why do factories need to be static?

In the context of service providers, a factory is a function that builds a service based on the parameters it is passed. If you have done some functional programming, you should consider a factory is a pure function.

Given a set of parameters, it will always generate the same result. Therefore, a factory have no need of any object state (the state is contained in the container that is passed in parameter).

Furthermore, compiled containers (like Symfony or PHP-DI) can use the fact that a factory is public static to greatly optimize the way they work with the service provider. By caching the results given by the getFactories and getExtensions methods, a compiled container can make the overhead of using Funky to nearly 0.

Troubleshooting

It says some file cannot be created

Funky needs to generate some PHP files to be fast. Those files will be written in the Funky 'generated' directory (so most of the time, in vendor/thecodingmachine/funky/generated). If Funky is called from Apache, Apache might not have the right to write files in this directory. You will have to change the rights of this directory to let Apache write in it.

Some xxxHelper class cannot be autoloaded

As explained above, Funky needs to generate some PHP files to be fast. Those classes are written in the Funky 'generated' directory. If you used Composer's autoritative classmap (for instance with the --classmap-authoritative option), Composer will scan all classes of your project to build the classmap. Problem: Funky's classes are not yet written! So Composer classmap will miss those classes. Therefore, when using Funky, you should not use the --classmap-authoritative option.

thecodingmachine/funky 适用场景与选型建议

thecodingmachine/funky 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 12.16k 次下载、GitHub Stars 达 7, 最近一次更新时间为 2017 年 11 月 10 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 thecodingmachine/funky 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 thecodingmachine/funky 我们能提供哪些服务?
定制开发 / 二次开发

基于 thecodingmachine/funky 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 12.16k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 7
  • 点击次数: 13
  • 依赖项目数: 22
  • 推荐数: 0

GitHub 信息

  • Stars: 7
  • Watchers: 6
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2017-11-10