Stuff to do when you’re not well

Last week I was sick with a cold. I was basically indoors for 5 straight days (one of those actually in bed all day). I was also unable to sleep for a couple of nights.

So I had a lot of extra time on my hands last week and, despite needing to make a full recovery, I also had the itch to start something I’ve been mulling over for a while.

More on that at another time. But suffice it to say it led me on an adventure which taught me some new stuff, an adventure which I’ve thoroughly enjoyed. So my tip for you in case you come down with a cold this winter…

Learn new stuff!

I’m a PHP developer. I’m also a big fan of Laravel. So if I build something now, it’s a safe bet that it’s going to be in/on Laravel.

I thought it would be awesome to build an open source tool that can easily be installed for the non-programmer, but also great for other devs to pick up and play with.

But I found that certain parts of what I’m planning to build aren’t really so open or easy to get going with.

For example, Laravel has great built-in support for broadcasting events over WebSockets and an awesome package in Echo that enables you to connect to a WebSocket server and consume your events on the client side.

The services it works for are limited though and would make my ideal solution a bit more tricky to use:

  • Pusher — which is paid for beyond various thresholds and requires some configuration plus set up of a separate account. I wanted to avoid a third-party service if possible.
  • Socket.IO — a great solution, but I didn’t fancy having a Node.JS application as a requirement for a PHP app. With that plus Redis to configure, it just seemed a bit of a handful, a definite barrier to entry that would put some people off and increase the requirements of a solution beyond something ‘turn-key’.

(I’ve used Pusher before. It’s a great service and their support is excellent. I’ve never set up a Socket.IO server or Redis, but apparently there’s a good community-run package available if you prefer this.)

I’d heard of Ratchet a while ago and was aware that it’s an all-PHP solution for setting up a WebSocket server with very minimal requirements, so I fancied having a go with that. I found a neat package that had an almost-ready-to-go Ratchet server integrated with Laravel. Great start!

I also set myself the challenge of making this work with Laravel Echo because this is a setup that’s clearly missing from this package. So I knew I’d need to fork Echo and build some bindings for Ratchet.

I then became aware of the need for some transport mechanism between the main Laravel application and the WebSocket server. Despite the code residing in essentially the same place, the Laravel application and the Ratchet server will technically be very separate from each other as they’ll be running in separate processes and so won’t easily share memory etc.

The upshot of that is that, without something in between the two, there’s no way for your Laravel app events to broadcast to the Ratchet server. That means the Ratchet server won’t get the messages to push onto attached WebSocket clients. Chain, broken :(

Enter ZeroMQ. This is something else entirely. It’s pure magic tbh. But I figured out enough of how it works to make it do what I want. It basically allows you to open a channel between two programs using a TCP socket. There’s very little requirements from either side and ZeroMQ is just a message broker, dutifully passing messages along the line from publisher to subscriber.

It’s also really easy to install on most platforms that it supports. I used Homebrew on my Mac and it’s a simple apt-get on Ubuntu. I’m sure it’s just as easy on other platforms.

Thankfully, the hard work of using ZeroMQ from within PHP has also been taken care of. There’s a PECL package which does what it says on the tin. And there are also a couple of packages that wrap this for Laravel. I picked this one because it looked like it plugged into Laravel’s broadcasting out of the box.

I am standing on the shoulders of giants.

So all I had to do was get laravel-echo, laravel-ratchet and laravel-zmq to talk to each other. Easy peasy! Right?

Well... Turns out I needed to figure out some TypeScript. Echo is written in TypeScript, so I wanted to stay true to that — I also hoped to borrow heavily from what’s been done before and save some time. But I’d never done TypeScript before.

Mercifully, all the hype is true: TypeScript is great and super simple to pick up if you’re used to any other OO language. Despite some minor JS quirkiness around super-/sub-class method contexts and this, I managed to extend Echo pretty easily with a Ratchet connector that works. This made me happy :)

Then I needed to tweak the laravel-zmq package. It wasn’t set to work with Laravel 5.5 and needed a little customisation. Plus I needed to get the correct method for the socket connection (the one I managed to get working was ZMQ::SOCKET_PUSH and ZMQ::SOCKET_PULL).

So I forked that, updated the dependencies, and fixed up some of the code. Now it works with Laravel 5.5’s broadcasting. Boom! Done.

Finally, laravel-ratchet. This is the one I really need to get my head around. This is where all the fun stuff was going to happen — the socket server and it’s connection to ZeroMQ would all exist here, clients can subscribe to channels, and events sent from the application would be forwarded to relevant clients.

I knew Ratchet was built on ReactPHP, but I’d never used ReactPHP before. I wasn’t sure if I needed to know it, but I ended up learning about how it works trying to figure out how to get my server doing what I wanted.

I also ended up extending the original laravel-ratchet package quite a bit. I ended up pushing back some of my improvements that brought it up to date with Laravel 5.5.

I built further on it to allow it to work with ZeroMQ in more situations and it’s specifically configured to work with the setup I’ve got with my version of laravel-zmq.

After all of this (and lots of fiddling around with Composer and NPM — and even more coughing and sneezing) I managed to get a working broadcasting set up using Laravel, Ratchet and ZeroMQ!

I’m really pleased with the result, but I know I will revisit this as I’m sure there’s more I can do to polish this.

I think what I enjoyed most about this whole experience has been:

  • it’s flexed a lot of what I already know about modern PHP,
  • it’s encouraged me to follow some new practices and refine the use of my tools,
  • it’s exposed me to at least 3 other people’s code (and thus their thinking processes) which is never bad,
  • it’s given me cause to stretch myself and learn some new things (TypeScript, WebSockets, ZeroMQ),
  • it’s forced me (yes, FORCED!) to publish no less than 3 open source packages (albeit forks of other people’s stellar work),
  • AND I think it’s actually helped me get over my cold! (DISCLAIMER: Healing effects not guaranteed. Double-blind study required.)

In case you’re interested in setting up Ratchet with Laravel Echo, please check out these three packages as I’d love to make them better :)