WordPress Playground was prototyped in a week

Adam Zieliński Avatar

Posted by

on

People are often surprised that the first prototype of WordPress Playground came together in just a week.

It is so very tempting to wear my serious face and add something like oh, you know, that’s just experience and a few tricks from robust chaos theory. Some experience was involved, sure, but mostly tricks and chaos. And lots of luck. Here’s what happened after I got stubborn about running WordPress in the browser:

Getting to Hello World

I looked for prior art on running PHP in the browser and found two projects. The first one, phpjs, was interesting but couldn’t possibly run WordPress. The second one was PHP in browser and gave me really high hopes, especially once I found the php-wasm fork running Drupal. If Drupal works, I thought WordPress should work too. And maybe I had virtually zero prior experience with WebAssembly, but such a minor detail wasn’t going to stop me!

I cloned php-wasm and followed the README to start the build process – I was sure it would fail. Except it didn’t! About an hour later, I was looking at a WebAssembly build of PHP. Great! After some trial and error, I even got the basic <?php echo "Hello world"; to work. I didn’t fully understand why it worked, but it did work nonetheless. Good.

It was time to run WordPress, and that’s also where the trouble started. WordPress consists of hundreds of files. How do I even ship that in the browser?! That was as far as I could poke around without understanding what I was doing – I had to learn some WebAssembly.

Shipping WordPress in the browser

PHP was built to WebAssembly from its source written in C programming language. The good news was that my knowledge of PHP internals matched my experience with C and compilers. The bad news was that I only ever wrote like three C programs, and most of them may have been “Hello World”. I spent the next few days hoping to scrap enough information to make any progress. The scope of my research was all parts of C, compilation, and to WebAssembly.

The barrier of entry was steep. I was only scratching the surface, and, truth be told, I almost gave up. I’ve learned that php-wasm built PHP to WebAssembly using a compiler called Emscripten, and that I could use the --preload-file option to include WordPress in the final PHP build. After just 20 short attempts, I had a clunky 50MB PHP+WordPress bundle that yielded an error page with no CSS. Progress!

Overcoming WordPress dependency on MySQL

The error page was about a database connection. That was a tricky thing to tackle – WordPress requires MySQL, but MySQL wouldn’t build to wasm. No problem, I thought. Let’s find some MySQL-compatible alternatives. One of them, Tidb, looked promising at first, but turned out to be different enough from MySQL to rule it out as a drop-in replacement. Also, it was 70MB large. Bummer! And then I thought I wonder how that Drupal demo stores data?

It turned out the Drupal demo used SQLite. Of course, that’s why php-wasm was built with SQLite support! I thought. A quick Google search revealed that WordPress could run on SQLite, too, with the wp-sqlite-db plugin. Yay, everything aligned neatly. I downloaded the SQLite plugin, included it in the WebAssembly build, refreshed the page, and… there was the WordPress installer! Success! Well, there also was a metric ton of PHP warnings, no CSS, and a 404 error on any navigation. But it was still a success!

Resolving broken navigation and CSS with service workers

Navigation and CSS were broken because all links and assets rendered by WordPress pointed to localhost network – only no WordPress was running there. Hm! The entire project now hinged on somehow rewiring all URLs to something the browser could handle internally. At first, I thought this is as far as it goes, but then I remembered the Drupal demo had to solve this exact problem, too. There was hope!

Upon closer look, I found Drupal navigation was handled by service workers. Service workers are like JavaScript proxy servers sitting between the browser and the network. They are typically used to implement offline mode in progressive web apps by intercepting network requests and providing a cached response, but the Drupal demo found a more crafty application. It used PHP instead of a request cache to source responses. Why not? Abstractly speaking, both transform HTTP requests into HTTP responses.

Embracing this approach, I prepared a service worker that used WordPress to handle all WordPress-related requests. Conceptually, it looked like this:

self.addEventListener("fetch", (event) => {
	// Intercept and handle WordPress-related requests internally
	
	if ( isWordPressRequest(event.request) ) {
		event.respondWith( renderWithWordPress( event.request ) );
	return;
	}

	// Pass all other requests to the network
	event.respondWith(
		return fetch(event.request);
	);
});

And it worked! The styles were loading, the links were clickable, and the first proof of concept of WordPress Playground was finished!

Navigating challenges and embracing the thrill of exploration

That was where the real challenge started. There were dozens of rough edges to fix, like the omnipresent PHP warnings, failing WordPress tests, or frequent browser crashes, to name just a few. Also, vital features were missing – like support for WordPress plugins. So many problems! Worse, every single one could be an unsolvable show-stopper for the entire project, and I wouldn’t find out for weeks or months.

The thought was paralyzing, so I found a more joyful one: I was an explorer on an exciting expedition to a beautiful new land, about to start sketching the map.

Leave a Reply

Blog at WordPress.com.

%d bloggers like this: