forked from reactjs/react-php-v8js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ReactJS.php
171 lines (156 loc) · 4.59 KB
/
ReactJS.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<?php
/**!
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/**
* A PHP class to render React components on the server
* and then hook the components on the client side.
* Requires V8JS PHP extension: http://php.net/v8js
*/
class ReactJS {
private
/**
* Name of the component to render
* @var string
*/
$component,
/**
* Properties that go along with the component
* @var mixed
*/
$data = null,
/**
* Instance of V8Js class
* @var object
*/
$v8,
/**
* Custom error handler
* @var callable
*/
$errorHandler;
/**
* Initialize by passing JS code as a string.
* The application source code is concatenated string
* of all custom components and app code
*
* @param string $libsrc React's source code
* @param string $appsrc Application source code
*/
function __construct($libsrc, $appsrc) {
$react = array();
// stubs, react
$react[] = "var console = {warn: function(){}, error: print};";
$react[] = "var global = global || this, self = self || this, window = window || this;";
$react[] = $libsrc;
$react[] = "var React = global.React, ReactDOM = global.ReactDOM, ReactDOMServer = global.ReactDOMServer;";
// app's components
$react[] = $appsrc;
$react[] = ';';
$concatenated = implode(";\n", $react);
$this->v8 = new V8Js();
$this->executeJS($concatenated);
}
/**
* Which components is to be rendered along with it's data
* E.g.
* $rjs->setComponent('MyTable', array('content' => $q4_results));
*
* @param string $component Component name
* @param mixed $data Any type of data to be passed as params
* when initializing the component. Optional.
* @return object $this instance
*/
function setComponent($component, $data = null) {
$this->component = $component;
$this->data = json_encode($data);
return $this;
}
/**
* Custom error handler. The default one var_dumps the exception
* and die()s.
*
* @param callable $err Callback passed to call_user_func()
* @return object $this instance
*/
function setErrorHandler($err) {
$this->errorHandler = $err;
return $this;
}
/**
* Returns the markup to print to the page
*
* @return string HTML string
*/
function getMarkup() {
$js = sprintf(
"print(ReactDOMServer.renderToString(React.createElement(%s, %s)))",
$this->component,
$this->data);
return $this->executeJS($js);
}
/**
* Returns JS to be inlined in the page (without <script> tags)
* It instantiates the client side, once the JS arrives
*
* NOTE: This class makes no attempt to load files JS so you can load it
* however is appropriate - from a CDN, asynchronously, etc.
*
* e.g. getJS('document.body');
* renders in body and doesn't retain a var
* e.g. getJS('#page', "GLOB");
* renders in element id="page" and assigns the component to
* a JavaScript variable named GLOB for later use if needed
* @param string $where A reference to a DOM object, or an ID
* for convenience if prefixed by a #. E.g. "#page"
* It will be passed to document.getElementById('page')
* @param string $return_var Optional name of JS variable to be assigned to
* the rendered component
* @return string JS code
*/
function getJS($where, $return_var = null) {
// special case for IDs
if (substr($where, 0, 1) === '#') {
$where = sprintf(
'document.getElementById("%s")',
substr($where, 1)
);
}
return
($return_var ? "var $return_var = " : "") .
sprintf(
"ReactDOM.render(React.createElement(%s, %s), %s);",
$this->component,
$this->data,
$where
);
}
/**
* Executes Javascript using V8JS, with primitive exception handling
*
* @param string $js JS code to be executed
* @return string The execution response
*/
private function executeJS($js) {
try {
ob_start();
$this->v8->executeString($js);
return ob_get_clean();
} catch (V8JsException $e) {
if ($this->errorHandler) {
call_user_func($this->errorHandler, $e);
} else {
// default error handler blows up bad
echo "<pre>";
echo $e->getMessage();
echo "</pre>";
die();
}
}
}
}