Archive for the 'Programming' category

Buffered vs. unbuffered channels in Golang

published on December 25, 2017.

As any newcomer to Golang and it’s ecosystem, I was eager to find out what is this hubbub about these things called goroutines and channels. I read the documentation and blog posts, watched videos, tried out some of the “hello world” examples and even wasted a couple of days trying to solve the puzzles for day 18 from Advent of Code 2017 using goroutines and failed spectacularly.

All this was just… doing stuff without actually understanding of when, how, and why use goroutines and channels. And without that basic understanding, most of my attempts at doing anything that resembles a useful example ended up with deadlocks. Lots and lots and lots of deadlocks.

Ugh. I decided to stop forcing myself to understand all of this concurrency thing and figure it all out once I actually need it.

Few days later, I start reading the description for the first puzzle day 20. The gist of the problem is that we have many particles “flying” around on three axes: X, Y, and Z. Each particle has a starting position, a starting velocity, and a constant acceleration. When a particle moves, the acceleration increases the velocity of the particle, and that new velocity determines the next position of the particle. Our task is to find the particle that will be the closest to the center point after every particle has moved the same number of times.

Then there’s this one sentence in the description:

Each tick, all particles are updated simultaneously.

Could it… could it be? A puzzle where goroutines can be applied to do what they were meant to be doing? Only one way to find out, and that’s to try and use goroutines to solve the puzzle.

Prior knowledge

Up until this point I knew the following things about goroutines and channels:

  • goroutines are started with the go keyword,
  • we must wait on the gouroutines to be done, otherwise they fall out of scope and things sort of leak,
  • the waiting can be done with either sync.WaitGroup or with a “quit” channel,
  • I prefer the sync.WaitGroup approach as it is easier for me to follow it,
  • goroutines communicate through channels, by sending and receiving a specific data type on them,
  • there are buffered and unbuffered channels, but other than it’s pretty much ¯\_(ツ)_/¯
  • I think it’s not possible to send and receive to the same channel in the same goroutine?

Setting up the code

From the puzzle’s description we know we have a bunch of particles, every particle has an ID from zero to N, every particle has a position, velocity, and acceleration, and we want to know the particle’s destination from the center. I went ahead and made a struct like this:

type Particle struct {
	id int
	p  coord
	v  coord
	a  coord
	d  int
}

The coord is again a struct that holds the XYZ coordinates and has two methods on it, nothing fancy. When the particle moves, we add the acceleration to the velocity and the velocity to the position. Again, nothing interesting there.

That’s pretty much all the setup I had.

Time to move

With all the prior knowledge I had, my idea was to move every particle in a separate goroutine for 10000 times, send every particle over a channel to somewhere where I can compare them and find the one that’s the closest to the center, and use sync.WaitGroup to orchestrate the waiting on the goroutines to move the particles. The 10k is just a random number that seemed high enough so all particles are given enough time to move.

The first iteration looked like something the following:

func closest(particles []Particle) Particle {
	var cp Particle
	var wg sync.WaitGroup
	wg.Add(len(particles))
	pch := make(chan Particle)

	for _, particle := range particles {
		go move(particle, 10000, pch, &wg)
	}

	wg.Wait()
	close(pch)
    return cp
}

cp will hold the closest particle, there’s the wg to do all the waiting, the pch channel to send particles over it.

For every particle in the slice of particles I tell it to “split out” in a goroutine and move that Particle for 10000 times. The move function moves the particle for the given number of iterations, sends it over the pch channel once we’re done moving it, and tells the wait group that it’s done:

func move(particle Particle, iterations int, pch chan Particle, wg *sync.WaitGroup) {
	for i := 0; i < iterations; i++ {
		particle.move()
	}
	pch <- particle
	wg.Done()
}

So far this seems like something that could work. Running this code as is, results in “fatal error: all goroutines are asleep - deadlock!“. OK, sort of make sense that it fails, we only send to the particle channel, we never receive from it.

Send and you shall receive

So… let’s receive from it I guess:

func closest(particles []Particle) Particle {
    // snip...
	for _, particle := range particles {
		go move(particle, 10000, pch, &wg)
	}

    p := <-pch
    log.Println(p)

	wg.Wait()
	close(pch)
    return cp
}

Surprise! “fatal error: all goroutines are asleep - deadlock!“. Errr…

