{"id":1715,"date":"2013-09-13T13:42:44","date_gmt":"2013-09-13T18:42:44","guid":{"rendered":"http:\/\/www.phpied.com\/?p=1715"},"modified":"2013-09-13T13:42:44","modified_gmt":"2013-09-13T18:42:44","slug":"server-side-react-with-php","status":"publish","type":"post","link":"https:\/\/www.phpied.com\/server-side-react-with-php\/","title":{"rendered":"Server-side React with PHP"},"content":{"rendered":"<p><em>Problem: Build web UIs<\/em><br \/>\nSolution: React<br \/>\n<em>Problem: UI built in JS is anti-SEO (assuming search engines are still noscript) and bad for perceived performance (blank page till JS arrives)<\/em><br \/>\nSolution: <a href=\"https:\/\/github.com\/facebook\/react-page\">React page<\/a> to render the first view<br \/>\n<em>Problem: Can't host node.js apps \/ I have tons of PHP code<\/em><br \/>\nSolution: Use PHP then!<\/p>\n<p>This post is an initial hack to have React components render server-side in PHP.<\/p>\n<h2>Previously...<\/h2>\n<p>So <a href=\"https:\/\/www.phpied.com\/remarkable-react\/\" title=\"Remarkable React\">you know about React<\/a> and how to <a href=\"https:\/\/www.phpied.com\/reactive-table\/\" title=\"Reactive table\">build your own<\/a> components. And you know you can <a href=\"https:\/\/www.phpied.com\/installing-v8js-for-php-on-a-mac\/\" title=\"Installing v8js for PHP on a Mac\">run JavaScript inside PHP<\/a> scripts, thanks to v8js. So nothing can stop you from rendering React components on the server side in PHP. Which means you send the first view from the server and then continue from there.<\/p>\n<h2>Step by step (<a href=\"http:\/\/www.youtube.com\/watch?v=ay6GjmiJTPM\">oh baby!<\/a>)<\/h2>\n<ol>\n<li>Get the <a href=\"http:\/\/facebook.github.io\/react\/downloads.html\">latest React build<\/a>, unzip to a <code>react\/<\/code> sub-directory of where your PHP scripts live on the server<\/li>\n<li>Create your own components, and put them in <code>react\/build\/<\/code> too for simplicity. I'll just have one <code>Table<\/code> component from the <a href=\"https:\/\/www.phpied.com\/reactive-table\/\" title=\"Reactive table\">earlier blog post<\/a><\/li>\n<li>Create test.php that concatenates JS code consisting of: stubs, react, custom components. Fetch (or fake) data somehow (this where you PHP code you've been slaving away for the past 3 years shines). Render the custom component with the PHP-fetched data.<\/li>\n<li>Load http:\/\/localhost\/test.php<\/li>\n<li>Profit!<\/li>\n<\/ol>\n<h2>How (<a href=\"http:\/\/www.youtube.com\/watch?v=YuJ-A3_KY1Q\">A-haw haw haw haw<\/a>)<\/h2>\n<p>First, the custom component goes into <code>react\/build\/table.js<\/code>:<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-reserved\">var<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">Table<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-identifier\">React<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">createClass<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-identifier\">render<\/span><span class=\"hl-code\">: <\/span><span class=\"hl-reserved\">function<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n    <\/span><span class=\"hl-reserved\">return<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-code\">\r\n      <\/span><span class=\"hl-identifier\">React<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">DOM<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">table<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-reserved\">null<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-identifier\">React<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">DOM<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">tbody<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-reserved\">null<\/span><span class=\"hl-code\">,\r\n        <\/span><span class=\"hl-reserved\">this<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">props<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">data<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">map<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-reserved\">function<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">row<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n          <\/span><span class=\"hl-reserved\">return<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-code\">\r\n            <\/span><span class=\"hl-identifier\">React<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">DOM<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">tr<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-reserved\">null<\/span><span class=\"hl-code\">, \r\n              <\/span><span class=\"hl-identifier\">row<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">map<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-reserved\">function<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">cell<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n                <\/span><span class=\"hl-reserved\">return<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">React<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">DOM<\/span><span class=\"hl-code\">.<\/span><span class=\"hl-identifier\">td<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-reserved\">null<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-identifier\">cell<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n              <\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n        <\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n  <\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>Alternatively the more readable version goes to <code>react\/src\/test.js<\/code> and you transform it to the build version:<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-code\">var Table = React.createClass({\r\n  render: function () {\r\n    return (\r\n      <\/span><span class=\"hl-brackets\">&lt;<\/span><span class=\"hl-reserved\">table<\/span><span class=\"hl-brackets\">&gt;<\/span><span class=\"hl-brackets\">&lt;<\/span><span class=\"hl-reserved\">tbody<\/span><span class=\"hl-brackets\">&gt;<\/span><span class=\"hl-code\">\r\n        {this.props.data.map(function(row) {\r\n          return (\r\n            <\/span><span class=\"hl-brackets\">&lt;<\/span><span class=\"hl-reserved\">tr<\/span><span class=\"hl-brackets\">&gt;<\/span><span class=\"hl-code\">\r\n              {row.map(function(cell) {\r\n                return <\/span><span class=\"hl-brackets\">&lt;<\/span><span class=\"hl-reserved\">td<\/span><span class=\"hl-brackets\">&gt;<\/span><span class=\"hl-code\">{cell}<\/span><span class=\"hl-brackets\">&lt;\/<\/span><span class=\"hl-reserved\">td<\/span><span class=\"hl-brackets\">&gt;<\/span><span class=\"hl-code\">;\r\n              })}\r\n            <\/span><span class=\"hl-brackets\">&lt;\/<\/span><span class=\"hl-reserved\">tr<\/span><span class=\"hl-brackets\">&gt;<\/span><span class=\"hl-code\">);\r\n        })}\r\n      <\/span><span class=\"hl-brackets\">&lt;\/<\/span><span class=\"hl-reserved\">tbody<\/span><span class=\"hl-brackets\">&gt;<\/span><span class=\"hl-brackets\">&lt;\/<\/span><span class=\"hl-reserved\">table<\/span><span class=\"hl-brackets\">&gt;<\/span><span class=\"hl-code\">\r\n    );\r\n  }\r\n});<\/span><\/pre>\n<\/div>\n<p>Now, let's see about this <code>test.php<\/code>. Starting:<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-code\"> \r\n<\/span><span class=\"hl-inlinetags\">&lt;?php<\/span><span class=\"hl-code\">\r\n<\/span><span class=\"hl-var\">$v8<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-reserved\">new<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-identifier\">V8Js<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>Hm, easy enough. Now let's start pushing some JS to an array to concatenate later.<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-var\">$react<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-reserved\">array<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>Concatenating code is ugly, but here in PHP we have to pass JS code to V8 as a string. Surely the following code can be cleaned up a bit by using external files, but for a quick hack it's just \"perfect\" :).<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> stubs, react<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n<\/span><span class=\"hl-var\">$react<\/span><span class=\"hl-brackets\">[<\/span><span class=\"hl-brackets\">]<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-string\">var console = {warn: function(){}, error: print}<\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-code\">;\r\n<\/span><span class=\"hl-var\">$react<\/span><span class=\"hl-brackets\">[<\/span><span class=\"hl-brackets\">]<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-string\">var global = {}<\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-code\">;\r\n<\/span><span class=\"hl-var\">$react<\/span><span class=\"hl-brackets\">[<\/span><span class=\"hl-brackets\">]<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-identifier\">file_get_contents<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">react\/build\/react.js<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n<\/span><span class=\"hl-var\">$react<\/span><span class=\"hl-brackets\">[<\/span><span class=\"hl-brackets\">]<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-string\">var React = global.React<\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>React uses <code>console.warn()<\/code> and <code>console.error()<\/code> which do not exist in V8. But there's <code>print()<\/code> in V8. So warnings will be silenced and errors will be printed on the page. We can be more creating here, e.g. print server-side JS errors in client side JS console but hey, this is a proof of concept.<\/p>\n<p>The <code>global<\/code> jazz is a React bug IMO because at the top of the react.js file there's a bootstrapping code that goes like: <code>window ? window.React : global.React<\/code>. In V8 there's no <code>window<\/code> nor <code>global<\/code>, so hence the workaround.<\/p>\n<p>Load custom react components:<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> my custom components<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n<\/span><span class=\"hl-var\">$react<\/span><span class=\"hl-brackets\">[<\/span><span class=\"hl-brackets\">]<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-identifier\">file_get_contents<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-string\">react\/build\/table.js<\/span><span class=\"hl-quotes\">'<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>Now the \"brains\" of the app. We must fetch data somehow. This is where your legacy PHP can do its thing. We don't care about this as long as we end up with an array of data to put in a table<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> my application<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n<\/span><span class=\"hl-var\">$data<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-reserved\">array<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> database, web services, whatevers<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n    <\/span><span class=\"hl-reserved\">array<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-number\">1<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-number\">2<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-number\">3<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">,\r\n    <\/span><span class=\"hl-reserved\">array<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-number\">4<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-number\">5<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-number\">6<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">,\r\n    <\/span><span class=\"hl-reserved\">array<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-number\">7<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-number\">8<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-number\">9<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>Now for the magic: instead  of rendering a react component in a DOM node, you can render it to a string. This is an async operation, so you need to pass a callback. V8's <code>print()<\/code> is the most appropriate callback. It will simply pass the output to PHP's <code>print()<\/code>\/<code>echo<\/code><\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-var\">$react<\/span><span class=\"hl-brackets\">[<\/span><span class=\"hl-brackets\">]<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-identifier\">sprintf<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-string\">React.renderComponentToString(Table({data: %s}), print)<\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-code\">,\r\n  <\/span><span class=\"hl-identifier\">json_encode<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-var\">$data<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>That's enough JavaScript!<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> concat all JS<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n<\/span><span class=\"hl-var\">$react<\/span><span class=\"hl-code\"> = <\/span><span class=\"hl-identifier\">implode<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-string\">;<\/span><span class=\"hl-special\">\\n<\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-code\">, <\/span><span class=\"hl-var\">$react<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;<\/span><\/pre>\n<\/div>\n<p>Run the JavaScript:<\/p>\n<div class=\"hl-main\">\n<pre><span class=\"hl-reserved\">try<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-var\">$v8<\/span><span class=\"hl-code\">-&gt;<\/span><span class=\"hl-identifier\">executeString<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-var\">$react<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n<\/span><span class=\"hl-brackets\">}<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-reserved\">catch<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-identifier\">V8JsException<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-var\">$e<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-brackets\">{<\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-comment\">\/\/<\/span><span class=\"hl-comment\"> blow up spectacularly<\/span><span class=\"hl-comment\"><\/span><span class=\"hl-code\">\r\n  <\/span><span class=\"hl-reserved\">echo<\/span><span class=\"hl-code\"> <\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-string\">&lt;pre&gt;<\/span><span class=\"hl-quotes\">&quot;<\/span><span class=\"hl-code\">; <\/span><span class=\"hl-identifier\">var_dump<\/span><span class=\"hl-brackets\">(<\/span><span class=\"hl-var\">$e<\/span><span class=\"hl-brackets\">)<\/span><span class=\"hl-code\">;\r\n<\/span><span class=\"hl-brackets\">}<\/span><\/pre>\n<\/div>\n<p>Boom!<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/www.phpied.com\/wp-content\/uploads\/2013\/09\/phpreact.png\" alt=\"phpreact\" width=\"572\" height=\"485\" class=\"alignnone size-full wp-image-1716\" srcset=\"https:\/\/www.phpied.com\/wp-content\/uploads\/2013\/09\/phpreact.png 572w, https:\/\/www.phpied.com\/wp-content\/uploads\/2013\/09\/phpreact-300x254.png 300w\" sizes=\"(max-width: 572px) 100vw, 572px\" \/><\/p>\n<h2>todo<\/h2>\n<ul>\n<li>Marry the server-side generated code with React on the client side to handle events and reactive updates (in my example I don't even load client side React)<\/li>\n<li>Strip the events part of React as it's not needed server-side<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Problem: Build web UIs Solution: React Problem: UI built in JS is anti-SEO (assuming search engines are still noscript) and bad for perceived performance (blank page till JS arrives) Solution: React page to render the first view Problem: Can&#8217;t host node.js apps \/ I have tons of PHP code Solution: Use PHP then! This post [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[5,40,366],"tags":[],"_links":{"self":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts\/1715"}],"collection":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/comments?post=1715"}],"version-history":[{"count":0,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/posts\/1715\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/media?parent=1715"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/categories?post=1715"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.phpied.com\/wp-json\/wp\/v2\/tags?post=1715"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}