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
- Render React on the server
- Take the state and DOM
- Send it to the client
- Client executes app.js
- React loads state
- Tries to render but finds server-rendered DOM
- Skips re-painting DOM & adds listeners
- ???
- 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?
- PHP Router receives request
- Prepares data as JSON
- php-v8 compiles/loads app.js
- Injects JS data context as global variable
- $script->run
- JS Framework (i.e. React) finds data
- Renders App as HTML
- (React) PHP injects data into HTML
- 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