Oh, right, can’t send and receive to a channel in the same goroutine! Even though, receiving is not in the same goroutine as sending, lets see what happens if we do receive in one:

func closest(particles []Particle) Particle {
    // snip...
	for _, particle := range particles {
		go move(particle, 10000, pch, &wg)
	}

    go func() {
        p := <-pch
        log.Println(p)
    }()

	wg.Wait()
	close(pch)
    return cp
}

Guess what? “fatal error: all goroutines are asleep - deadlock!” This should totally be possible, I’m doing something wrong!

Matej mentioned something on Twitter the other day that buffered channels can send and receive inside the same goroutine. Let’s try a buffered channel, see if that works.

Maybe buffers is what we need after all

When creating the pch channel, we pass in a second argument to make, the size of the buffer for the channel. No idea what’s a good size for it, so let’s make it the size of the particles slice:

func closest(particles []Particle) Particle {
    // snip...
	pch := make(chan Particle, len(particles))

	for _, particle := range particles {
		go move(particle, 10000, pch, &wg)
	}

    p := <-pch
    log.Println(p)

	wg.Wait()
	close(pch)
    return cp
}

Run it and… A-ha! One particle gets logged! There’s no comparing of particles in there yet, so it must be the first particle that was sent on that channel. OK, let’s range over it, that’ll do it:

func closest(particles []Particle) Particle {
    // snip...
	for _, particle := range particles {
		go move(particle, 10000, pch, &wg)
	}

    for p := range pch {
        log.Println(p)
    }

	wg.Wait()
	close(pch)
    return cp
}

“fatal error: all goroutines are asleep - deadlock!” motherf… gah! Fine, I’ll loop over all the particles and receive from the channel, see what happens then:

func closest(particles []Particle) Particle {
    // snip...
	for _, particle := range particles {
		go move(particle, 10000, pch, &wg)
	}

    for i := 0; i < len(particles); i++ {
		p := <-pch
		log.Println(p)
	}

	wg.Wait()
	close(pch)
    return cp
}

Bingo! All particles logged, no deadlocks. Throw in a closure to find the closest particle and we’re done!

// snip...
    var findcp = func(p Particle) {
        if cp.d == 0 || particle.d < cp.d {
			cp = particle
		}
    }
    for i := 0; i < len(particles); i++ {
		p := <-pch
		findcp(p)
	}
// snip...

For my input I get that the answer is Particle number 243, submit it to Advent of Code and it’s the correct answer! I did it! I used goroutines and channels to solve one puzzle!

But… how?

I made it work, but I still didn’t understand how and why does this work. Time to play around it with some more.

I’ve seen code examples using range to range over a channel and use whatever is received from the channel to do something with it. Countless blog posts and tutorials, I’ve never seen a “regular” for loop and in it receiving from the channel. There must be a nicer way to achieve the same. Re-reading a couple of the articles, I spot the error I made in the range approach; I was closing the channel too late:

func closest(particles []Particle) Particle {
    // snip...
	for _, particle := range particles {
		go move(particle, 10000, pch, &wg)
	}

	wg.Wait()
	close(pch)

    for p := range pch {
        findcp(cp)
    }

    return cp
}

Particle number 243! After some thinking about it, it makes sense, or at least this is how I made it make sense:

Lesson number 1 — golang’s range doesn’t “like” open-ended things, it needs to know where does the thing we range over begin and where does it end. By closing the channel we tell range that it’s OK to range over the pch channel, as nothing will send to it any more.

To put it in another way, if we leave the channel open when we try to range over it, range can’t possibly know when will the next value come in to the channel. It might happen immediately, it might happen in 2 minutes.

Next step is to try and make it work using unbuffered channels. If I just make this buffered channel an unbuffered one, but otherwise leave the working code as-is, it blows up with a deadlock. Something something same goroutine.

Back to the example where I tried to receive in a separate goroutine:

    // snip..
    go func() {
        p := <-pch
        log.Println(p)
    }()
    // snip...

Ah, this goroutine runs only once. It even prints out a single particle at the beginning, I just didn’t look closely enough the first time, all I saw was the deadlock error message and moved on. I should probably loop over the channel somehow and receive from it in that loop.

I remembered reading something about a weird looking for/select loop, let’s try writing one of those:

    // snip..
    go func() {
        for{
			select {
			case p := <-pch:
				findcp(p)
			}
		}
    }()
    // snip...

