{"id":18929431,"url":"https://github.com/thecodingmachine/funky","last_synced_at":"2025-10-27T17:04:48.409Z","repository":{"id":57067873,"uuid":"108279389","full_name":"thecodingmachine/funky","owner":"thecodingmachine","description":"Write service providers easily using annotations","archived":false,"fork":false,"pushed_at":"2022-12-06T15:14:13.000Z","size":51,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-11T18:59:47.282Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thecodingmachine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-25T14:11:02.000Z","updated_at":"2023-12-19T17:41:23.000Z","dependencies_parsed_at":"2023-01-23T08:55:11.765Z","dependency_job_id":null,"html_url":"https://github.com/thecodingmachine/funky","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecodingmachine%2Ffunky","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecodingmachine%2Ffunky/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecodingmachine%2Ffunky/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thecodingmachine%2Ffunky/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thecodingmachine","download_url":"https://codeload.github.com/thecodingmachine/funky/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248662320,"owners_count":21141559,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T11:32:41.481Z","updated_at":"2025-10-27T17:04:48.348Z","avatar_url":"https://github.com/thecodingmachine.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Latest Stable Version](https://poser.pugx.org/thecodingmachine/funky/v/stable)](https://packagist.org/packages/thecodingmachine/funky)\n[![Total Downloads](https://poser.pugx.org/thecodingmachine/funky/downloads)](https://packagist.org/packages/thecodingmachine/funky)\n[![Latest Unstable Version](https://poser.pugx.org/thecodingmachine/funky/v/unstable)](https://packagist.org/packages/thecodingmachine/funky)\n[![License](https://poser.pugx.org/thecodingmachine/funky/license)](https://packagist.org/packages/thecodingmachine/funky)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/thecodingmachine/funky/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/thecodingmachine/funky/?branch=master)\n[![Build Status](https://travis-ci.org/thecodingmachine/funky.svg?branch=master)](https://travis-ci.org/thecodingmachine/funky)\n[![Coverage Status](https://coveralls.io/repos/thecodingmachine/funky/badge.svg?branch=master\u0026service=github)](https://coveralls.io/github/thecodingmachine/funky?branch=master)\n\n**Work in progress, no stable version yet**\n\nthecodingmachine/funky\n======================\n\nFunky is tool to help you write service providers compatible with [container-interop/service-provider](https://github.com/container-interop/service-provider/).\n\n### Without Funky:\n\nThe current trend is to directly implement the `ServiceProviderInterface` when writing service providers.\n\nFor instance:\n\n```php\nclass WhoopsMiddlewareServiceProvider implements ServiceProviderInterface\n{\n\n    public function getFactories()\n    {\n        return [\n            Middleware::class =\u003e [self::class,'createMiddleware'],\n        ];\n    }\n\n    public function getExtensions()\n    {\n        return [\n            MiddlewareListServiceProvider::MIDDLEWARES_QUEUE =\u003e [self::class,'updatePriorityQueue']\n        ]\n    }\n\n    public static function createMiddleware() : WhoopsMiddleware\n    {\n        return new WhoopsMiddleware();\n    }\n\n    public static function updatePriorityQueue(ContainerInterface $container, \\SplPriorityQueue $queue) : \\SplPriorityQueue\n    {\n        $queue-\u003einsert($container-\u003eget(Middleware::class), MiddlewareOrder::EXCEPTION_EARLY);\n        return $queue;\n    }\n}\n```\n\n### With Funky:\n\n```php\nclass WhoopsMiddlewareServiceProvider extends Funky\\ServiceProvider\n{\n    /**\n     * @Factory(\n     *   tags={@Tag(name=\"middlewares.queue\", priority=MiddlewareOrder::EXCEPTION_EARLY)}\n     * )\n     */\n    public static function createMiddleware() : WhoopsMiddleware\n    {\n        return new WhoopsMiddleware();\n    }\n}\n```\n\nFunky implements the `getFactories` and `getExtensions` methods.\n\nYour class simply extends `TheCodingMachine\\Funky\\ServiceProvider`. Funky will scan your class for `@Factory` and `@Extension` annotations.\n\n## Install\n\nSimply require `thecodingmachine/funky` from your service provider package.\n\n```php\n$ composer require thecodingmachine/funky\n```\n\n## Usage\n\nInstead of creating a class that implements `Interop\\Container\\ServiceProviderInterface`, you extend the `TheCodingMachine\\Funky\\ServiceProvider` class.\n\n## The @Factory annotation\n\n### Default naming\n\nBy default, the `@Factory` is using the return type as the name of the container entry\n\n```php\n/**\n * @Factory\n */\npublic static function createMiddleware() : WhoopsMiddleware\n{\n    return new WhoopsMiddleware();\n}\n```\n\n```php\n$container-\u003eget(WhoopsMiddleware::class) // will return the service\n```\n\n### Specifying a name\n\nYou can use the \"name\" attribute of the `@Factory` annotation to specify a name:\n\n```php\n/**\n * @Factory(name=\"whoops\")\n */\npublic static function createMiddleware() : WhoopsMiddleware\n{\n    return new WhoopsMiddleware();\n}\n```\n\n```php\n$container-\u003eget('whoops') // will return the service\n```\n\n### Using the method name\n\nYou can use the \"nameFromMethodName\" attribute of the `@Factory` annotation to tell Funky to use the method name as an identifier:\n\n```php\n/**\n * @Factory(nameFromMethodName=true)\n */\npublic static function whoopsMiddleware() : WhoopsMiddleware\n{\n    return new WhoopsMiddleware();\n}\n```\n\n```php\n$container-\u003eget('whoopsMiddleware') // will return the service\n```\n\n## Auto-wiring\n\nFunky 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.\n\n```php\n/**\n * @Factory()\n */\npublic static function myService(LoggerInterface $logger) : MyService\n{\n    // $logger is fetched from the container using \"$container-\u003eget(LoggerInterface::class)\"\n    return new MyService($logger);\n}\n```\n\nIf you do not add type-hints (or use a scalar type-hint), Funky will try to fetch the dependency using the parameter name.\n\n```php\n/**\n * @Factory()\n */\npublic static function myService(string $ROOT_PATH) : MyService\n{\n    // $ROOT_PATH is fetched from the container using \"$container-\u003eget('ROOT_PATH')\"\n    return new MyService(string $ROOT_PATH);\n}\n```\n\nFinally, at any point, you can also inject the whole container to fetch any dependency from it:\n\n```php\n/**\n * @Factory()\n */\npublic static function myService(ContainerInterface $container) : MyService\n{\n    return new MyService($container-\u003eget('ROOT_PATH'));\n}\n```\n\n\n## Extending entries\n\nYou can extend entries from the container using the `Extension` annotation.\n\nYou 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.\n\n```php\n/**\n * @Extension()\n */\npublic static function registerTwigExtension(\\Twig_Environment $twig, MyTwigExtension $extension) : \\Twig_Environment\n{\n    $twig-\u003eregister($extension);\n    return $twig;\n}\n```\n\n### Specifying a name\n\nYou can use the \"name\" or \"nameFromMethodName\" attribute of the `@Extension` annotation to specify the name of the entry to be extended:\n\n```\n@Extension(name=\"twig\") // The extended entry is named \"twig\"\n```\n\n```php\n/**\n * The extended entry is named twig because the method name is \"twig\"\n * @Extension(name=\"twig\") \n */\npublic static function registerExtension(\\Twig_Environment $twig, MyTwigExtension $extension) : \\Twig_Environment\n{\n    // ...\n}\n```\n\n\n```php\n/**\n * The extended entry is named \"twig\" because the method name is \"twig\"\n * @Extension(nameFromMethodName=true) \n */\npublic static function twig(\\Twig_Environment $twig, MyTwigExtension $extension) : \\Twig_Environment\n{\n    // ...\n}\n```\n\n## Tags\n\nOut of the box, the [container-interop/service-provider](https://github.com/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\".\n\nFunky offers you an easy way to tag services, using the `@Tag` annotation. This is a great way to remove a lot of boilerplate code!\n\nHere is an example:\n\n```php\n/**\n * @Factory(\n *     tags={@Tag(name=\"twigExtensions\")}\n * ) \n */\npublic static function myTwigExtension(\\Twig_Environment $twig, MyTwigExtension $extension) : \\MyTwigExtension\n{\n    // ...\n}\n```\n\nThis piece of code declares a `\\MyTwigExtension` entry, and adds this entry to the `twigExtensions` tag.\n\nThereafter, you can fetch the tagged services from the container easily.\n\nFor instance:\n\n```php\n/**\n * Here, the tag \"twigExtensions\" used in the function above is injected using auto-wiring.  \n * @Factory()\n */\npublic static function twig(iterable $twigExtensions) : \\Twig_Environment\n{\n    // ...\n    $twig = new Twig_Environement(...);\n    foreach ($twigExtensions as $twigExtension) {\n        $twig-\u003eregister($twigExtension);\n    }\n}\n```\n\nYou can specify an optional priority level for each tagged service:\n\n```php\n/**\n * @Factory(\n *     tags={@Tag(name=\"my.tag\", priority=42.1)}\n * ) \n */\n```\n\nLow priority items will appear first. High priority items will appear last.\n\nNote: 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.\n\n## FAQ\n\n\n### Why the name?\n\nBecause the PHP ecosystem loves music (did you notice? Composer, Symfony, ...)\nAnd because Funky takes its roots and inspiration from [bitexpert/disco](https://github.com/bitExpert/disco), a PSR-11 compliant container that also relies on annotations!\n\n### Why do factories need to be *static*?\n\nIn the context of service providers, a factory is a function that builds a service based on the parameters it is passed.\nIf you have done some functional programming, you should consider a factory is a **pure function**.\n\nGiven 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).\n\nFurthermore, 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.\nBy caching the results given by the `getFactories` and `getExtensions` methods, a compiled container can make the overhead of using Funky to nearly 0.\n\n## Troubleshooting\n\n### It says some file cannot be created\n\nFunky 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`).\nIf Funky is called from Apache, Apache might not have the right to write files in this directory.\nYou will have to change the rights of this directory to let Apache write in it.\n\n### Some xxxHelper class cannot be autoloaded\n\nAs explained above, Funky needs to generate some PHP files to be fast. Those classes are written in the Funky 'generated' directory.\nIf you used [Composer's autoritative classmap](https://getcomposer.org/doc/articles/autoloader-optimization.md#optimization-level-2-a-authoritative-class-maps) \n(for instance with the `--classmap-authoritative` option), Composer will scan all classes of your project to build\nthe classmap. Problem: Funky's classes are not yet written! So Composer classmap will miss those classes.\nTherefore, when using Funky, you should not use the `--classmap-authoritative` option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthecodingmachine%2Ffunky","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthecodingmachine%2Ffunky","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthecodingmachine%2Ffunky/lists"}