{"id":15672676,"url":"https://github.com/ytake/phluxor","last_synced_at":"2025-04-14T13:12:33.874Z","repository":{"id":243262396,"uuid":"811823669","full_name":"ytake/phluxor","owner":"ytake","description":"A toolkit for flexible actor models in PHP, empowering the PHP ecosystem","archived":false,"fork":false,"pushed_at":"2025-01-07T05:48:49.000Z","size":394,"stargazers_count":33,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-07T00:32:10.525Z","etag":null,"topics":["actor-model","actorsystem","php","swoole"],"latest_commit_sha":null,"homepage":"https://phluxor.github.io/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ytake.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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},"funding":{"github":"ytake","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2024-06-07T11:26:08.000Z","updated_at":"2025-02-10T23:46:32.000Z","dependencies_parsed_at":"2024-06-07T17:41:07.233Z","dependency_job_id":"34ee02ba-22e1-4a6d-a41b-765a471b90a5","html_url":"https://github.com/ytake/phluxor","commit_stats":{"total_commits":60,"total_committers":1,"mean_commits":60.0,"dds":0.0,"last_synced_commit":"f60607fc119160fb933e4a656080c442b915c54d"},"previous_names":["ytake/phluxor"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ytake%2Fphluxor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ytake%2Fphluxor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ytake%2Fphluxor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ytake%2Fphluxor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ytake","download_url":"https://codeload.github.com/ytake/phluxor/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248886325,"owners_count":21177644,"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":["actor-model","actorsystem","php","swoole"],"created_at":"2024-10-03T15:29:59.637Z","updated_at":"2025-04-14T13:12:33.744Z","avatar_url":"https://github.com/ytake.png","language":"PHP","funding_links":["https://github.com/sponsors/ytake"],"categories":[],"sub_categories":[],"readme":"# Phluxor\n\nA toolkit for flexible actor models in PHP, empowering the PHP ecosystem.\n\nrequires PHP 8.3 and swoole.\n\nand Protocol Buffers for message serialization. / not supported other serialization formats yet.\n\nDocumentation is under preparation.\n\ndo not use this in production yet.\n\n[sample web application / phluxor-http-application-samples](https://github.com/ytake/phluxor-http-application-samples)  \n[demonstrate the use of Phluxor](https://github.com/ytake/phluxor-example)  \n[demonstrates how to use Phluxor with Event Sourcing and CQRS.](https://github.com/ytake/phluxor-example-event-sourcing-cqrs)  \n[Documentation](https://phluxor.github.io/en/guide/)\n\nPhluxorはPHPでのアクターモデルを実現するためのツールキットです。    \nPHP 8.3 と swooleが必要です。  \nメッセージのシリアライズには、Protocol Buffersを利用します。  \n他のシリアライズフォーマットはまだサポートされていません。\n\n```mermaid\nsequenceDiagram\n    participant A as Actor A\n    participant B as Actor B\n    participant C as Actor C\n\n    A-\u003e\u003eB: Message(Data X)\n    Note over B: B processes the received message, updates internal state,\u003cbr\u003e and prepares the next message\n    B-\u003e\u003eC: Message(Data Y)\n    Note over C: C processes the message from B.\u003cbr\u003eMeanwhile, A or other actors can progress with separate tasks.\n\n    par Concurrent Execution\n        A-\u003e\u003eC: Another Message(Data Z)\n        C-\u003e\u003eB: Result Message(Computed W)\n    and\n        B-\u003e\u003eA: State Report(State S)\n    end\n```\n\n## Installation\n\n```bash\n$ composer require phluxor/phluxor\n```\n\n## already implemented\n\n- actor model\n- actor lifecycle\n- supervision\n- actor registry\n- actor messaging\n- become/unbecome\n- mailbox / dispatcher\n- event stream\n- future\n- persistent actors (in memory / mysql)\n- [OpenTelemetry](https://opentelemetry.io/docs/languages/php/) support (metrics) \n- router / round-robin, broadcast, scatter-gather, etc.\n- [remoting](https://github.com/ytake/phluxor-remote) / Remoting is the mechanism by which Actors on different nodes talk to each other internally.  \n\n## work in progress\n\n- open telemetry support (tracing)\n- virtual actors / cluster support\n\n## Supervision\n\nexception handling is done by the actor system, and the actor can be supervised by parent , root actors.\n\n- `OneForOneStrategy`\n- `AllForOneStrategy`\n- `ExponentialBackoffStrategy`\n- `RestartStrategy`\n\n## Easy to use\n\nlike a akka http, you can use it with a simple API.  \nuse mezzio / mezzio-swoole / phluxor.  \nand you can use it with swoole / open swoole.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nnamespace App\\ActorSystem;\n\nuse App\\Message\\Add;\nuse App\\Message\\Cancel;\nuse App\\Message\\Event;\nuse App\\Message\\GetEvent;\nuse Phluxor\\ActorSystem\\Context\\ContextInterface;\nuse Phluxor\\ActorSystem\\Message\\ActorInterface;\n\nclass TicketSeller implements ActorInterface\n{\n    private int $tickets = 0;\n    private string $name = '';\n    private string $id   = '';\n\n    public function receive(ContextInterface $context): void\n    {\n        $msg = $context-\u003emessage();\n        switch (true) {\n            case $msg instanceof Add:\n                // change actor state\n                $this-\u003ename    = $msg-\u003ename;\n                $this-\u003etickets = $msg-\u003etickets;\n                $this-\u003eid      = $context-\u003eself()?-\u003eprotobufPid()-\u003egetId();\n                break;\n            case $msg instanceof GetEvent:\n                $context-\u003erequestWithCustomSender(\n                    $context-\u003esender(),\n                    new Event($this-\u003ename, $this-\u003etickets),\n                    $context-\u003eparent()\n                );\n                break;\n            case $msg instanceof Cancel:\n                $context-\u003erequestWithCustomSender(\n                    $context-\u003esender(),\n                    new Cancel(),\n                    $context-\u003eparent()\n                );\n                $context-\u003epoison($context-\u003eself());\n                break;\n        }\n    }\n}\n```\n\nsend message to actor.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nnamespace App\\ActorSystem;\n\nuse App\\Event\\EventCreated;\nuse App\\Message\\EventDescription;\nuse App\\Message\\EventExists;\nuse Phluxor\\ActorSystem\\Context\\ContextInterface;\nuse Phluxor\\ActorSystem\\Exception\\SpawnErrorException;\nuse Phluxor\\ActorSystem\\Message\\ActorInterface;\nuse Phluxor\\ActorSystem\\Props;\nuse Swoole\\Coroutine\\WaitGroup;\n\nuse function array_merge;\nuse function sprintf;\n\nclass BoxOffice implements ActorInterface\n{\n    public function receive(ContextInterface $context): void\n    {\n        $msg = $context-\u003emessage();\n        switch (true) {\n            case $msg instanceof EventDescription:\n                try {\n                    $result = $context-\u003espawnNamed(\n                        Props::fromProducer(fn() =\u003e new TicketSeller()),\n                        $msg-\u003ename\n                    );\n                    $context-\u003esend($result-\u003egetRef(), new Add($msg-\u003ename, $msg-\u003etickets));\n                    $context-\u003erespond(new EventCreated($msg-\u003ename, $msg-\u003etickets));\n                } catch (SpawnErrorException $e) {\n                    $context-\u003erespond(new EventExists());\n                }\n                break;\n            }\n        }\n    }\n}\n```\n\n## Become/Unbecome\n\nan actor can change its behavior by `become` and `unbecome` methods.\n\nexample:\n\none, other are the behavior methods.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nnamespace Acme;\n\nuse Phluxor\\ActorSystem\\Behavior;\nuse Phluxor\\ActorSystem\\Context\\ContextInterface;\nuse Phluxor\\ActorSystem\\Message\\ActorInterface;\nuse Phluxor\\ActorSystem\\Message\\ReceiveFunction;\n\nclass EchoSetBehaviorActor implements ActorInterface\n{\n    private Behavior $behavior;\n\n    public function __construct()\n    {\n        $this-\u003ebehavior = new Behavior();\n        $this-\u003ebehavior-\u003ebecome(\n            new ReceiveFunction(\n                fn(ContextInterface $context) =\u003e $this-\u003eone($context)\n            )\n        );\n    }\n\n    public function receive(ContextInterface $context): void\n    {\n        $this-\u003ebehavior-\u003ereceive($context);\n    }\n\n    public function one(ContextInterface $context): void\n    {\n        if ($context-\u003emessage() instanceof BehaviorMessage) {\n            $this-\u003ebehavior-\u003ebecome(\n                new ReceiveFunction(\n                    fn(ContextInterface $context) =\u003e $this-\u003eother($context)\n                )\n            );\n        }\n    }\n\n    public function other(ContextInterface $context): void\n    {\n        if ($context-\u003emessage() instanceof EchoRequest) {\n            $context-\u003erespond(new EchoResponse());\n        }\n    }\n}\n```\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse Acme\\BehaviorMessage;\nuse Acme\\EchoSetBehaviorActor;\nuse Acme\\EchoRequest;\nuse Phluxor\\ActorSystem;\nuse Phluxor\\ActorSystem\\ActorContext;\nuse Phluxor\\ActorSystem\\Props;\n\nuse function Swoole\\Coroutine\\go;\nuse function Swoole\\Coroutine\\run;\n\nfunction main(): void \n{\n    run(function () {\n        $system = ActorSystem::create();\n        go(function (ActorSystem $system) {\n            $pid = $system-\u003eroot()-\u003espawn(\n                Props::fromProducer(\n                    fn() =\u003e new EchoSetBehaviorActor()\n                )\n            );\n            $system-\u003eroot()-\u003esend($pid, new BehaviorMessage());\n            $future = $system-\u003eroot()-\u003erequestFuture($pid, new EchoRequest(), 1);\n            var_dump($future-\u003eresult());\n            $system-\u003eroot()-\u003estop($pid);\n        }, $system);\n    });\n}\n\n```\n\n## Persistent Actors\n\nパーシステンスアクターは、以前の状態から復元できるアクターです。  \n永続化に利用するデータベースは、お使いの環境などに合わせて自由にアダプターを実装することができます。  \nデフォルトではインメモリとMySQLが実装されています。  \nメッセージのシリアライズには、Protocol Buffersを利用しますので、事前にProtocol Buffersのファイルを用意しておく必要があります。\n以下はサンプルのProtocol Buffersファイルです。\n\npersistent actors are actors that can be restored from the previous state.  \nyou can implement an adapter freely according to your environment for the database used for persistence.    \nby default, in-memory and MySQL are implemented.  \nProtocol Buffers is used for message serialization, so you need to prepare a Protocol Buffers file in advance.    \n\nfor example, the protocol buffers file is like this.\n\n```protobuf\nsyntax = \"proto3\";\n\npackage Acme.Persistence.ProtoBuf;\n\noption php_namespace = \"Acme\\\\Persistence\\\\ProtoBuf\";\noption php_metadata_namespace = \"Acme\\\\Metadata\";\n\nmessage Message {\n  string message = 1;\n}\n\nmessage Snapshot {\n  string message = 1;\n}\n\n```\n\n詳しい永続化の使い方は、永続アクターのサンプルを参照してください。[Persistence / MySQL](example/persistence/SampleSystem.php)  \n\nfor example, the persistent actor is like this. [Persistence / MySQL](example/persistence/SampleSystem.php)\n\nuse `Phluxor\\Persistence\\Mixin` trait and implement `Phluxor\\Persistence\\PersistentInterface`.\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nnamespace Acme\\Persistence;\n\nuse Phluxor\\ActorSystem\\Context\\ContextInterface;\nuse Phluxor\\ActorSystem\\Message\\ActorInterface;\nuse Phluxor\\Persistence\\Message\\RequestSnapshot;\nuse Phluxor\\Persistence\\Mixin;\nuse Phluxor\\Persistence\\PersistentInterface;\nuse Acme\\Persistence\\ProtoBuf\\Message;\nuse Acme\\Persistence\\ProtoBuf\\Snapshot;\n\nclass InMemoryTestActor implements ActorInterface, PersistentInterface\n{\n    use Mixin;\n\n    private string $state = '';\n\n    public function receive(ContextInterface $context): void\n    {\n        $msg = $context-\u003emessage();\n        switch (true) {\n            case $msg instanceof RequestSnapshot:\n                $this-\u003epersistenceSnapshot(new TestSnapshot(['message' =\u003e $this-\u003estate]));\n                break;\n            case $msg instanceof TestSnapshot:\n                $this-\u003estate = $msg-\u003egetMessage();\n                break;\n            case $msg instanceof TestMessage:\n                if (!$this-\u003erecovering()) {\n                    $this-\u003epersistenceReceive($msg);\n                }\n                $this-\u003estate = $msg-\u003egetMessage();\n                break;\n            case $msg instanceof Query:\n                $context-\u003erespond($this-\u003estate);\n                break;\n        }\n    }\n}\n```\n\nmore examples are in the [Persistence Tests](tests/Persistence) directory.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fytake%2Fphluxor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fytake%2Fphluxor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fytake%2Fphluxor/lists"}