{"id":21057806,"url":"https://github.com/driftphp/demo","last_synced_at":"2025-10-08T02:53:23.663Z","repository":{"id":36312920,"uuid":"223241806","full_name":"driftphp/demo","owner":"driftphp","description":"Demo for DriftPHP","archived":false,"fork":false,"pushed_at":"2023-02-01T23:50:16.000Z","size":4332,"stargazers_count":133,"open_issues_count":7,"forks_count":11,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-03T17:23:22.904Z","etag":null,"topics":["demo","driftphp","reactphp","redis"],"latest_commit_sha":null,"homepage":"https://driftphp.io","language":"CSS","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}},"created_at":"2019-11-21T18:49:15.000Z","updated_at":"2023-10-20T00:51:27.000Z","dependencies_parsed_at":"2023-01-17T00:46:05.493Z","dependency_job_id":null,"html_url":"https://github.com/driftphp/demo","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%2Fdemo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/driftphp%2Fdemo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/driftphp%2Fdemo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/driftphp%2Fdemo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/driftphp","download_url":"https://codeload.github.com/driftphp/demo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254442429,"owners_count":22071864,"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":["demo","driftphp","reactphp","redis"],"created_at":"2024-11-19T17:04:47.644Z","updated_at":"2025-10-08T02:53:18.630Z","avatar_url":"https://github.com/driftphp.png","language":"CSS","readme":"# DriftPHP Demo\n\nThis is a simple Demo about how to use properly DriftPHP. Please, read carefully\nour [Documentation](https://driftphp.io) in order to understand the rationale\nbehind each functionality. Built on top of **PHP8**.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"public/driftphp.png\"\u003e\n\u003c/p\u003e\n\n\u003e Remember. This is a demo package. We encourage to make us PRs if you find some\n\u003e bugs, but we're not going to add new features unless is necessary.\n\nThis demo uses these DriftPHP packages\n- Server\n- Http Kernel\n- Command Bus + AMQP Adapter\n- Event Bus + AMQP Adapter\n- Websocket\n- Twig Adapter\n- DBAL\n\n\u003e This demo supports PHP8. We only made 2 changes to make this happen. First of\n\u003e all, new DriftPHP projects that want to change to PHP8 should start using the\n\u003e php8 Docker image - driftphp/base-php8 -. After that, and because some\n\u003e dependencies don't have their updates to accept the new PHP version, you\n\u003e should install/update your composer dependencies with the flag\n\u003e `--ignore-platform-reqs`. With these 2 changes, we want to welcome PHP8 with a\n\u003e big smile on our faces :)\n\n## Help US :heart:\n\nDid you enjoy this demo? Do you think that this DriftPHP Framework deserves an\nopportunity inside the ecosystem? Then easy. You can help us by making some\nsmall actions\n\n- Make a tweet with hashtag #driftPHP. Tell the world what did you see here\n- :star: our used components by doing just `composer thanks`\n\nYou can join our small community in \n[DriftPHP in Gitter](https://gitter.im/driftphp/community) if you have any\nissue or question around this project. We will be very happy to say hello :)\n  \n## Description\n\nThis project is a sample for integrating Symfony and ReactPHP. The base of the\nproject is a simple and clean DriftPHP installation using `drift/skeleton`, so\nthat means that, if your code is built on top of that structure, you're a little\nbit nearer than turning your project non-blocking.\n\nIn this case, we have build an HTTP key/value service, allowing us to store\nstring values under some keys, get that values and delete them. As simple as it\nsounds. We will use Twig as well to render some small templates with some of\nthis stored information.\n\n```yml\n/ GET\n/values/{key} GET\n/values/{key} PUT\n/values/{key} DELETE\n```\n\nWe could have implemented the feature by using a regular DriftPHP installation,\nby installing an Nginx and by using one of the existing Redis solutions for PHP.\nThat would have been the fastest and simplest solution, but we want a little bit\nmore.\n\n- We want simplicity. No more Nginx for a simple server\n- We want performance. Each ms matters. Really.\n- We want a non-blocking implementation. If we have one long query, we don't\n  want the server to be blocked\n  \nThese are the prerequisites for that project, and by using this new\nrepositories, we can have everything you need here in one single server.\n\nLet's check how.\n\n## Installation - Docker Compose\n\nYou can start using this demo by using `docker-compose`. As easy as it sounds.\n\n```bash\n./docker-compose-build.sh\n```\n\nOnce the whole environment is up and running, you'll be able to request the\nserver by accessing the port `8999` through your browser.\n\n```bash\nhttp://127.0.0.1:8999/\n```\n\n\u003e You can change the forwarded port by changing the values from the .env file  \n\u003e\n\u003e SERVER_PORT=8999\n\u003e WEBSOCKET_PORT=1234\n\nYou might encounter some troubles. Since this docker-compose definition file\nworks with some external images, some pieces could not work as expected due to\nsome cache problems. Make sure that you run this demo from the scratch and not\nfrom cached containers and volumes.\n\n## The architecture\n\nPlease. Keep reading a bit more in order to understand how this application is\ndesigned in terms of architecture. First of all, let's take a look at our\ndesired achievements.\n\n- We basically want a service that saves key-value pairs in a persistent\ndatabase.\n- We want to expose a basic API Rest (PUT, DELETE, GET) in order to be able to\ninteract with this persistence layer.\n- We assume that the whole key-value data-set will be small enough to be stored\nin memory, so we will design out application on top of events. Read from memory,\nwrite to persistence layer.\n- We want to expose a websocket where we will send some events. For now, each\ntime a key is added/updated or deleted, we will create a new domain event.\n- Other websocket connections and disconnections will produce events as well.\n- We want a landing page with real-time updated key-value database and domain\nevents.\n\nBefore continuing, take the next 2 minutes thinking how would you implement this\narchitecture using Laravel or Symfony. First of all, in these frameworks you\nsimply can't use memory as a local storage, so each read means a persistence\nhit (yes, Redis is persistence as well). Working with a distributed set of\nservices and websockets would need external software pieces in order to work,\nbasically because Symfony (and all other frameworks) can't do more than one\nthing at the same time.\n\nLet's try with DriftPHP. In order to be able to do that, we will work with all\nthese containers, using the same networks and interacting among each other.\n\n- RabbitMQ as a queues and exchanges system\n- PostgreSQL as a persistence layer used as a driver in our DBAL configuration\n\n- Tiny balancer, listening at port 8999 and balancing among next servers\n- Server1 - DriftPHP server serving HTTP requests and subscribed to a temporary.\n  Configured to Enqueue commands asynchronously\n  exchange for domain events\n- Server2 - Same than Server1\n- Server3 - Same than Server1\n\n- Websocket - Exposes one websocket route in `/events` and dispatches all domain\n  events in a documented format\n  \nSo let's take a look at each action\n\n### Put a value\n\nYou can put a value using the exposed api REST.\n\n```bash\ncurl -i -XPUT -H 'Content-Type: application/json' localhost:8999/values/mykey -d'myvalue'\n\nHTTP/1.1 200 OK\ncache-control: no-cache, private\ndate: Thu, 09 May 2019 18:44:03 GMT\ncontent-type: application/json\nX-Powered-By: React/alpha\nContent-Length: 48\nConnection: close\n\n{\"key\":\"mykey\",\"value\":\"myvalue\",\"message\":\"OK\"}\n```\n\nAs you can see, you're using the port `8999`, what means that the balancer will\nforward your request to one of the existing servers. This server will handle the\nrequest and will execute a new Command through the command bus. Because the\ncommand bus is configured to work asynchronously, instead of handling the\ncommand, a middleware will enqueue it in a infrastructure queue (RabbitMQ using\nAMQP protocol).\n\nThe command consumer will consume the command and will execute again the command\nthrough the command bus, but in this case in the inline command bus (this one\njust avoid the async middleware). That means that, in this case, the handler\nwill handle the command and will update our persistence layer (PostgreSQL) with\nthe new value.\n\nAfter that, the handler will create a new domain event and this one will be\ndispatched using the event bus. That means that all other network services\nsubscribed to this queue will receive this domain event.\n\nIn fact, our 3 servers and the websocket will receive this domain event,\nallowing them all to reload a fresh copy of our data in memory (all key-values).\nEach websocket connection will receive as well an update related to this domain\nevent, updating the local and displayed lists.\n\n### Delete a value\n\nIn that case, we want to delete one of our values.\n\n```bash\ncurl -i -XDELETE localhost:8999/values/mykey\n\nHTTP/1.1 200 OK\ncache-control: no-cache, private\ndate: Thu, 09 May 2019 18:47:03 GMT\ncontent-type: application/json\nX-Powered-By: React/alpha\nContent-Length: 29\nConnection: close\n\n{\"key\":\"mykey\",\"message\":\"1\"}\n```\n\nThe workflow is exactly the same one than the *PUT* action. Both work exactly\nthe same way.\n\n### Get a value\n\nWe can use the api REST to request one key's value. If the key exists then we\nwill have the value.\n\n```bash\ncurl -i -XGET localhost:8999/values/mykey\n\nHTTP/1.1 200 OK\ncache-control: no-cache, private\ndate: Thu, 09 May 2019 18:45:17 GMT\ncontent-type: application/json\nX-Powered-By: React/alpha\nContent-Length: 33\nConnection: close\n\n{\"key\":\"mykey\",\"value\":\"myvalue\"}\n```\n\nOtherwise, we will have a 404.\n\n```bash\ncurl -i -XGET localhost:8999/values/nonexistingkey\n\nHTTP/1.1 404 Not Found\ncache-control: no-cache, private\ndate: Sat, 25 Jan 2020 17:23:33 GMT\ncontent-type: text/html; charset=UTF-8\nX-Powered-By: React/alpha\nContent-Length: 0\nConnection: close\n```\n\nThanks to last workflows, reading a value is as simple as you will see here.\nOur request is handled by one of our servers (remember, the balancer forwards\nit to one of our defined servers). The server creates a new Query object\nrequesting a value given a key, and the handler uses the configured repository.\n\nRepositories save an updated list of key-values, keeping it fresh by using the\ndomain events received through the event subscriber, so we could simply accept\nthat yes, these values are properly updated!\n\nJust check if the key is in the local array and return the value.\nThat's it. Without any persistence layer access, without having bottlenecks.\n\n### Visit 127.0.0.1:8999/\n\nYou can open your browser to `http://127.0.0.1:8999` and you will be see 2\nlists. The first one will be the current state of the website. These values are\nnot taken from the persistence layer, but from one fresh copy allocated in one\nof our servers memory.\n\nThe other list is real-time updated list of domain events and websocket events\nfrom our system. These are the current events notified here.\n\n- A new key-value has been put\n- A key has been deleted\n- A connection has been connected to the route /events\n- A connection has been disconnected from the route /events\n\nAs soon as our persistence layer changes, both lists will change (the first one\nby applying some deltas, the second by appending new domain events).\n\nYou can open multiple times this endpoint at the same time and play with the\nconcurrency.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdriftphp%2Fdemo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdriftphp%2Fdemo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdriftphp%2Fdemo/lists"}