PHP vs. Javascript

Everyone has their primary languages that they find themselves using over and over again through their career. Mine happens to C#, PHP and Javascript. PHP get’s a lot of shit though. I honestly, don’t get it. Both languages have changed very dramatically over the last several years… Well, more one than the other.

So, let’s hit the pros of each language and community.

Pros for Javascript:

  • a massive library of shit and gold (npm)
  • a very “functional light” feel to the language. It’s FP when you want/need to be, and procedural when you want/need it to be
  • async is easy

Pros for PHP:

  • there’s a stackoverflow question for anything you ever want or need to build with amazing documentation on the site
  • the syntax is very stable and very backwards compatible

OK, got it? Good. My challenge was to build a “game”, something really complex and in both languages. No transpiling is aloud. In order to do this “right”, I needed a few PHP libraries and extensions.

First and foremost is php-uv which allows PHP 7.1 to use libuv for async i/o. Finally, we’ll use Aerys, a non-blocking web server written in PHP that takes advantage of libuv and provides a nice interface similar to Javascript async/await by using generators.

For the Javascript side, we’re using node v7.5.0 with the --harmony flag. This gives us a native async/await without transpiling.

All of this is running on a 4 core i5, with 8gb of ram and Gentoo Linux on a Surface Pro 3.

Exploring Async

One of the things that’s attractive to this arrangement, is that almost any given library can be ported, 1-to-1 between each language. Let’s look at some examples:

/**
	 * Load events and recreate current state
	 *
	 * @return \Generator
	 */
	public function Load() {

		$latestSnapshot = yield $this->container->storage->LoadSnapshot( $this->id );

		if ( $latestSnapshot ) {
			$this->state       = $latestSnapshot['state'];
			$this->nextVersion = $latestSnapshot['version'] + 1;
		} else {
			$latestSnapshot    = [ 'version' => - 1 ];
			$this->nextVersion = 0;
		}

		$this->records = yield $this->container->storage->LoadEvents( $this->id, $latestSnapshot['version'] );

		yield from $this->ReduceEvents();
	}

and the Javascript:

/**
	 * Loads the aggregate from ES and replays past events
	 * @returns {Promise.}
	 */
	async Load() {
		let latestSnapshot = await this._container.storage.LoadSnapshot( this._id );

		if ( latestSnapshot ) {
			this._state = latestSnapshot.state;
			this._nextVersion = latestSnapshot.version + 1;
		} else {
			latestSnapshot = {
				version: - 1
			};
			this._nextVersion = 0;
		}

		this._records = await this._container.storage.LoadEvents( this._id, latestSnapshot.version );
		await this._ReduceEvents();
	}

Woah … these things are almost identical, but, don’t be fooled. PHP is using generators while under the hood, Javascript is using Promises. These behave very differently.

Generators aren’t executed until they are iterated (or yielded). So that means a function call to a generator function will not be executed until yield is called on it. Whereas a Promise executes immediately, you just won’t get the result until you call .then on it.

These differences meant I had to do some interesting things. For example, in PHP, I was able to do this to create a lock on a resource:

/**
	 * Stores all unstored events in an array
	 *
	 * @param array $events The events
	 *
	 * @return \Generator
	 */
	public function Store( $id, $instanceId, array &$events ): \Generator {
		while($this->isLocked($id)) {
			yield;
		}

By having a yield statement all by itself, it’s the equivalent of putting a process.nextTick() or a setTimeout(0) on everything below it. We can literally yield execution, vs. this in Javascript:

/**
	 * Stores the given events in the store
	 * @param {string} id
	 * @param {string} instanceId
	 * @param {Array} events
	 * @returns {Promise
	 */
	async Store( id, instanceId, events ) {
		if ( this.IsLocked( instanceId ) ) {
			await this.locks[instanceId]();
		}

These look almost identical … except there’s a large chunk of code in the Javascript version to create these locks. There’s no such thing in the PHP version.

This tiny idiosyncrasy caused some modifications to be made in both versions to accomplish the goals set up by the project. Both are not perfect, by any stretch of the imagination.

Exploring Libraries

With the PHP version, I usually had to fork a library and rewrite it to use non-blocking sockets and “play nice” with the “new” async world of PHP. This is currently a major concern I have with this way of doing things. Until the rest of the PHP world decides that async is the way to go, many (read: all) libraries are doing things synchronously.

Language Features

There’s literally 0 difference between the two languages, as far as features go. I cannot think of any common-use feature in either language that cannot be emulated, almost line for line, between the two languages. I invite you to think for yourself and comment below … best one gets half a bitcoin, I have a metric crap ton of them and don’t use them.

I’m interested to see what there is. It took me a weekend to port redux and redux-saga to PHP. It would probably take a few hundred hours to port something like WordPress to Javascript, but, it’s not as hard as you would think … just very effing tedious.

Speed

PHP gets a lot of crap for how “slow” it is. PHP 7.1, with Aerys and php-uv came in 0.01s faster than node 7.5.0, on average, for all the websocket endpoints created. That surprised me. However, I don’t have “scientific proof”, but I’m working on it. All I’ve done thus far is casual testing.

Either way, they seem to be the about the same speed…

Conclusion

I like either language, they both have their quirks … and PHP is more consistent with it’s inconsistent quirks, while Javascript … not so much. For example, this annoys the living shit out of me:

// node 7.5.0 --harmony, native/no transpile
try {
  await somethingThatThrows();
} catch ( err ) {
  console.log( 'this will never be called!' );
}

this makes crazy. Batshit crazy. Sometimes it gets caught, sometimes it doesn’t. I don’t get it. I just don’t.

I don’t know how useful this is, or will be, to anyone out there. But I found this to be an interesting exercise nonetheless. It’s always good to do something different every once in awhile…