JS-in-PHP
Universal React with your existing backend

What is React?

  • Just a view layer
  • Highly reusable components
  • Scalable development-wise
  • Solves state management problems
  • low effort youtube content

SPA Pitfalls

  • Large JS payloads
  • Heavily reliant on RPC/APIs
  • No DOM until client-render

SEO is king
 SPA sucks at it

  • Load time penalties
  • No crawlable content
  • Say good bye to your page-rank

Simple Server-side rendering

  1. Render React on the server
  2. Take the state and DOM
  3. Send it to the client
  4. Client executes app.js
  5. React loads state
  6. Tries to render but finds server-rendered DOM
  7. Skips re-painting DOM & adds listeners
  8. ???
  9. Profit!

But my app needs data!

But all my servers/backend is PHP!

JS-in-PHP
https://github.com/pinepain/php-v8

What is php-v8?

  • v8 is Chrome's Javascript runtime (node uses it)
  • pinepain (great guy) made it into an extension
  • Almost 1-to-1 PHP API to the native v8 C++ API
  • Runs most recent v8 versions (with features like compiler code-cache)
  • Supports isolates, contexts, memory constraints, sandboxing etc

Example

<?php
$isolate = new \V8\Isolate();
$context = new \V8\Context($isolate);
$source = new \V8\StringValue($isolate, "'Hello' + ', World!'");

$script = new \V8\Script($context, $source);
$result = $script->Run($context);

echo $result->ToString($context)->Value(), PHP_EOL;


//result is last statement executed

How do we use it for SSR?

  1. PHP Router receives request
  2. Prepares data as JSON
  3. php-v8 compiles/loads app.js
  4. Injects JS data context as global variable
  5. $script->run
  6. JS Framework (i.e. React) finds data
  7. Renders App as HTML
  8. (React) PHP injects data into HTML
  9. PHP sends rendered response

Adv Example (PHP)

<?php
$isolate = new Isolate();
$context = new Context($isolate);

//Load our app.js file from disk
$sourceString = file_get_contents(__DIR__ . '/app.js');
$stringValue = new StringValue($isolation, $sourceString);
$source = new Source($stringValue, null, $cachedData);

//Compile it (add caching of compiled code here)
ScriptCompiler::Compile($context, $source, CompileOptions::kConsumeCodeCache);

//Here's some state I prepared earlier!
$jsonInitialState = json_encode($preparedState);

//Make our dirty PHP values into v8 ones
$initialStateVarName = new \V8\StringValue($isolation, '__INITIAL_STATE__');
$initialStateValue = new \V8\StringValue($isolation, $jsonInitialState);

//Get the JS contexts global object and inject our prepared state
$globalObject = $context->GlobalObject();
$globalObject->Set($context, $initialStateVarName, $initialStateValue);

//Execute
$result = $script->Run($context);

echo($result); //Gimmie that DOM

Adv Example (React)

//JS
const initialState = (global && global.__INITIAL_STATE__) ? 
    global.__INITIAL_STATE__ : {};

const store = createStore(
	rootReducer,
	initialState,
	enhancer
);

const app = (
    <Provider store={store}>
        <App>
        </App>
    <Provider>
);

if(isServerSide) {
    global.__RESULT__ = ReactDOMServer.renderToHtml(app);
    global.__INITIAL_STATE__ = JSON.stringify(store.getState());
}
else {
    ReactDOM.render(app, document.getElementById('app'));
}

Gotchas

$phpSync =  "Synchronous";
echo($phpSync);

getJsSync().then((sync) => {
    console.log(sync);
});

I'll race you

Async data fetching
(fetch, $.get etc)

  • php-v8 is sandboxed (isolate)
  • XMLHttpRequest is BOM!
  • Even if you could make requests
    you'd never get the results!

PHP polyfills!

(very simplified example)

<?php

//Create our "fetch" function
$fetch = function (\V8\FunctionCallbackInfo $args)
{
	$context = $args->GetContext();
	foreach ($args->Arguments() as $arg) 
	{
		$out[] = $arg->ToString($context)->Value();
	}

	$contents = file_get_contents($out[0]);

	$args->GetReturnValue()->Set(new \V8\IntegerValue($args->GetIsolate(), $contents));
});

//Make a v8 template out of it
$fetchFuncTemplate = new \V8\FunctionTemplate($isolate, $fetch);

//Grab the JS runtime global object and inject our fetch function which actually calls into PHP
$context->GlobalObject()
    ->Set($context, new \V8\StringValue($isolate, 'fetch'), $fetchFuncTemplate->GetFunction($context));

There goes the BOM

  • BOM = Browser Object Model
  • Say goodbye to setTimeout/setInterval
  • requestAnimationFrame too
  • oh yeah forget about window and navigator

What else can php-v8 do?

  • Don't just fetch, create a psr/request object and serve it internally with no HTTP overhead
  • Run sandboxed user code with mem/cpu limits
  • Use that sweet NPM package you always wanted
  • Reduces API requests
  • Helps you keep all that Legacy code that you love!

The end

 

https://github.com/pinepain/php-v8
https://simplywall.st

JS-in-PHP

By Jabin Bastian

JS-in-PHP

  • 357