Particle number 243! Yey! And again, after giving it some thought, this is how I explained this unbuffered channel version to myself:

Lesson number 2 — an unbuffered channel can’t hold on to values (yah, it’s right there in the name “unbuffered”), so whatever is sent to that channel, it must be received by some other code right away. That receiving code must be in a different goroutine because one goroutine can’t do two things at the same time: it can’t send and receive; it must be one or the other.

So far whatever I threw at these two lessons learned, they’re standing their grounds.

Buffered or unbuffered?

Armed with these two bits of new knowledge, when to use buffered and when to use unbuffered channels?

I guess buffered channels can be used when we want to aggregate data from goroutines and then do with that data some further processing either in the current goroutine or in new ones. Another possible use case would be when we can’t or don’t want to receive on the channel at the exact moment, so we let the senders fill up the buffer on the channel, until we can process it.

And I guess in any other case, use an unbuffered channel.

And that’s pretty much all I learned from this one Advent of Code puzzle. Here’s my final solution using buffered channels and here’s the one using unbuffered channels. I even figured out other ways to make it work while writing this blog post, but those solutions all come from understanding of how channels work.

If you spot any errors in either my working code examples or in my reasoning, please let me know. I want to know better. Thanks!

Happy hackin’!

Prooph query bus

published on December 20, 2017.

Continuing on with the Prooph series, I want to take a look at the query bus of the Prooph service bus component. The query bus provides a way to issue a query (not necessarily a database query!) to a query handler. This handler is then responsible to return a result for our query.

Why would we need a query bus in the first place? While some may argue that the query bus is not really required, it can be a nice addition to complete the CQRS idea. Just as we have a single endpoint to handle all of our commands and events, we have a single endpoint that can handle all the queries.

The query bus allows the handler to do whatever it needs to do to return the result, synchronously or asynchronously. This is achieved by having the query bus return a ReactPHP Promise. The query handler itself will be a deferred unit of work, which allows it to promise to the querier that the query will be resolved or rejected sometime in the future.

Every query message we send with the query bus, must be routed to exactly one query handler on the other side. Of course, multiple query messages can be routed to the same handler.

Prooph’s service bus also supports a plugin system which we can use, for example, to have authorization of commands, events, and queries, logging… But more on that in a future blog post.

A quick example

The query message, just as a command or an event, can be pretty much anything — a primitive like a string or an integer, a custom data transfer object, or a class implementing the Prooph\Common\Messaging\Message interface by extending the Query class from the prooph-common library.

Setting up the query bus for using it is similar to setting up the command bus or the event bus:

  • we create the query bus,
  • we create a query router that the query bus uses to route query messages to query handlers,
  • we route a query message to its query handler,
  • we attach the router to the query bus,
  • and finally, we dispatch the query on the query bus.

Let’s see how it looks like in code:

query-bus.php

<?php declare(strict_types=1);

require_once 'vendor/autoload.php';

use Prooph\ServiceBus\Plugin\Router\QueryRouter;
use Prooph\ServiceBus\QueryBus;

$queryBus = new QueryBus();

$queryRouter = new QueryRouter();

$queryRouter->route('A simple string')
            ->to(new ProophExample\QueryHandler\Primitives());

$queryRouter->attachToMessageBus($queryBus);

$queryBus->dispatch('A simple string')
         ->done(function($result) {
            echo $result . PHP_EOL;
         }, function ($reason) {
            echo $reason . PHP_EOL;
         });

Not much going on but it shows how to set up and use the query bus.

The query handler part of this example looks like this:

src/ProophExample/QueryHandler/Primitives.php

<?php declare(strict_types=1);

namespace ProophExample\QueryHandler;

use React\Promise\Deferred;

class Primitives
{
    public function __invoke(string $query, Deferred $deferred)
    {
        $i = rand(1, 10);

        if ($i % 2 == 0) {
            $deferred->resolve(str_rot13($query));
        } else {
            $deferred->reject("Out of luck");
        }
    }
}

The query handler is an invokable that gets invoked with the string query and a React\Promise\Deferred unit of work, which we use to either resolve or reject the query.

While this example with the primitives gives an overall picture of how to use the query bus, it’s not really useful.

How many open CFPs are on JoindIn?

