JavaScript rendering in PHP with Lambda

Published

As a web developer you’ve must came across the term Polymorphic JavaScript, or Universal JavaScript as some may prefer to call it. The technique behind it is even mentioned as “The Future of web apps”. So even for the PHP community it is hard to not stumble upon any buzz around it. In this article I’ll explain another solution that integrates into any PHP application very easily. It’s called Lambda and is a hot new AWS service.

If you’re not familiar with Lambdas, it is a serverless computing service where you can upload your code as functions and AWS handles everything required regarding the scaling and availability of it. These functions can then be invoked from any web or mobile application.

Currently you have two main options to run any JavaScript in your PHP application; you can install the V8js extension (which embeds the V8 engine in your PHP app) or let PHP and Node.js communicate with each other through the DNode RPC protocol.

With the addition of Lambda, we can upload our JavaScript code to the AWS cloud and invoke our code through API calls. Let’s get started with a simple “Hello, World!” function;

js
/**
* Very simple Lambda which just returns a string on invocation.
*/
exports.handler = function (event, context) {
context.succeed('Hello, World!')
}

We need to upload this piece of code to our AWS account. Tom Bray has written an excellent tutorial about it with the same piece of code. If you have finished the tutorial and are able to run the code through the AWS console, let’s try to invoke the same code through the AWS PHP SDK;

php
<?php
require_once __DIR__.'/vendor/autoload.php';
use Aws\Lambda\LambdaClient;
$client = LambdaClient::factory([
'version' => 'latest',
// The region where you have created your Lambda
'region' => 'eu-west-1',
]);
$result = $client->invoke([
// The name your created Lamda function
'FunctionName' => 'hello-world',
]);
echo json_decode((string) $result->get('Payload'));
// You'll now see "Hello, World! in your output"

Awesome! As you can see we get the result of the Lambda function back as a JSON encoded payload. The example is very basic but hopefully you will already see the many usages of Lambda here. To take this code to the next step, we’ll try to render a small React application inside a simple Silex application.

Let’s say that we have a React component which just renders a simple string;

js
import React from 'react'
const Application = (props) => <div>Hello, {props.name}!</div>
export default Application

Not that complicated and this component can be very easily rendering on the client side with the following;

html
<html>
<head>
<meta charset="utf-8" />
<title>php-react-lamda-example</title>
</head>
<body>
<div id="root"></div>
<script>
window.__INITIAL_PAYLOAD__ = { name: 'World' }
</script>
<script src="/bundle.js"></script>
</body>
</html>
js
import React from 'react'
import { render } from 'react-dom'
import Application from './app'
const payload = window.__INITIAL_PAYLOAD__
render(<Application name={payload.name} />, document.getElementById('root'))

As you can see, it gets the name property from a payload object and renders the component into the #root element.

To render the component on Lambda, we need to upload the component’s code alongside the following handler;

js
import React from 'react'
import { renderToString } from 'react-dom/server'
import Application from '../client/app'
export function handler(event, context) {
const markup = renderToString(<Application name={event.name} />)
context.succeed(markup)
}

Instead of rendering the component into an HTML element, we pass the resulting string to the succeed callback of the Lambda function.

Then we need to invoke the Lambda function through a very simple PHP application;

php
<?php
$filename = __DIR__.'/../dist/'.preg_replace('#(\?.*)$#', '', $_SERVER['REQUEST_URI']);
if (php_sapi_name() === 'cli-server' && is_file($filename)) {
return false;
}
require_once __DIR__.'/../vendor/autoload.php';
use Aws\Lambda\LambdaClient;
use Silex\Application;
$app = new Application();
$app['debug'] = true;
$app->get('/', function () use ($app) {
$client = LambdaClient::factory([
'version' => 'latest',
'region' => 'eu-west-1',
]);
$payload = json_encode(['name' => 'Server']);
$result = $client->invoke([
'FunctionName' => 'php-react-lamda-example',
'Payload' => $payload,
]);
$markup = json_decode((string) $result->get('Payload'));
return <<<EOF
<html>
<head>
<meta charset="utf-8">
<title>php-react-lamda-example</title>
</head>
<body>
<div id="root">$markup</div>
<script>
window.__INITIAL_PAYLOAD__ = $payload;
</script>
<script src="/bundle.js"></script>
<body>
</html>
EOF;
});
$app->run();

When a visitor navigates to the root of our PHP application, we invoke the Lambda function and pass a JSON encoded payload to it. The result is a React markup string which we can use to render the React component server side. Also note that I’ve also passed the payload to the client side.

A very basic example, but it can be easily extended to much more complex PHP / JavaScript applications. The full source of the example — including the compiling part with webpack — can be viewed here.