<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rob Landers</title>
    <description>The latest articles on Forem by Rob Landers (@withinboredom).</description>
    <link>https://forem.com/withinboredom</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F586443%2F0bb73dfe-5359-4ce0-8d7f-259eb6c03e3d.jpeg</url>
      <title>Forem: Rob Landers</title>
      <link>https://forem.com/withinboredom</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/withinboredom"/>
    <language>en</language>
    <item>
      <title>PHP + Actors == Dapr</title>
      <dc:creator>Rob Landers</dc:creator>
      <pubDate>Fri, 26 Feb 2021 20:33:12 +0000</pubDate>
      <link>https://forem.com/withinboredom/php-actors-dapr-39h1</link>
      <guid>https://forem.com/withinboredom/php-actors-dapr-39h1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclosure: I'm the PHP SDK maintainer&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;PHP and actors?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Su7Bo9MMnyl0ZE263z/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Su7Bo9MMnyl0ZE263z/giphy.gif" alt="wait-what.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dapr (&lt;strong&gt;D&lt;/strong&gt;istributed &lt;strong&gt;Ap&lt;/strong&gt;plication &lt;strong&gt;R&lt;/strong&gt;untime) supports PHP, and in my humble opinion, actors are the coolest part about Dapr. Dapr implements the &lt;a href="https://www.microsoft.com/en-us/research/project/orleans-virtual-actors/" rel="noopener noreferrer"&gt;virtual actor pattern&lt;/a&gt;. 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. 🤣&lt;/p&gt;

&lt;p&gt;OK, it's a bit more than that, and I highly suggest reading Dapr's &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/actors/actors-overview/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; about it to get a better overview. If you're curious what these actors look like in PHP, stick around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the project
&lt;/h2&gt;

&lt;p&gt;With the &lt;a href="https://github.com/dapr/php-sdk" rel="noopener noreferrer"&gt;php-sdk&lt;/a&gt;, you create a new project with &lt;code&gt;composer init&lt;/code&gt; and run &lt;code&gt;composer require dapr/php-sdk&lt;/code&gt; to install it.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  State and stuff
&lt;/h2&gt;

&lt;p&gt;The actor's state is pretty straightforward. Check it out:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


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

&lt;h2&gt;
  
  
  Creating the interface
&lt;/h2&gt;

&lt;p&gt;If you want to use a PHP client, you'll need to define an interface for the actor:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


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

&lt;h2&gt;
  
  
  Implementing the interface
&lt;/h2&gt;

&lt;p&gt;And now, to implement the interface, it's as simple as you'd expect:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here you can see the familiar &lt;code&gt;#[DaprType()]&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;phpunit&lt;/code&gt; with &lt;code&gt;composer require --dev phpunit/phpunit&lt;/code&gt; and write up some unit tests:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;You can now test this with &lt;code&gt;phpunit&lt;/code&gt;, which you can add as a script so that testing is as easy as &lt;code&gt;composer test&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tying it all together
&lt;/h2&gt;

&lt;p&gt;Now that there's an actor implementation, the runtime needs to be configured and initialized. To configure the runtime, create a &lt;code&gt;config.php&lt;/code&gt; file:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;small&gt;You can find all the supported keys &lt;a href="https://docs.dapr.io/developing-applications/sdks/php/#configure-your-service" rel="noopener noreferrer"&gt;in the docs too&lt;/a&gt;.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;There's a relatively simple and opinionated runtime provided out-of-the-box that uses &lt;a href="https://php-di.org/" rel="noopener noreferrer"&gt;PHP-DI&lt;/a&gt;. However, you can bring your own DI container, router, or an entire framework.&lt;/p&gt;

&lt;p&gt;Time to initialize the runtime:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here you can see a simple &lt;code&gt;error_log()&lt;/code&gt; that outputs the current URI and then the &lt;code&gt;App::create()&lt;/code&gt; call where we configure the DI container.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;$app-&amp;gt;start()&lt;/code&gt; is called, and the built-in routes are handled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a client
&lt;/h2&gt;

&lt;p&gt;Of course, this is no fun if you can't remotely call the service. So, using &lt;code&gt;psysh&lt;/code&gt;, a repl can be provided:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The server
&lt;/h3&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  The client
&lt;/h3&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Errata
&lt;/h2&gt;

&lt;p&gt;The code in this post lives here:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/withinboredom" rel="noopener noreferrer"&gt;
        withinboredom
      &lt;/a&gt; / &lt;a href="https://github.com/withinboredom/example-actors" rel="noopener noreferrer"&gt;
        example-actors
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Simple Dapr Actor Example&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;A simple actor that keeps track of a count.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Running the server&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ 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&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Running the repl&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;$ composer repl
&amp;gt;&amp;gt;&amp;gt;  $counter = $app-&amp;gt;run(fn(Dapr\Actors\ActorProxy $proxy) =&amp;gt; $proxy-&amp;gt;get(Example\Interfaces\CountInterface::class, 'an id'))
=&amp;gt; Dapr\Proxies\dapr_proxy_Count {#2770
     +id: "an id",
     +DAPR_TYPE: "Count",
   }
&amp;gt;&amp;gt;&amp;gt; $counter-&amp;gt;getCount()
=&amp;gt; 0
&amp;gt;&amp;gt;&amp;gt; $counter-&amp;gt;incrementAndGet(13)
=&amp;gt; 13
&amp;gt;&amp;gt;&amp;gt; $counter-&amp;gt;incrementAndGet(13)
=&amp;gt; 26
&amp;gt;&amp;gt;&amp;gt; $counter-&amp;gt;incrementAndGet(13)
=&amp;gt; 39
&amp;gt;&amp;gt;&amp;gt; $counter-&amp;gt;getCount();
=&amp;gt; 39
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/withinboredom/example-actors" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;You can even fork and fiddle with the unit tests on the &lt;a href="https://phpsandbox.io/n/srjds" rel="noopener noreferrer"&gt;php sandbox&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>actors</category>
      <category>dapr</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