JoindIn has an open API which we can use to query it about events, like conferences and meetups. I think we can use it to show a better example of the query bus.

We’re going to have a query message that we’ll use to pass the type of the event we’re interested in — all, hot, upcoming, past, cfp — and a query handler that will assemble the URL for the API call and call it with a simple file_get_contents.

The query message for this example looks something like the following:

src/ProophExample/Query/JoindInEvents.php

<?php declare(strict_types=1);
namespace ProophExample\Query;

use Assert\Assertion;

class JoindInEvents
{
    private $type;
    public function __construct(string $type)
    {
        Assertion::choice($type, ['all', 'hot', 'upcoming', 'past', 'cfp']);
        $this->type = $type;
    }

    public function type(): string
    {
        return $this->type;
    }
}

We pass it in a string $type, assert that it is one of the expected values and set it as a class property. Really not much else to it than that.

The query handler will handle that query, issue the API call and resolve the React promise if it manages to decode the JSON response, or reject it if it fails:

src/ProophExample/QueryHandler/JoindInEvents.php

<?php declare(strict_types=1);

namespace ProophExample\QueryHandler;

use ProophExample\Query\JoindInEvents as Query;
use React\Promise\Deferred;

class JoindInEvents
{
    public function __invoke(Query $query, Deferred $deferred)
    {
        $url = 'https://api.joind.in/v2.1/events';

        $eventType = $query->type();

        if ($eventType != 'all') {
            $url .= '?filter=' . $eventType;
        }

        $response = file_get_contents($url);

        $jsonResponse = json_decode($response);

        if ($jsonResponse === null) {
            $deferred->reject("Error decoding json: " . json_last_error_msg());
        }

        $deferred->resolve($jsonResponse);
    }
}

In a real production code we’d probably use a proper HTTP client instead of file_get_contents, do more error checking and stuff, but in only a few lines of code we can see how to create a query handler.

To put it all together and call it, we’d have something like the following example:

query-bus.php

<?php declare(strict_types=1);

require_once 'vendor/autoload.php';

use Prooph\ServiceBus\Plugin\Router\QueryRouter;
use Prooph\ServiceBus\QueryBus;

$queryBus = new QueryBus();

$queryRouter = new QueryRouter();

$queryRouter->route(ProophExample\Query\JoindInEvents::class)
            ->to(new ProophExample\QueryHandler\JoindInEvents());

$queryRouter->attachToMessageBus($queryBus);

$queryBus->dispatch(new ProophExample\Query\JoindInEvents('cfp'))
         ->done(function($result) {
            echo sprintf("There are %d CFPs!", $result->meta->count) . PHP_EOL;
         }, function($reason){
            echo $reason . PHP_EOL;
         });

If the query message was resolved by the query handler we print out how many CFPs are there right now, and if the query handler rejected the query message, we print out the reason of rejection.

As with the command and the event bus, the examples seen here are available in my prooph-examples repository.

Happy hackin’!

P.S.: Thanks to Alexander Miertsch for helping me understand the query bus a little more!

Mockery return values based on arguments

published on December 12, 2017.

Sometimes when working with Mockery mock objects, we want to tell a mocked method to return different values for different arguments. It is a rare occasion when I need this feature, but every time I need it, I’m happy it’s there.

The feature that allows us to return different values based on arguments is the andReturnUsing Mockery method, which takes a closure as an argument:

example.php

$dependencyMock = \Mockery::mock('SomeDependency');
$dependencyMock->shouldReceive('callDependency')
    ->andReturnUsing(function ($argument) {
        if ($argument <= 10) {
            return 'low';
        }

        return 'high';
    });

$dependencyMock->callDependency(10); // 'low'
$dependencyMock->callDependency(11); // 'high'

Any number of times we call our callDependency method on our mock object with a number 10 or less, it will return 'low', otherwise it will return 'high'.

Not much of an example, so let’s take a look at one a bit closer to a real world scenario.

Say we’re using Doctrine’s entity manager to get repositories for our entities in a service class:

src/ArticleService.php

<?php

class ArticleService
{
    public function __construct(EntityManager $em)
    {
        $this->articleRepo = $em->getRepository(Entity\Article::class);
        $this->authorRepo = $em->getRepository(Entity\Author::class);
    }
}

