{"id":21057904,"url":"https://github.com/driftphp/docs","last_synced_at":"2026-01-02T03:42:35.891Z","repository":{"id":100777986,"uuid":"224198408","full_name":"driftphp/docs","owner":"driftphp","description":"Docs for DriftPHP","archived":false,"fork":false,"pushed_at":"2023-01-05T10:42:43.000Z","size":293,"stargazers_count":12,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-20T19:35:44.599Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HTML","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/driftphp.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-26T13:26:45.000Z","updated_at":"2023-01-05T10:34:44.000Z","dependencies_parsed_at":"2023-06-09T21:15:31.482Z","dependency_job_id":null,"html_url":"https://github.com/driftphp/docs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/driftphp%2Fdocs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/driftphp%2Fdocs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/driftphp%2Fdocs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/driftphp%2Fdocs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/driftphp","download_url":"https://codeload.github.com/driftphp/docs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243502521,"owners_count":20301078,"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-19T17:05:44.006Z","updated_at":"2026-01-02T03:42:35.805Z","avatar_url":"https://github.com/driftphp.png","language":"HTML","readme":"# Architecture\r\n\r\nThis framework is an exercise of mixing the best of two excellent worlds if we\r\ntalk about PHP. On one hand we have Symfony, a collection of PHP components with\r\na large usage around the PHP ecosystem. On top of them, the Symfony framework,\r\nwith a well known project architecture. On the other hand, we find ReactPHP, a\r\nPHP library on top of Promises and an EventLoop, that provides a really easy\r\nnon-blocking way of managing Requests using a single PHP thread.\r\n\r\nDriftPHP framework follow the Symfony structure, the same Request/Response \r\nparadigm and the same Kernel bases, but turning all the process non-blocking by\r\nusing ReactPHP promises and event loop.\r\n\r\nIf you're used to working with Symfony skeleton, DriftPHP will seem a bit\r\nfamiliar then. You will recognise the kernel (a different one, but in fact,\r\nalmost the same), the way Controllers and Commands are defined, called and used,\r\nand the way we define services using Dependency Injection (yes, you can still\r\nuse autowiring here...).\r\n\r\nBefore start digging into DriftPHP, is important to understand a little bit what\r\nconcepts are you going to use, or even discover, in this package. Each topic is\r\nstrongly related to the development of any application on top of DriftPHP, so\r\nmake sure you understand each one of them. In the future, all these topics will\r\nbe packed into a new and important chapter of the documentation is being built\r\nat this moment.\r\n\r\n## Symfony Kernel\r\n\r\nHave you ever thought what the Symfony Kernel is really about? In fact, we could\r\nsay that is one of the most important elements of the whole Symfony ecosystem,\r\nand we would be wrong if we say that is not the most core one. But what is it\r\nreally about? Let's reduce its complexity into 1 simple bullet.\r\n\r\n- Given a Request, give me a Response\r\n\r\nThat's it. Having a Request object, properly hydrated from the magic PHP\r\nvariables (`$_GET`, `$_POST`, `$_SERVER` ...), the kernel has one single (and\r\nimportant) job. Guess everything is needed to return a Response. In order to\r\nmake that job, the Kernel uses an event dispatcher to dispatch some events. All\r\nthese events are properly documented in the Symfony documentation. And because\r\nof these events and some subscribers included in the `FrameworkBundle` package,\r\nwe can match a route, guess the proper Controller instance and call the\r\nController method, depending on our configuration.\r\n\r\nOnce we are in the controller, and if we don't have injected the Container\r\nunder any circumstance, then we can say that the Framework job is properly\r\nfinished. This is what we can call our domain  \r\n\r\n**Welcome to your domain**\r\n\r\nSo on one side of the framework (probably in your Symfony `/public/index.php`\r\nfile) you will find a line where the Kernel handles a Request and expects a\r\nResponse.\r\n\r\n```php\r\n$response = $kernel-\u003ehandle($request);\r\n```\r\n\r\nEach time you see something like that, take in account that your server will be\r\nblocked from the function call, until the result is returned. This is what we\r\ncan define as a blocking operation. If the whole operations lasts 10 seconds,\r\nduring 10 seconds nothing will be doable in this PHP thread (yes. 10 seconds.\r\nA slow Elasticsearch operation or MYSql operation could last something like\r\nthat, at least, could be possible, right?)\r\n\r\nWe will change that.\r\nKeep reading.\r\n\r\n## HTTP Server\r\n\r\nNow remember one of your Symfony projects. Remember about the Apache or Nginx\r\nconfiguration, then remember about the PHP-FPM, about how to link all together,\r\nabout deploying it on docker...\r\n\r\nHave you ever thought about what happens there? I'm going to show you a small\r\nbullet list about what's going on.\r\n\r\n- Your Nginx takes the HTTP Request.\r\n- The Http Request is passed to PHP-FPM and is interpreted by PHP-FPM.\r\n- Interpreted means, basically, that your `public/index.php` file will be\r\nexecuted.\r\n- One kernel is instanced and a new Symfony Request is created..\r\n- The Symfony Request is handled and a new Response is generated\r\n- The Response is passed through the Nginx and returned to the final user.\r\n\r\nNow, let's ask ourselves a simple question. How many requests do we have per day\r\nin our project? Or even more important. How long it takes to generate a kernel, \r\nbuild all the needed services (**all**) once and again, and return the result?\r\n\r\nWell. The answer is pretty easy. Depends on how much performance do you really\r\nneed. If you have a 1000 requests/day blog, then you will be probably okay with\r\nthis stack, but what if you have millions per hour? How important in that case\r\ncan be a simple millisecond?\r\n\r\nCan we solve this?\r\nYes. We will solve this.\r\nKeep reading.\r\n\r\n## ReactPHP\r\n\r\nSince some years ago, PHP turned a bit more interesting with the implementation\r\nof Promises from ReactPHP. The language is exactly the same, PHP, but the unique\r\ndifference between the regular PHP usage and the ReactPHP is that each time your\r\nproject uses the infrastructure layer (Filesystem, Redis, Mysql) everything is\r\nreturned eventually.\r\n\r\nAbout the fundamental problem of using ReactPHP Promises in your regular Symfony\r\napplication you can find a little bit more information on these links.\r\n\r\n- https://medium.com/@apisearch/symfony-and-reactphp-series-82082167f6fb\r\n- https://es.slideshare.net/MarcMorera/when-symfony-met-promises-167235900\r\n\r\nAnd some links about ReactPHP\r\n\r\n- https://reactphp.org\r\n- https://github.com/reactphp\r\n\r\nIn few words. You cannot use Promises in Symfony, basically because the kernel\r\nis blocking. Having a Request, wait for the Response. Nothing can be `eventual`\r\nhere, so even if you use Promises, at the end, you will have to wait for the\r\nresponse because the Controller **have to** return a Response instance.\r\n\r\nCan we solve this?\r\nAgain. Yes.\r\nKeep reading.\r\n\r\n## The Framework\r\n\r\nSo, having 3 small problems in front of us, and with the big promise to solve\r\nthis situation, here you have the two main packages that this framework offers\r\nto the user. All of them can be used separately, but all together work as is\r\nexpected.\r\n\r\n- [HTTP Kernel](https://github.com/driftphp/http-kernel)\r\n\r\nSimple. Overwrites the regular and blocking Symfony Kernel to a new one, \r\ninspired by the original, but adapted to work on top of Promises. So everything\r\nhere will be non-blocking, and everything will happen *eventually*.\r\n\r\nCheck the documentation about the small changes you need in your project to use\r\nthis kernel. Are pretty small, but completely necessary if you want to start\r\nusing Promises in your domain.\r\n\r\n- In your Controller, now you will have to return a Promise instead of a\r\nResponse\r\n- Your event listeners will have to be packed inside a Promise as well.\r\nRemember, all will happen eventually.\r\n\r\nFirst solved problem\r\n\r\n- [HTTP Server](https://github.com/driftphp/server)\r\n\r\nForget about PHP-FPM, about Nginx, Apache or any other external servers. Forget\r\nabout them because you will not need them anymore. With this new server, you\r\nwill be able to connect your application to a socket directly with no\r\nintermediaries.\r\n\r\nAnd guess what.  \r\nYour kernel will be created once, and only once.  \r\nThat means that the first requests will last as long as all your current\r\nrequests, but the next ones... well, you won't believe the numbers.\r\n\r\n- [Skeleton](https://github.com/driftphp/skeleton)\r\n\r\nThe skeleton is very simple and basic. Instead of having multiple and different\r\nways of doing a simple thing, we have defined a unique way of doing everything.\r\nFirst of all, the main app configuration (Kernel, routes, services and \r\nbootstrap) is included in the main `Drift/` folder.\r\n\r\nYou can place all your services wherever you want, but we encourage you to use a\r\nsimple and useful architecture based on layers.\r\n\r\n```yaml\r\nDrift/ - All your Drift configuration\r\nsrc/\r\n    - Controller/ - Your controllers. No business logic here\r\n    - Console/ - Your console commands. No logic here\r\n    - Domain/ - Your domain. Only your domain\r\n    - Redis/ - Your Redis adapters\r\n    - Mysql/ - Your Mysql adapters\r\n```\r\n\r\n# Getting started\r\n\r\nWelcome to the documentation of DriftPHP. Yet another framework on top of PHP,\r\nyes, but in DriftPHP we want to make a deal with you. Performance matters\r\nequally to functionality, so you take care about your domain, and we take care\r\nabout serving it in an insane way.\r\n\r\n\u003e DriftPHP is not stable. This means that you should not put it on production,\r\n\u003e at least before making a really big testing coverage, specially functional\r\n\u003e testing coverage. We will work to make this collection of packages stable as\r\n\u003e fast as we can.\r\n\r\nYou can start using DriftPHP by just installing our small skeleton project. In\r\nthis skeleton you will be able to start working in a traditional project on top\r\nof the Symfony syntax and Request/Response paradigm.\r\n\r\n``` bash\r\ncomposer create-project drift/skeleton -sdev\r\n```\r\n\r\n## Start the server\r\n\r\nOnce you have created a small skeleton of your application on your computer,\r\nlet's run the server. This server is meant to be production-ready, so in this\r\ndocumentation you will find many ways to deploy this in your infrastructure.\r\n\r\n``` bash \r\nphp vendor/bin/server run 0.0.0.0:8000\r\n```\r\n\r\nYou can start the framework in development mode and enable debugging. Of course,\r\nthis configuration is meant to be used only on development.\r\n\r\n``` bash \r\nphp vendor/bin/server run 0.0.0.0:8000 --dev --debug\r\n```\r\n\r\nAnd you can even start the server in watcher mode by using the watch command \r\ninstead of the run one. As soon as you make changes on your code, the server\r\nwill be reloaded, so you don't have to restart it again once and again.\r\n\r\n``` bash \r\nphp vendor/bin/server watch 0.0.0.0:8000 --dev --debug\r\n```\r\n\r\n## Test the endpoints\r\n\r\n\u003e You can test a couple of endpoints already built-in in this skeleton. Make \r\n\u003e sure to delete the endpoint before working on a real life project.\r\n\r\nOnce you have the server connected to a websocket, it's time to check some\r\nendpoints and see if this is already working.\r\n\r\n\u003e Make sure your server is properly running\r\n\r\nYou can test a simple test endpoint...\r\n\r\n```bash\r\ncurl \"127.0.0.1:8000\"\r\n```\r\n\r\n... or you can test how static content server works by opening in your browser\r\na distributed image. You have to open in your browser\r\n`http://127.0.0.1:8000/public/driftphp.png` and you should see our amazing logo.\r\n\r\n## Events\r\n\r\nDriftPHP uses the same kernel events that Symfony does. You can check a bit more\r\ninformation about them in \r\n[Built-in Symfony Events](https://symfony.com/doc/current/reference/events.html).\r\nDriftPHP introduces a new kernel event called `kernel.preload`, and dispatched\r\nonce the Kernel is booted. This event is dispatched only once and must be used\r\nonly for preloading content before the first request is handled.\r\n\r\n```php\r\nuse Drift\\HttpKernel\\Event\\PreloadEvent;\r\n\r\n/**\r\n * @param PreloadEvent $event\r\n */\r\npublic function onKernelPreload(PreloadEvent $event)\r\n{\r\n    // ...\r\n}\r\n```\r\n\r\nOn the other hand, the event `kernel.terminate` is never triggered, as the\r\nkernel is never terminated.\r\n\r\nHere you have the list of used events in DriftPHP kernel.\r\n\r\n- kernel.preload\r\n- kernel.request\r\n- kernel.controller\r\n- kernel.controller_arguments\r\n- kernel.view\r\n- kernel.response\r\n- kernel.finish_request\r\n- kernel.exception\r\n\r\n## Built-in services\r\n\r\nIn DriftPHP you can find some already built-in services, ready to be injected\r\nand used in your services. They are all mostly related to the EventLoop\r\n(remember that you can work only with one running EventLoop, and this one is\r\nalready created for the server itself).\r\n\r\n### EventLoop\r\n\r\nYou can use the EventLoop in your services by injecting it, even using\r\nautowiring.\r\n\r\n```php\r\npublic function __construct(LoopInterface $loop)\r\n{\r\n    $this-\u003eloop = $loop;\r\n}\r\n```\r\n\r\nYou can access to the loop in the container by using this service name\r\n\r\n- drift.event_loop\r\n\r\n### Filesystem\r\n\r\nDriftPHP have created for you a Filesystem instance. Use this object to make all\r\ndisk requests, like reading from a file or writing a new one. Do **not** use the\r\nregular PHP methods or your server will experiment a serious lack of performance\r\ndue to blocking operations.\r\n\r\nYou can read a bit more about this package at\r\n[ReactPHP Filesystem](https://github.com/reactphp/filesystem)\r\n\r\n```php\r\nuse React\\Filesystem\\Filesystem;\r\n\r\npublic function __construct(Filesystem $filesystem)\r\n{\r\n    $this-\u003efilesystem = $filesystem;\r\n}\r\n```\r\n\r\nYou can access to the loop in the container by using this service name\r\n\r\n- drift.filesystem\r\n\r\n## Creating a Controller\r\n\r\nThis is the easiest part of all. You can create controllers the same way you've\r\ndone until now, but the **only** difference is that, hereinafter, you won't\r\nreturn Response instances, but Promises that, once resolved, will return a\r\nResponse object.\r\n\r\nLet's take a look at `GetValueController` code of our demo. This is the response\r\ncode for the controller called method. This Redis client is asynchronous, so\r\neach time you call any method, for example `-\u003eget($key)` you won't get a value,\r\nbut a promise. \r\n\r\n```php\r\nnamespace App\\Controller;\r\n\r\nuse App\\Redis\\RedisWrapper;\r\nuse React\\Promise\\PromiseInterface;\r\nuse Symfony\\Component\\HttpFoundation\\JsonResponse;\r\nuse Symfony\\Component\\HttpFoundation\\Request;\r\n\r\n/**\r\n * Class GetValueController\r\n */\r\nclass GetValueController\r\n{\r\n    /**\r\n     * @var RedisWrapper\r\n     *\r\n     * Redis Wrapper\r\n     */\r\n    private $redisWrapper;\r\n\r\n    /**\r\n     * PutValueController constructor.\r\n     *\r\n     * @param RedisWrapper $redisWrapper\r\n     */\r\n    public function __construct(RedisWrapper $redisWrapper)\r\n    {\r\n        $this-\u003eredisWrapper = $redisWrapper;\r\n    }\r\n\r\n    /**\r\n     * Invoke\r\n     *\r\n     * @param Request $request\r\n     *\r\n     * @return PromiseInterface\r\n     */\r\n    public function __invoke(Request $request)\r\n    {\r\n        $key = $request\r\n            -\u003eattributes\r\n            -\u003eget('key');\r\n\r\n        return $this\r\n            -\u003eredisWrapper\r\n            -\u003egetClient()\r\n            -\u003eget($key)\r\n            -\u003ethen(function($value) use ($key) {\r\n                return new JsonResponse(\r\n                    [\r\n                        'key' =\u003e $key,\r\n                        'value' =\u003e is_string($value)\r\n                            ? $value\r\n                            : null,\r\n                    ],\r\n                    200\r\n                );\r\n            });\r\n    }\r\n}\r\n```\r\n\r\nWhen the promise is resolved, the `then` method is called with the given result\r\nas a unique parameter. Once we have this value, we return a `JsonResponse`\r\ninstance inside the callback.\r\n\r\nAnd because a `-\u003ethen()` method returns a new Promise, and we're returning\r\ndirectly this value, the kernel receives a Promise and not a value. And that's\r\nit. The server should take care of the rest\r\n\r\n## Check the demo\r\n\r\nIf you want a bit more experience with DriftPHP you might have a look at our\r\ndistributed demo. We will update this demo in order to have as many built-in\r\nfunctionalities and integrations as we can, so make sure you keep up-to-date\r\nwith new updates.\r\n\r\n- [DriftPHP Demo](https://github.com/driftphp/demo)\r\n\r\nMake sure you give this demo a Github star. That will help us a lot.\r\n\r\n# The HTTP Kernel\r\n\r\n[![CircleCI](https://circleci.com/gh/driftphp/http-kernel.svg?style=svg)](https://circleci.com/gh/driftphp/http-kernel)\r\n\r\nThis package provides async features to the Symfony (+4.3) Kernel. This\r\nimplementation uses [ReactPHP Promise](https://github.com/reactphp/promise) \r\nlibrary and paradigm for this purposes.\r\n\r\n## Installation\r\n\r\nYou can install the package with composer. This is a PHP Library, so installing\r\nthis repository will not change your original project behavior.\r\n\r\n```yml\r\n{\r\n  \"require\": {\r\n    \"drift/http-kernel\": \"dev-master\"\r\n  }\r\n}\r\n```\r\n\r\nYou can get the [source code](https://github.com/driftphp/http-kernel) from \r\nGithub.\r\n\r\nOnce you have the package under your vendor folder, now it's time to turn you\r\napplication asynchronous-friendly by changing your kernel implementation, from\r\nthe Symfony regular HTTP Kernel class, to the new Async one.\r\n\r\n```php\r\nuse Drift\\HttpKernel\\AsyncKernel;\r\n\r\nclass Kernel extends AsyncKernel\r\n{\r\n    use MicroKernelTrait;\r\n```\r\n\r\n\u003e With this change, nothing should happen. This async kernel maintains all back\r\n\u003e compatibility, so should work inside any synchronous (regular) Symfony\r\n\u003e project.\r\n\r\n## Controllers\r\n\r\nYour controller will be able to return a Promise now. It is mandatory to do\r\nthat? Non-blocking operations are always optional, so if you build your domain\r\nblocking, this is going to work as well.\r\n\r\n```php\r\n/**\r\n * Class Controller.\r\n */\r\nclass Controller\r\n{\r\n    /**\r\n     * Return value.\r\n     *\r\n     * @return Response\r\n     */\r\n    public function getValue(): Response\r\n    {\r\n        return new Response('X');\r\n    }\r\n\r\n    /**\r\n     * Return fulfilled promise.\r\n     *\r\n     * @return PromiseInterface\r\n     */\r\n    public function getPromise(): PromiseInterface\r\n    {\r\n        return new FulfilledPromise(new Response('Y'));\r\n    }\r\n}\r\n```\r\n\r\nBoth controller actions are correct.\r\n\r\n## Event Dispatcher\r\n\r\nGoing asynchronous has some intrinsic effects, and one of these effects is that\r\nevent dispatcher has to work a little bit different. If you base all your domain\r\non top of Promises, your event listeners must be a little bit different. The\r\nevents dispatched are exactly the same, but the listeners attached to them must\r\nchange a little bit the implementation, depending on the expected behavior.\r\n\r\nAn event listener can return a Promise. Everything inside this promise will be\r\nexecuted once the Promise is executed, and everything outside the promise will\r\nbe executed at the beginning of all listeners, just before the first one is\r\nfulfilled.\r\n\r\n```php\r\n/**\r\n * Handle get Response.\r\n *\r\n * @param ResponseEvent $event\r\n *\r\n * @return PromiseInterface\r\n */\r\npublic function handleGetResponsePromiseA(ResponseEvent $event)\r\n{\r\n    $promise = (new FulfilledPromise())\r\n        -\u003ethen(function () use ($event) {\r\n        \r\n            // This line is executed eventually after the previous listener\r\n            // promise is fulfilled\r\n        \r\n            $event-\u003esetResponse(new Response('A'));\r\n        });\r\n        \r\n    // This line is executed before the first event listener promise is\r\n    // fulfilled\r\n        \r\n    return $promise;\r\n}\r\n```\r\n\r\n# The Server\r\n\r\n[![CircleCI](https://circleci.com/gh/driftphp/server.svg?style=svg)](https://circleci.com/gh/driftphp/server)\r\n\r\nThis package provides an async server for DriftPHP framework based on ReactPHP\r\npackages and Promise implementation. The server is distributed with all the\r\nSymfony based kernel adapters, and can be easily extended for new Kernel\r\nmodifications.\r\n\r\n## Installation\r\n\r\nIn order to use this server, you only have to add the requirement in composer.\r\nOnce updated your dependencies, you will find a brand new server bin inside the\r\n`vendor/bin` folder.\r\n\r\n```yml\r\n{\r\n  \"require\": {\r\n    \"driftphp/server\": \"dev-master\"\r\n  }\r\n}\r\n```\r\n\r\nYou can get the [source code](https://github.com/driftphp/server) from \r\nGithub.\r\n\r\n## Usage\r\n\r\nThis is a PHP file. This means that the way of starting this server is by, just,\r\nexecuting it.\r\n\r\n```console\r\nvendor/bin/server run 0.0.0.0:8100\r\n```\r\n\r\nYou will find that the server starts with a default configuration. You can\r\nconfigure how the server starts and what adapters use.\r\n\r\n- Adapter: The kernel adapter for the server. This server needs a Kernel\r\n  instance in order to start serving Requests. By default, `symfony4`. Can be\r\n  overridden with option `--adapter` and the value must be a valid class\r\n  namespace of an instance of `KernelAdapter`\r\n\r\n```console\r\nphp vendor/bin/server run 0.0.0.0:8100 --adapter=symfony4\r\nphp vendor/bin/server run 0.0.0.0:8100 --adapter=My\\Own\\Adapter\r\n```\r\n\r\n- Environment: Kernel environment. By default `prod`, but turns `dev` if the\r\n  option `--dev` is found, or the defined one if you define it with `--env`\r\n  option.\r\n\r\n```console\r\nphp vendor/bin/server run 0.0.0.0:8100 --dev\r\nphp vendor/bin/server run 0.0.0.0:8100 --env=test\r\n```\r\n\r\n- Debug: Kernel will start with this option is enabled. By default false,\r\n  enabled if the option `--debug` is found. Makes sense on development\r\n  environment, but is not exclusive.\r\n\r\n```console\r\nphp vendor/bin/server run 0.0.0.0:8100 --dev --debug\r\n```\r\n\r\n## Watcher\r\n\r\nThe server uses a watcher for dynamic content changes. This watches will check\r\nfor changes on every file you want, and will reload the server properly.\r\n\r\n- Go to [PHP Watcher documentation](https://github.com/seregazhuk/php-watcher)\r\n\r\nIn order to start the server in a watching mode, use the `watch` command instead\r\nof the `run` command. All the documented options under the `run` command will be\r\nvalid in this new command.\r\n\r\n```console\r\nphp vendor/bin/server watch 0.0.0.0:8100\r\n```\r\n\r\n## Serving static files\r\n\r\nKernel Adapters have already defined the static folder related to the kernel.\r\nFor example, Symfony4 adapter will provide static files from folder `/public`.\r\n\r\nYou can override the static folder with the command option `--static-folder`.\r\nAll files inside this defined folder will be served statically in a non-blocking\r\nway\r\n\r\n```console\r\nphp vendor/bin/server run 0.0.0.0:8100 --static-folder=public\r\n```\r\n\r\nYou can disable static folder with the option `--no-static-folder`. This can be\r\nuseful when working with the adapter value and want to disable the default\r\nvalue, for example, for an API.\r\n\r\n\r\n```console\r\nphp vendor/bin/server run 0.0.0.0:8100 --no-static-folder\r\n```\r\n\r\n# The Command Bus\r\n\r\n[![CircleCI](https://circleci.com/gh/driftphp/command-bus-bundle.svg?style=svg)](https://circleci.com/gh/driftphp/command-bus-bundle)\r\n\r\nDriftPHP is a performance-oriented framework on top of PHP. This means that we,\r\nas a project, encourage you to take care of the architecture of all our\r\nimplementations. Not only on terms of returning times, but on terms of\r\nscalability as well.\r\n\r\nOne of the DriftPHP components brings you the opportunity to work on top of CQRS\r\npattern, using Commands and Queries, some command buses and an already built-in\r\nasync adapters with different implementations.\r\n\r\n## Installation\r\n\r\nYou can install the package by using composer, or getting the \r\n[source code](https://github.com/driftphp/command-bus-bundle) from Github.\r\n\r\n```bash\r\ncomposer require drift/command-bus\r\n```\r\n\r\nOnce the package is properly loaded, make sure the bundle is loaded for all the\r\nenvironments. You should add the bundle in `Drift/config/bundles.php`.\r\n\r\n```php\r\nreturn [\r\n    //\r\n    Drift\\CommandBus\\CommandBusBundle::class =\u003e ['all' =\u003e true],\r\n    //\r\n];\r\n```\r\n\r\nOnce the bundle is loaded in the kernel, you'll be able to use 3 different\r\nbuses, each one of them with an specific purpose.\r\n\r\n- [QueryBus](#query-bus)\r\n- [CommandBus](#command-bus)\r\n- [InlineCommandBus](#inline-command-bus)\r\n\r\n## Query Bus\r\n\r\nUse this bus for asking queries. Remember that in the CQRS pattern, queries\r\nshould never change the state of the persistence layer and should return a value\r\nor a Domain exception.\r\n\r\n```php\r\nuse Drift\\CommandBus\\Bus\\QueryBus;\r\nuse Symfony\\Component\\HttpFoundation\\Request;\r\nuse Symfony\\Component\\HttpFoundation\\Response;\r\npublic function __execute(Request $request) {\r\n    /* @var QueryBus $queryBus */\r\n    return $queryBus\r\n        -\u003eask(new GetSomething())\r\n        -\u003ethen(function($something) {\r\n        \r\n            return new Response($something, 200);\r\n        });\r\n}\r\n```\r\n\r\nYou can use *autowiring* for the QueryBus injection or you can manually inject\r\nthe bus by using it's service name `drift.query_bus`\r\n\r\n```yaml\r\nMy\\Service:\r\n    arguments:\r\n      - \"@drift.query_bus\"\r\n```\r\n\r\nYou can add handlers to this bus by defining them with the tag `query_handler`.\r\nRemember that you can define them all in bulk by using *autowiring* and setting\r\nthis tag just one time.\r\n\r\n```yaml\r\nDomain\\QueryHandler\\:\r\n    resource: \"../../src/Domain/QueryHandler\"\r\n    tags: ['query_handler']\r\n```\r\n\r\n## Command Bus\r\n\r\nUse this bus for making changes to the persistence layer. In this case, CQRS\r\npattern tells that Commands do **NOT** return any value, and if you return a\r\nDomain exception, this one should not be related to the persistence layer.\r\n\r\n```php\r\nuse Drift\\CommandBus\\Bus\\CommandBus;\r\nuse Symfony\\Component\\HttpFoundation\\Request;\r\nuse Symfony\\Component\\HttpFoundation\\Response;\r\npublic function __execute(Request $request) {\r\n    /* @var CommandBus $commandBus */\r\n    return $commandBus\r\n        -\u003eexecute(new DoSomething())\r\n        -\u003ethen(function() {\r\n        \r\n            return new Response('OK', 202);\r\n        });\r\n}\r\n```\r\n\r\nYou can use *autowiring* for the CommandBus injection or you can manually inject\r\nthe bus by using it's service name `drift.command_bus`\r\n\r\n```yaml\r\nMy\\Service:\r\n    arguments:\r\n      - \"@drift.command_bus\"\r\n```\r\n\r\nYou can add handlers to this bus by defining them with the tag `command_handler`.\r\nRemember that you can define them all in bulk by using *autowiring* and setting\r\nthis tag just one time.\r\n\r\n```yaml\r\nDomain\\CommandHandler\\:\r\n    resource: \"../../src/Domain/CommandHandler\"\r\n    tags: ['command_handler']\r\n```\r\n\r\nThis bus can be configured as async. That means that all your commands will be\r\nextracted from the bus at the point you chose and enqueued somewhere. You'll be\r\nable to consume these commands with a consumer in a non-blocking way.\r\n\r\n## Inline Command Bus\r\n\r\nThis bus will contain exactly the same handlers and middlewares than the last\r\none, but in this case, the async middleware will not be included never. Use this\r\nbus for commands that you'd never like to be removed from the bus and enqueued\r\nfor asynchronous consumption.\r\n\r\n```php\r\nuse Drift\\CommandBus\\Bus\\InlineCommandBus;\r\nuse Symfony\\Component\\HttpFoundation\\Request;\r\nuse Symfony\\Component\\HttpFoundation\\Response;\r\npublic function __execute(Request $request) {\r\n    /* @var InlineCommandBus $inlineCommandBus */\r\n    return $inlineCommandBus\r\n        -\u003eexecute(new DoSomething())\r\n        -\u003ethen(function() {\r\n        \r\n            return new Response('OK', 202);\r\n        });\r\n}\r\n```\r\n\r\nYou can use *autowiring* for the InlineCommandBus injection or you can manually\r\ninject the bus by using it's service name `drift.inline_command_bus`\r\n\r\n```yaml\r\nMy\\Service:\r\n    arguments:\r\n      - \"@drift.inline_command_bus\"\r\n```\r\n\r\n## Middleware\r\n\r\nAll these buses can have middleware classes defined in your application or\r\ndomain layer. Middleware classes are just simple PHP classes with one public\r\nmethod. In DriftPHP, unlike other bus implementations, the middleware will be\r\nable to be part of your domain, so implementing an external interface will not\r\nbe a thing.\r\n\r\n- Where is the contract then? you might ask...\r\n\r\nWell, is true that a contract should exist between the bus and your domain, and\r\nthe configuration should know what method should be called as soon as is\r\nrequired.\r\n\r\nWell. In DriftPHP you just configure the name of the service and the name of the\r\nmethod to be called, and if the Middleware is not properly configured, then an\r\nException will be thrown during the Kernel build stage.\r\n\r\nLet's see how you can append middleware services in your buses.\r\n\r\n```yaml\r\ncommand_bus:\r\n    query_bus:\r\n        middlewares:\r\n          - middleware1.service\r\n          - Another\\Service\r\n    command_bus:\r\n        middlewares:\r\n          - middleware1.service\r\n          - Another\\Service\r\n```\r\n\r\nRemember that both the CommandBus and the InlineCommandBus will share all\r\nconfiguration.\r\n\r\n## Asynchronous Bus\r\n\r\nBy default, the CommandBus asynchronous property is completely disabled. You can\r\nenable it by choosing one of the proposed implementations. All of them are\r\nimplemented using the *Producer/Consumer* pattern.\r\n\r\n- Redis : In order to use this implementation, you will have to require and\r\nconfigure `drift/redis-bundle` package as is described at\r\n[Redis Adapter](#redis-adapter)\r\n\r\nIn this example, we create a redis client named `queues` that will connect to\r\nlocalhost. Then we define that our command bus will be asynchronous and will\r\nuse this given redis client to use it as a queue system, using the `commands`\r\nredis key.\r\n\r\n```yaml\r\nredis:\r\n    clients:\r\n        queues:\r\n            host: 127.0.0.1\r\ncommand_bus:\r\n    command_bus:\r\n        async_adapter:\r\n            redis:\r\n                client: queues\r\n                key: comamnds\r\n```\r\n\r\n- AMQP (RabbitMQ) : In order to use this implementation, you will have to require\r\nand configure `drift/amqp-bundle` package as is described at\r\n[AMQP Adapter](#amqp-adapter)\r\n\r\nIn this example, we create an amqp client named `queues` that will connect to\r\nlocalhost. Then we define that our command bus will be asynchronous and will\r\nuse this given amqp client to use it as a queue system, using the `commands`\r\namqp queue.\r\n\r\n```yaml\r\namqp:\r\n    connections:\r\n        queues:\r\n            host: 127.0.0.1\r\ncommand_bus:\r\n    command_bus:\r\n        async_adapter:\r\n            redis:\r\n                connection: queues\r\n                queue: comamnds\r\n```\r\n\r\n- InMemory : You can use this adapter only for testing purposes. Commands are\r\nappended in an internal array, and can be consulted during the testing case. You\r\ncan retrieve the adapter by getting the service \r\n`Drift\\CommandBus\\Async\\InMemoryAdapter` defined as public by default.\r\n\r\n```yaml\r\ncommand_bus:\r\n    command_bus:\r\n        async_adapter:\r\n            in_memory:\r\n```\r\n\r\nBy default, if you only have one strategy defined, this one will be the chosen\r\none. If you have many of them, the first one will be the used one. You can\r\nchange this behavior and specifically select which one you want to use by\r\nsetting it in configuration.\r\n\r\n```yaml\r\ncommand_bus:\r\n    command_bus:\r\n        async_adapter:\r\n            adapter: redis\r\n            in_memory:\r\n            redis:\r\n                connection: queues\r\n                queue: comamnds\r\n```\r\n\r\nBy default, the async adapter will be prepended in the beginning of the bus. You\r\ncan change where you want to add this middleware by hand in the middleware list\r\nin configuration\r\n\r\n```yaml\r\ncommand_bus:\r\n    command_bus:\r\n        middlewares:\r\n          - middleware1.service\r\n          - @async\r\n          - Another\\Service\r\n```\r\n\r\nIn this case, the first `middleware1.service` middleware will be executed before\r\nthe command is extracted from the bus, and will be executed too once the command\r\nis consumed asynchronously eventually.\r\n\r\n\u003e You might have the option of choosing what commands should be executed\r\n\u003e asynchronously and what commands should be executed inline. Well, this chose\r\n\u003e should be done in the application layer (Controllers, Commands), so make sure\r\n\u003e you inject CommandBus or InlineCommandBus depending on that needs.\r\n\u003e\r\n## Command Consumer\r\n\r\nIf we have used an async adapter for enqueue all (or just some) commands, then\r\nwe should have a mechanism where to consume these commands. And good new are\r\nthat this bundle already have this consumer available and ready to use.\r\n\r\nAs consumed commands shouldn't be enqueued again unless something bad happens,\r\ninstead of using CommandBus we will use InlineCommandBus (remember, same \r\nconfiguration and async disabled).\r\n\r\nTo start consuming commands, just use console command. In order to take care\r\nabout memory consumption, you can make this consumer valid only during *n*\r\niterations. After these iterations, the consumer will die. Only valid if you\r\nhave a supervisor behind or similar, checking the number of instances running.\r\n\r\n```bash\r\nphp bin/console bus:consume-commands --limit 10\r\n```\r\n\r\nBy default, no limit.\r\n\r\n# ReactPHP functions\r\n\r\n[![CircleCI](https://circleci.com/gh/driftphp/reactphp-functions.svg?style=svg)](https://circleci.com/gh/driftphp/reactphp-functions)\r\n\r\nSet of simple PHP functions turned non-blocking on too of\r\n[ReactPHP](https://reactphp.org/)\r\n\r\n## Installation\r\n\r\nYou can install the package by using composer, or getting the \r\n[source code](https://github.com/driftphp/reactphp-functions) from Github.\r\n\r\n```bash\r\ncomposer require drift/react-functions\r\n```\r\n\r\n## Quickstart example\r\n\r\nYou can easily add a sleep in your non-blocking code by using these functions.\r\nIn this code you can wait 1 second before continuing with the execution of your\r\npromise, while the loop is actively switching between active Promises.\r\n\r\n```php\r\n    // use a unique event loop instance for all parallel operations\r\n    $loop = React\\EventLoop\\Factory::create();\r\n\r\n    $promise = $this\r\n        -\u003edoWhateverNonBlocking($loop)\r\n        -\u003ethen(function() use ($loop) {\r\n\r\n            // This will be executed on time t=0\r\n            return React\\sleep(1, $loop);\r\n        })\r\n        -\u003ethen(function() {\r\n            \r\n            // This will be executed on time t=1\r\n        });\r\n```\r\n\r\n## Usage\r\n\r\nThis lightweight library has some small methods with the exact behavior than\r\ntheir sibling methods in regular and blocking PHP.\r\n\r\n```php\r\nuse Drift\\React;\r\n\r\nReact\\sleep(...);\r\n```\r\n\r\n## EventLoop\r\n\r\nEach function is responsible for orchestrating the [EventLoop](https://github.com/reactphp/event-loop#usage)\r\nin order to make it run (block) until your conditions are fulfilled.\r\n\r\n```php\r\n$loop = React\\EventLoop\\Factory::create();\r\n```\r\n\r\n\u003e Under DriftPHP framework, you don't have to create a new EventLoop instance,\r\n\u003e but inject the one you have already built, named `@drift.event_loop`.\r\n\r\n## sleep\r\n\r\nThe `sleep($seconds, LoopInterface $loop)` method can be used to sleep for\r\n$time seconds.\r\n\r\n```php\r\nReact\\sleep(1.5, $loop)\r\n    -\u003ethen(function() {\r\n        // Do whatever you need after 1.5 seconds\r\n    });\r\n```\r\n\r\nIt is important to understand all the possible sleep implementations you can use\r\nunder this reactive programming environment.\r\n\r\n- `\\sleep($time)` - Block the PHP thread n seconds, and after this time is\r\nelapsed, continue from the same point\r\n- `\\Clue\\React\\Block\\sleep($time, $loop` - Don't block the PHP thread, but let\r\nthe loop continue doing cycles. Block the program execution after n seconds, and\r\nafter this time is elapsed, continue from the same point. This is a blocking\r\nfeature.\r\n- `\\Drift\\React\\sleep($time, $loop)` - Don't block neither the PHP thread nor\r\nthe program execution. This method returns a Promise that will be resolved after\r\nn seconds. This is a non-blocking feature.\r\n\r\n## usleep\r\n\r\nThe `sleep($seconds, LoopInterface $loop)` method can be used to sleep for\r\n$time microseconds.\r\n\r\n```php\r\nReact\\usleep(3000, $loop)\r\n    -\u003ethen(function() {\r\n        // Do whatever you need after 3000 microseconds\r\n    });\r\n```\r\n\r\nThe same rationale than the [`React\\sleep`](#sleep) method. This is a\r\nnon-blocking action.\r\n\r\n## mime_content_type\r\n\r\nThe `mime_content_type(\"/tmp/file.png\", LoopInterface $loop)` method can be used\r\nto guess the mime content type of a file. If failure, then rejects with a\r\nRuntimeException.\r\n\r\n```php\r\nReact\\mime_content_type(\"/tmp/file.png\", $loop)\r\n    -\u003ethen(function(string $type) {\r\n        // Do whatever you need with the found mime type\r\n    });\r\n```\r\n\r\nThis is a non-blocking action and equals the regular PHP function\r\n[mime_content_type](https://www.php.net/manual/en/function.mime-content-type.php).\r\n\r\n# Adapters\r\n\r\nIn order to be able to use DriftPHP and your whole domain in a non-blocking way,\r\nall your infrastructure operation must be done by using ReactPHP libraries. This\r\nframework, and taking the advantage of Symfony bundle architecture, offers you a\r\nset of components to make you configure these clients in a more easy and\r\nstructured way.\r\n\r\n## Redis adapter\r\n\r\n[![CircleCI](https://circleci.com/gh/driftphp/redis-bundle.svg?style=svg)](https://circleci.com/gh/driftphp/redis-bundle)\r\n\r\nThis is a simple adapter for Redis on top of ReactPHP and DriftPHP. Following\r\nthe same structure that is followed in the Symfony ecosystem, you can use this\r\npackage as a Bundle, only usable under DriftPHP Framework.\r\n\r\n### Installation\r\n\r\nYou can install the package by using composer, or getting the \r\n[source code](https://github.com/driftphp/redis-bundle) from Github.\r\n\r\n```bash\r\ncomposer require drift/redis-bundle\r\n```\r\n\r\n### Configure\r\n\r\nThis package will allow you to configure all your Redis async clients, taking\r\ncare of duplicity and the loop integration. Once your package is required by\r\ncomposer, add the bundle in the kernel and change your `services.yaml`\r\nconfiguration file to defined the clients\r\n\r\n```yaml\r\nredis:\r\n    clients:\r\n        users:\r\n            host: \"127.0.0.1\"\r\n            port: 6379\r\n            database: \"/users\"\r\n            password: \"secret\"\r\n            protocol: \"rediss://\"\r\n            idle: 0.5\r\n            timeout: 10.0\r\n        orders:\r\n            host: \"127.0.0.1\"\r\n            database: \"/orders\"\r\n```\r\n\r\nOnly host is required. All the other values are optional.\r\n\r\n### Usage\r\n\r\nOnce you have your clients created, you can inject them in your services by\r\nusing the name of the client in your dependency injection arguments array\r\n\r\n```yaml\r\na_service:\r\n    class: My\\Service\r\n    arguments:\r\n        - \"@redis.users_client\"\r\n        - \"@redis.orders_client\"\r\n```\r\n\r\nYou can use Autowiring as well in the bundle, by using the name of the client\r\nand using it as a named parameter\r\n\r\n```php\r\nuse Clue\\React\\Redis\\Client;\r\n\r\npublic function __construct(\r\n    Client $usersClient,\r\n    Client $ordersClient\r\n)\r\n```\r\n\r\n## Mysql adapter\r\n\r\n[![CircleCI](https://circleci.com/gh/driftphp/mysql-bundle.svg?style=svg)](https://circleci.com/gh/driftphp/mysql-bundle)\r\n\r\nThis is a simple adapter for Mysql on top of ReactPHP and DriftPHP. Following\r\nthe same structure that is followed in the Symfony ecosystem, you can use this\r\npackage as a Bundle, only usable under DriftPHP Framework.\r\n\r\n### Installation\r\n\r\nYou can install the package by using composer, or getting the \r\n[source code](https://github.com/driftphp/mysql-bundle) from Github.\r\n\r\n```bash\r\ncomposer require drift/mysql-bundle\r\n```\r\n\r\n### Configure\r\n\r\nThis package will allow you to configure all your Mysql async clients, taking\r\ncare of duplicity and the loop integration. Once your package is required by\r\ncomposer, add the bundle in the kernel and change your `services.yaml`\r\nconfiguration file to defined the connections\r\n\r\n```yaml\r\nmysql:\r\n    connections:\r\n        users:\r\n            host: \"127.0.0.1\"\r\n            port: 3306\r\n            user: \"root\"\r\n            password: \"root\"\r\n            database: \"users\"\r\n        orders:\r\n            host: \"127.0.0.1\"\r\n            user: \"root\"\r\n            password: \"root\"\r\n            database: \"orders\"\r\n```\r\n\r\nAll parameters are required, but the port. This one is `3306` by default.\r\n\r\n### Usage\r\n\r\nOnce you have your connections created, you can inject them in your services by\r\nusing the name of the connection in your dependency injection arguments array\r\n\r\n```yaml\r\na_service:\r\n    class: My\\Service\r\n    arguments:\r\n        - \"@mysql.users_connection\"\r\n        - \"@mysql.orders_connection\"\r\n```\r\n\r\nYou can use Autowiring as well in the bundle, by using the name of the \r\nconnection and using it as a named parameter\r\n\r\n```php\r\nuse React\\MySQL\\ConnectionInterface;\r\nuse React\\MySQL\\Io\\LazyConnection;\r\n\r\npublic function __construct(\r\n    ConnectionInterface $usersConnection,\r\n    LazyConnection $ordersConnection\r\n)\r\n```\r\n\r\n## PostgreSQL adapter\r\n\r\n[![CircleCI](https://circleci.com/gh/driftphp/postgresql-bundle.svg?style=svg)](https://circleci.com/gh/driftphp/postgresql-bundle)\r\n\r\nThis is a simple adapter for PostgreSQL on top of ReactPHP and DriftPHP. Following\r\nthe same structure that is followed in the Symfony ecosystem, you can use this\r\npackage as a Bundle, only usable under DriftPHP Framework.\r\n\r\n### Installation\r\n\r\nYou can install the package by using composer, or getting the \r\n[source code](https://github.com/driftphp/postgresql-bundle) from Github.\r\n\r\n```bash\r\ncomposer require drift/postgresql-bundle\r\n```\r\n\r\n### Configure\r\n\r\nThis package will allow you to configure all your PostgreSQL async clients, taking\r\ncare of duplicity and the loop integration. Once your package is required by\r\ncomposer, add the bundle in the kernel and change your `services.yaml`\r\nconfiguration file to defined the connections\r\n\r\n```yaml\r\npostgresql:\r\n    connections:\r\n        users:\r\n            host: \"127.0.0.1\"\r\n            port: 5432\r\n            user: \"root\"\r\n            password: \"root\"\r\n            database: \"users\"\r\n        orders:\r\n            host: \"127.0.0.1\"\r\n            user: \"root\"\r\n            password: \"root\"\r\n            database: \"orders\"\r\n```\r\n\r\nAll parameters are required, but the port. This one is `5432` by default.\r\n\r\n### Usage\r\n\r\nOnce you have your clients created, you can inject them in your services by\r\nusing the name of the connection in your dependency injection arguments array\r\n\r\n```yaml\r\na_service:\r\n    class: My\\Service\r\n    arguments:\r\n        - \"@postgresql.users_client\"\r\n        - \"@postgresql.orders_client\"\r\n```\r\n\r\nYou can use Autowiring as well in the bundle, by using the name of the \r\nconnection and using it as a named parameter\r\n\r\n```php\r\nuse PgAsync\\Client;\r\n\r\npublic function __construct(Client $client)\r\n```\r\n\r\nUnder the hood the bundle is a wrapper on top of [voryx/PgAsync](https://github.com/voryx/PgAsync) library. The detailed information about class `Client` you can find on the underlying library web-page.\r\n\r\n## Twig adapter\r\n\r\n[![CircleCI](https://circleci.com/gh/driftphp/twig-bundle.svg?style=svg)](https://circleci.com/gh/driftphp/twig-bundle)\r\n\r\nThis is a simple adapter for Twig on top of ReactPHP and DriftPHP. Following\r\nthe same structure that is followed in the Symfony ecosystem, you can use this\r\npackage as a Bundle, only usable under DriftPHP Framework.\r\n\r\nThis package will work for you as a simple bridge between your DriftPHP and Twig\r\nas a templating engine. Of course, the behavior will be exactly the same than\r\nworking on top of other frameworks, like Symfony, but some small changes have\r\nbeen introduced here in order to be non-blocking.\r\n\r\n### Installation\r\n\r\nYou can install the package by using composer, or getting the \r\n[source code](https://github.com/driftphp/twig-bundle) from Github.\r\n\r\n```bash\r\ncomposer require drift/twig-bundle\r\n```\r\n\r\n### Usage\r\n\r\nYou have two different ways of using Twig in DriftPHP. Both ways are pretty\r\nsimilar, but could be discussed about the grade of coupling that we want between\r\nour layers.\r\n\r\nThe first one is the regular one. We can inject Twig as a dependency, and having\r\nthe path of our template, we can easily render our view. You can check here a\r\nsmall example about this strategy. As you can see, your controller turns\r\ndependent on a view reference and on the complete Twig engine, which can make\r\nsense somehow.\r\n\r\n```php\r\n/**\r\n * Class ViewValuesController\r\n */\r\nclass ViewValuesController\r\n{\r\n    private $twig;\r\n\r\n    /**\r\n     * PutValueController constructor.\r\n     *\r\n     * @param Environment $twig\r\n     */\r\n    public function __construct(Environment $twig)\r\n    {\r\n        $this-\u003etwig = $twig;\r\n    }\r\n\r\n    /**\r\n     * Invoke\r\n     *\r\n     * @param Request $request\r\n     *\r\n     * @return Response\r\n     */\r\n    public function __invoke(Request $request)\r\n    {\r\n        $template = $this\r\n            -\u003etwig\r\n            -\u003eload('redis/view_values.twig');\r\n        $values = ['drift', 'php'];\r\n\r\n        return new Response(\r\n            $template-\u003erender([\r\n                'values' =\u003e $values\r\n            ]),\r\n            200\r\n        );\r\n    }\r\n}\r\n```\r\n\r\nOn the other hand, you can make your controller **only** a Twig coupled part by\r\nimplementing a small interface, which will force you defining this template path\r\nwithout having to inject the Environment. In that case, you can return an array\r\nand the bundle will render properly the template.\r\n\r\n```php\r\nuse Drift\\Twig\\Controller\\RenderableController;\r\n\r\n/**\r\n * Class ViewValueController\r\n */\r\nclass ViewValueController implements RenderableController\r\n{\r\n    /**\r\n     * Invoke\r\n     *\r\n     * @param Request $request\r\n     *\r\n     * @return array\r\n     */\r\n    public function __invoke(Request $request) : array\r\n    {\r\n        return [\r\n            'key' =\u003e 'value'\r\n        ];\r\n    }\r\n\r\n    /**\r\n     * Get render template\r\n     *\r\n     * @return string\r\n     */\r\n    public static function getTemplatePath(): string\r\n    {\r\n        return 'redis/view_value.twig';\r\n    }\r\n}\r\n```\r\n\r\nIf you follow that strategy, then you will be able to return promises instead of\r\nsimple values. In that case, the bundle will wait until all promises are\r\nproperly fulfilled and will render the template.\r\n\r\n```php\r\nuse Drift\\Twig\\Controller\\RenderableController;\r\n\r\n/**\r\n * Class ViewValueController\r\n */\r\nclass ViewValueController implements RenderableController\r\n{\r\n    /**\r\n     * Invoke\r\n     *\r\n     * @param Request $request\r\n     *\r\n     * @return array\r\n     */\r\n    public function __invoke(Request $request) : array\r\n    {\r\n        return [\r\n            'key' =\u003e (new FulfilledPromise())\r\n                -\u003ethen(function() {\r\n                    return 'value';\r\n                })\r\n        ];\r\n    }\r\n\r\n    /**\r\n     * Get render template\r\n     *\r\n     * @return string\r\n     */\r\n    public static function getTemplatePath(): string\r\n    {\r\n        return 'redis/view_value.twig';\r\n    }\r\n}\r\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdriftphp%2Fdocs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdriftphp%2Fdocs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdriftphp%2Fdocs/lists"}