DEV Community

Cover image for PHP + Actors == Dapr
Rob Landers
Rob Landers

Posted on

PHP + Actors == Dapr

Disclosure: I'm the PHP SDK maintainer

PHP and actors?

wait-what.gif

Dapr is a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks.

Dapr (Distributed Application Runtime) supports PHP, and in my humble opinion, actors are the coolest part about Dapr. Dapr implements the virtual actor pattern. The virtual actor pattern essentially (and this is greatly simplified!) means that any instance of an actor will be "single-threaded" and doesn't exist until it's called. The single-threaded bit sounds exactly like what I'm used to with PHP, and the "doesn't exist until it's called" sounds like something I'd read in the PHP docs. 🤣

OK, it's a bit more than that, and I highly suggest reading Dapr's documentation about it to get a better overview. If you're curious what these actors look like in PHP, stick around.

Setting up the project

With the php-sdk, you create a new project with composer init and run composer require dapr/php-sdk to install it.

For the project, you'll create a simple actor that counts and call it Count, with two methods: a method to get the current count and increment the count by a given amount.

State and stuff

The actor's state is pretty straightforward. Check it out:

As you can see, the state has a single key: $count that is an integer with a default value of 0. You only have to extend the ActorState abstract class to use it in an actor (to provide a transaction). That's all you need!

Creating the interface

If you want to use a PHP client, you'll need to define an interface for the actor:

You can see the two methods and an attribute. The #[DaprType()] attribute specifies what the name of the actor is according to Dapr, which need not match what we call it in our code.

Implementing the interface

And now, to implement the interface, it's as simple as you'd expect:

Here you can see the familiar #[DaprType()] attribute, a constructor that takes the actor's id and its private state. From there, the actual implementation is straightforward as we're returning the state and/or incrementing it.

Testing

Of course, the code only looks simple. We should verify that there aren't any shenanigans and it works exactly as you expect. To do that, install phpunit with composer require --dev phpunit/phpunit and write up some unit tests:

None of the underlying framework is initialized for unit tests, so you can assert that given a starting state, the resulting state is what you expect.

You can now test this with phpunit, which you can add as a script so that testing is as easy as composer test.

Tying it all together

Now that there's an actor implementation, the runtime needs to be configured and initialized. To configure the runtime, create a config.php file:

You can find all the supported keys in the docs too.

There's a relatively simple and opinionated runtime provided out-of-the-box that uses PHP-DI. However, you can bring your own DI container, router, or an entire framework.

Time to initialize the runtime:

Here you can see a simple error_log() that outputs the current URI and then the App::create() call where we configure the DI container.

Finally, $app->start() is called, and the built-in routes are handled.

Making a client

Of course, this is no fun if you can't remotely call the service. So, using psysh, a repl can be provided:

Running it

The server

The client

Errata

The code in this post lives here:

Simple Dapr Actor Example

A simple actor that keeps track of a count.

Running the server

$ PHP_CLI_SERVER_WORKERS=10 dapr run --app-id example --app-port 3000 --dapr-http-port 3500 -- php -S 0.0.0.0:3000 src/index.php
Enter fullscreen mode Exit fullscreen mode

Running the repl

$ composer repl
>>>  $counter = $app->run(fn(Dapr\Actors\ActorProxy $proxy) => $proxy->get(Example\Interfaces\CountInterface::class, 'an id'))
=> Dapr\Proxies\dapr_proxy_Count {#2770
     +id: "an id",
     +DAPR_TYPE: "Count",
   }
>>> $counter->getCount()
=> 0
>>> $counter->incrementAndGet(13)
=> 13
>>> $counter->incrementAndGet(13)
=> 26
>>> $counter->incrementAndGet(13)
=> 39
>>> $counter->getCount();
=> 39



You can even fork and fiddle with the unit tests on the php sandbox.

Top comments (0)