Not the best of the codes, but we’ll manage. The entity manager receives two calls to the getRepository method, once for the Article entity, once for the Author entity.

In a test case we could then set up the mocks like so:

tests/ArticleServiceTest.php

<?php

class ArticleServiceTest extends MockeryTestCase
{
    public function setup()
    {
        $this->authorRepositoryMock = \Mockery::mock(AuthorRepository::class);
        $this->articleRepositoryMock = \Mockery::mock(ArticleRepository::class);
        $this->entityManagerMock = \Mockery::mock(EntityManager::class);
    }

    public function testArticleService()
    {
        $repositoryMap = [
            'Entity\Author' => $this->authorRepositoryMock,
            'Entity\Article' => $this->articleRepositoryMock,
        ];
        $this->entityManagerMock->shouldReceive('getRepository')
            ->andReturnUsing(function($argument) use ($repositoryMap) {
                return $repositoryMap[$argument];
            });

        $articleService = new ArticleService($this->entityManagerMock);
    }
}

In the setup method we create the three mock objects that we need and then in the test method we create a $repositoryMap to help us map entities to repositories. The repository map could have been created in the andReturnUsing closure as well.

Now when we instantiate the ArticleService with the mocked entity manager, that mocked entity manager will receive two calls to the getRepository method in the ArticleServices constructor, and it will use the closure defined in andReturnUsing to return the correct repository mock objects.

More than one way to do it

Of course there is another way to achieve the same thing and that’s by using andReturn for the return value expectations, but it’s a bit more to write:

tests/ArticleServiceTest.php

<?php
    public function testArticleService()
    {
        $this->entityManagerMock->shouldReceive('getRepository')
            ->with('Entity\Author')
            ->andReturn($this->authorRepositoryMock);
        $this->entityManagerMock->shouldReceive('getRepository')
            ->with('Entity\Article')
            ->andReturn($this->articleRepositoryMock);

        $articleService = new ArticleService($this->entityManagerMock);
    }

It does the same thing as the previous thing. We might even argue that this second example is even clearer than the first example, sure, for a relatively small argument “map”. But if we need to handle a case with more than just two possible arguments, andReturnUsing can help us in those cases.

Happy hackin’!

P.S.: The proper way to do this actually would be to refactor that ArticleService to not get the two repositories from the entity manager, but to inject them directly instead.

Five days of Advent of Gode

published on December 06, 2017.

A week or so ago, Luka mentioned this Advent of Code thing. I’ve been doing coding challenges and examples before, but never have I tried the AoC (this is the third year it’s running).

Advent of Code is a series of programming puzzles, where you get 2 puzzles a day for 25 days.

Given that I have started to learn golang again, I figured might as well learn more about it by joining this years challenge.

I keep a repository with my solutions to these puzzles and try to take notes for every day and now I want to look back at the first 5 days.

I started of the first day with just a mess of a code, just pushing for the first solution that gets the correct answer. On the second day I realised I won’t be learning much like this, so I decided to bring some order to the chaos: organised the code a bit nicer (even though it’s a far cry from good), added tests, and generally tried to get to a point where it’s easier to get started with a day’s challenge.

The AoC puzzles are relatively easy so far. Day 3 was the only day so far where I had problems wrapping my head around the problem. I’ve managed to figure out the solution for the first puzzle, but for the second puzzle I “cheated” and used the OEIS. shrug

As for golang… I don’t know enough of it to say if I like it or not. I mean, I do like it, sort of, but for these 5 days the most I did was toying around with strings and integers and slices and maps. That’s hardly enough time and usage to pass judgment on it.

On golang

I like how it fails to build if there’s an unused variable laying around, but then again I tend to save often, so I write:

for k, v := range slc {
}

Hit save and the IDE underlines that entire for line. What, why?! Hover over the line to see what the problem is… Oh, k and v are unused. Well, d-uh, I’m not done yet. But I still have to double check because the bug might be real, for example if I want to range over an integer the IDE will again underline the entire line.

For some reason I’m really bad at naming things in golang, quite often the variable and function names are just bad. I’m trying my best to follow the golang styles, but… I don’t know. I’m not sold on the whole “short rather than long” thing.

I keep mixing assignment = and assignment & declaration :=. But I’ll learn it, eventually.

I like everything about the types, even though I don’t understand everything about them, yet. Maps, for example, are not ordered even though I tried to use them as such once.

On slices

Except slices. Slices are weird. Well, were weird until I understood how golang treats and works with them.

I read four different articles to get to the point where I know what a slice is, only to get a succinct explanation of “slices are mutable views of an array”. For me, that one sentence explains it better than the four articles.

Here’s an example:

package main

import (
	"fmt"
)

func main() {
	x := []int{1, 2, 3, 4, 5}
	a := make([]int, 0)
	b := make([]int, len(x))

	y := x
	copy(a, x)
	copy(b, x)

	F(x)

	fmt.Println(x) // [2 3 4 5 6]
	fmt.Println(y) // [2 3 4 5 6]
	fmt.Println(a) // []
	fmt.Println(b) // [1 2 3 4 5]
}

func F(z []int) {
	for k, _ := range z {
		z[k]++
	}
}

Notice how both x and y are the same, even though we only modify x within the F() function, without even returning anything from that function. What happens is that the array on which the slice is built gets modified, which in turns modifies the slice(s) as well. Another thing worth remembering is to only append to a slice with the append() function.

On testing

I learned how to write tests. I guess there’s more to learn about them, but so far I’m doing OK. I’m writing a lot of repetitive things like this to get data providers:

for _, tt := range footests {
    r := Foo(tt.in)

    if r != tt.out {
        t.Errorf("Got %d for %s, expected %d", r, tt.in, tt.out)
    }
}

Even if there’s no shorter/better way to handle data providers in golang tests, I’ll just create some snippets and be done with all the copy/pasting.

I like that VS Code allows to run and debug a single test function. That’s really helpful. It shows small “run test” and “debug test” links above every test method. Click and off we go!

On packages

I haven’t yet learned how to properly work with my own packages, how to organize code and name packages to import one into the other, but I didn’t really had the need for it.

What I did learn, is that I can’t name a function like an existing package. For example, I wrote my own little function called sort, which made it impossible to import the sort package from the standard library.

All in all, I’m making good progress with both the AoC puzzles and with golang. I believe this will give me a good foundation for further learning and improving. There’s still a long road before me, but I feel I’m on the right track.

Happy hackin’!

Reacting to promises

published on November 29, 2017.

I was working on the next post of my Prooph service bus series, which includes the usage of ReactPHP promises. Given that I haven’t had the chance to take a closer look at it yet, I decided that this is the right time for it.

ReactPHP has several different components, with the end goal of providing a low-level library for event-driven programming in PHP. The one component I want to talk about today is the promise component, which is a Promises/A implementation for PHP.

What this promise library allows us is a nicer workflow with asynchronous code.

With promises, when we want to execute something asynchronously we defer the work that will be executed asynchronously. The Deferred unit of work will complete sometimes in the future, but we don’t know when. But it does promise that the work will be done, one way or the other.

The Promise is a sort of a placeholder for the result that will eventually be returned from our deferred work. This promise can then either be resolved or rejected by our deferred. When a promise is resolved successfully it has an associated value, and when it is rejected it has an associated reason for the rejection.

We use the then method on the promise to register handlers that will be called when the deferred is resolved or rejected.

To install the React/Promise component, run:

$ composer require react/promise

An example

Let’s say we have some code that does some asynchronous work. Checking the HTTP status code of a bunch of URLs, for example. We could create an invokable class that extends the Deferred:

FetchStatusCodes.php

<?php declare(strict_types=1);

use React\Promise\Deferred;

class FetchStatusCodes extends Deferred
{
    public function __invoke(array $urls)
    {
        $multiHandle = curl_multi_init();

        $handles = $this->getHandlesForUrls($urls, $multiHandle);

        $this->executeMultiHandle($multiHandle);

        $statusCodes = $this->getStatusCodes($handles);

        curl_multi_close($multiHandle);

        $successRate = $this->calculateSuccessRate($statusCodes);

        if ($successRate > 50) {
            $this->resolve($statusCodes);
        } else {
            $this->reject('Success rate too low: ' . $successRate);
        }
    }
}

I’ve left out here a bunch of code that deals with the actual fetching of the status codes, just to keep the “noise” down. The full example is available in this repository.

The important thing here is that we extend React\Promise\Deferred and that at the end we call the resolve() method to resolve this deferred if the success rate is over 50%, or that we call the reject() method if the success rate is below 50%.

The set up of the actual promise and its handlers would look something like this:

promise.php

<?php

$statusCodes = new FetchStatusCodes();
$promise = $statusCodes->promise();

$promise
    ->then(
        function($value) {
            var_dump($value);
        },
        function($reason) {
            echo $reason . PHP_EOL;
        }
    );

$urls = [
    'https://example.com/',
    'https://stackoverflow.com/',
    'https://www.google.com/',
    'https://www.google.com/no-such-url',
    'https://www.google.com:81'
];
$statusCodes($urls);

We create the FetchStatusCodes deferred object and get the promise. We setup the resolve and reject handler callbacks in the then method. They don’t do much for now:

  • the resolve handler dumps the value it got,
  • the reject handler prints out the reason of the rejection.

The output for a resolved promise would be something like this:

$ php promise.php
/home/robert/projects/react-promise-example/promise.php:32:
array(5) {
  'https://example.com/' => int(200)
  'https://stackoverflow.com/' => int(200)
  'https://www.google.rs/' => int(200)
  'https://www.google.com/no-such-url' => int(404)
  'https://www.google.com:81/' => int(0)
}

We’re not done yet!

The example above where we call the then method to set up our resolve/reject handlers, isn’t quite correct. Why?

When we call the then method it actually returns a new Promise. This feature of the Promises/A specification allows us to chain promises together.

On this second promise we can again set up our resolve/reject handlers calling the then method on it, same as we do for our first promise. The resolve handler of the second promise will be called with the return value of either the resolve or the reject handler of the first promise. The reject handler of the second promise will be called when either the resolve or the reject handler of the first promise throws an exception. And the then method of our second promise again returns a new, third promise.

Let’s see if an example makes it a bit more clearer:

promise.php

<?php

$statusCodes = new FetchStatusCodes();
$firstPromise = $statusCodes->promise();

$secondPromise = $firstPromise->then(
    function($statusCodes) {
        $successCodes = array_filter($statusCodes, function ($code) {
            if ($code >= 200 && $code < 300) {
                return true;
            }
            return false;
        });
        return $successCodes;
    },
    function($reason) {
        // handle rejected promise
        // gets called when Deferred gets reject-ed
    }
);

$thirdPromise = $secondPromise->then(
    function ($successCodes) {
        return json_encode($successCodes);
    },
    function ($reason) {
        // handle rejected promise
        // gets called when $firstPromise handlers throw an exception
    }
);

$urls = [
    'https://example.com/',
    'https://stackoverflow.com/',
    'https://www.google.com/',
    'https://www.google.com/no-such-url',
    'https://www.google.com:81'
];
$statusCodes($urls);

When our FetchStatusCodes deferred resolves, it will call the resolve handler of the $firstPromise. In that first resolve handler we get only the successful status codes and return them.

With this return from the resolve handler of the first promise, we “trigger” the resolve handler of the $secondPromise where we can, for example, json_encode our success codes. By returning this JSON string from the resolve handler of the second promise, we again “trigger” the resolve handler of the $thirdPromise, and so on.

Almost done!

When we call then, we make a new promise.

To actually be done with all the promises, we need to call the done method on the last promise in our chain. With done we stop making promises and use the result of our last promise:

promise.php

<?php
$thirdPromise->done(
    function ($jsonString) {
        echo $jsonString . PHP_EOL;
    },
    function ($reason) {
        // handle rejected promise
        // gets called when $secondPromise handlers throw an exception
    }
);

If we’d run the example now, we’d get something like this:

$ php promise.php | json_pp
{
   "https://example.com/" : 200,
   "https://www.google.rs/" : 200,
   "https://stackoverflow.com/" : 200
}

We additionally pipe the output of our example script to json_pp to pretty print the JSON string.

Now we’re done

ReactPHP promises have an ExtendedPromisesInterface that include additional shortcut and utility methods that are not part of the Promise/A specification. Their docs include some more examples, and Cees-Jan Kiewiet looks at examples using the react/dns component, among other things.

When we deal with asynchronous code in PHP, using ReactPHP promises gives us a way to deal with it in a much nicer, saner way.

Happy hackin’!

Robert Basic

Robert Basic

Software engineer, consultant, open source contributor.

Let's work together!

If you require outsourcing or consulting help on your projects, I'm available!

Robert Basic © 2008 — 2018
Get the feed