├── .gitignore ├── .php_cs ├── .travis.yml ├── README.md ├── composer.json ├── composer.lock ├── examples ├── node-server │ ├── package.json │ └── server.js ├── php-server │ ├── composer.json │ ├── composer.lock │ └── server.php └── v8.php ├── phpunit.xml.dist ├── src ├── React.php ├── ReactFactory.php ├── Renderer │ ├── FragmentProvidingRendererTrait.php │ ├── HTTPServerRenderer.php │ ├── LoggingRendererTrait.php │ ├── NodeProcessRenderer.php │ ├── NullRenderer.php │ ├── RendererInterface.php │ ├── SourceFilesRendererTrait.php │ ├── StdInProcessRendererTrait.php │ └── V8JsRenderer.php └── RuntimeFragmentProvider │ ├── GlobalObjectProvider.php │ ├── ProviderInterface.php │ └── SynchronousRequireProvider.php └── tests ├── bootstrap.php ├── cases └── Renderer │ └── V8JsRendererTest.php └── fixtures ├── TestComponent.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | /.idea/ 3 | node_modules 4 | /tests/fixtures/bundle.js -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | name('*.php') 5 | ->exclude(array( 6 | 'vendor' 7 | )) 8 | ->in(__DIR__); 9 | 10 | return Symfony\CS\Config\Config::create() 11 | ->finder($finder); -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | before_script: 9 | - composer self-update 10 | - composer install --dev -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReactJS PHP Render 2 | 3 | This library aims to provide multiple options for rendering React from PHP. 4 | 5 | ## Experimental 6 | 7 | The API is experimental and is likely to change. 8 | 9 | ## Concepts 10 | 11 | * Renderer (`ReactJS\Renderer\RendererInterface`) 12 | * This interface is implemented by mutiple renderers to provide different potential rendering options (HTTP Server, V8Js etc) 13 | * RuntimeFragmentProvider (`ReactJS\RuntimeFragmentProvider\ProviderInterface`) 14 | * This interface is implemented to provider different environment support (CommonJS, Globals etc) 15 | 16 | ## Usage 17 | 18 | Renderers can be used directly to generate either "mountable" React HTML (including checksums and ids), or to generate static markup. 19 | 20 | The React class (`ReactJS\React`) can be used to generate mountable React HTML along with JavaScript that will automatically mount the browser React component into the generated server rendered markup. 21 | 22 | ### Node 23 | 24 | When using a node process, users are required to provide source file(s) in an appropriate format for node to execute, these source file(s) need include: 25 | 26 | * React 27 | * The component you are attempting to render 28 | 29 | ```php 30 | $node = ReactJS\ReactFactory::createUsingNode( 31 | '/usr/bin/nodejs', 32 | ['bundle.js'] // bundle.js is a browserified bundle with React and TestComponent 33 | ); 34 | 35 | echo $node->renderAutoMountingComponent('./TestComponent'); 36 | ``` 37 | 38 | ### V8 39 | 40 | When using the V8Js php extension, users are required to provide source file(s) in the appropriate format for V8 to execute, these source file(s) need include: 41 | 42 | * React 43 | * The component you are attempting to render 44 | 45 | ```php 46 | $v8 = ReactJS\ReactFactory::createUsingV8( 47 | ['bundle.js'] // bundle.js is a browserified bundle with React and TestComponent 48 | ); 49 | 50 | echo $v8->renderAutoMountingComponent('./TestComponent'); 51 | ``` 52 | 53 | The result: 54 | 55 | ```html 56 |
57 | Some testing content 58 |
59 | 65 | ``` 66 | 67 | ## Installation (with composer) 68 | 69 | $ composer require camspiers/reactjs-php-render:dev-master 70 | 71 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "camspiers/reactjs-php-render", 3 | "description": "React rendering from PHP", 4 | "authors": [ 5 | { 6 | "name": "Cam Spiers", 7 | "email": "camspiers@gmail.com" 8 | }, 9 | { 10 | "name": "Pieter Vanderwerff", 11 | "email": "me@pieter.io" 12 | } 13 | ], 14 | "autoload": { 15 | "psr-4": { 16 | "ReactJS\\": "src/" 17 | } 18 | }, 19 | "require-dev": { 20 | "phpunit/phpunit": "~4.0" 21 | }, 22 | "require": { 23 | "php": ">=5.4.0", 24 | "psr/log": "~1.0", 25 | "guzzlehttp/guzzle": "~4.0", 26 | "symfony/process": "~2.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "426bffde737d8ada797a614bc4cad5f1", 8 | "packages": [ 9 | { 10 | "name": "guzzlehttp/guzzle", 11 | "version": "4.1.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/guzzle/guzzle.git", 15 | "reference": "577a69ff7d0a24e9576a2885c3a7afbaadd51ec1" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/577a69ff7d0a24e9576a2885c3a7afbaadd51ec1", 20 | "reference": "577a69ff7d0a24e9576a2885c3a7afbaadd51ec1", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-json": "*", 25 | "guzzlehttp/streams": "~1.0", 26 | "php": ">=5.4.0" 27 | }, 28 | "require-dev": { 29 | "ext-curl": "*", 30 | "phpunit/phpunit": "~4.0", 31 | "psr/log": "~1.0" 32 | }, 33 | "suggest": { 34 | "ext-curl": "Guzzle will use specific adapters if cURL is present" 35 | }, 36 | "type": "library", 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "4.1.x-dev" 40 | } 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "GuzzleHttp\\": "src/" 45 | }, 46 | "files": [ 47 | "src/functions.php" 48 | ] 49 | }, 50 | "notification-url": "https://packagist.org/downloads/", 51 | "license": [ 52 | "MIT" 53 | ], 54 | "authors": [ 55 | { 56 | "name": "Michael Dowling", 57 | "email": "mtdowling@gmail.com", 58 | "homepage": "https://github.com/mtdowling" 59 | } 60 | ], 61 | "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", 62 | "homepage": "http://guzzlephp.org/", 63 | "keywords": [ 64 | "client", 65 | "curl", 66 | "framework", 67 | "http", 68 | "http client", 69 | "rest", 70 | "web service" 71 | ], 72 | "time": "2014-06-08 20:00:20" 73 | }, 74 | { 75 | "name": "guzzlehttp/streams", 76 | "version": "1.1.0", 77 | "source": { 78 | "type": "git", 79 | "url": "https://github.com/guzzle/streams.git", 80 | "reference": "cf0c8c33ca95cc147efba4c714f630ee44767180" 81 | }, 82 | "dist": { 83 | "type": "zip", 84 | "url": "https://api.github.com/repos/guzzle/streams/zipball/cf0c8c33ca95cc147efba4c714f630ee44767180", 85 | "reference": "cf0c8c33ca95cc147efba4c714f630ee44767180", 86 | "shasum": "" 87 | }, 88 | "require": { 89 | "php": ">=5.4.0" 90 | }, 91 | "require-dev": { 92 | "phpunit/phpunit": "~4.0" 93 | }, 94 | "type": "library", 95 | "extra": { 96 | "branch-alias": { 97 | "dev-master": "1.0.x-dev" 98 | } 99 | }, 100 | "autoload": { 101 | "psr-4": { 102 | "GuzzleHttp\\Stream\\": "src/" 103 | }, 104 | "files": [ 105 | "src/functions.php" 106 | ] 107 | }, 108 | "notification-url": "https://packagist.org/downloads/", 109 | "license": [ 110 | "MIT" 111 | ], 112 | "authors": [ 113 | { 114 | "name": "Michael Dowling", 115 | "email": "mtdowling@gmail.com", 116 | "homepage": "https://github.com/mtdowling" 117 | } 118 | ], 119 | "description": "Provides a simple abstraction over streams of data (Guzzle 4+)", 120 | "homepage": "http://guzzlephp.org/", 121 | "keywords": [ 122 | "Guzzle", 123 | "stream" 124 | ], 125 | "time": "2014-04-03 04:48:24" 126 | }, 127 | { 128 | "name": "psr/log", 129 | "version": "1.0.0", 130 | "source": { 131 | "type": "git", 132 | "url": "https://github.com/php-fig/log.git", 133 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" 134 | }, 135 | "dist": { 136 | "type": "zip", 137 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", 138 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", 139 | "shasum": "" 140 | }, 141 | "type": "library", 142 | "autoload": { 143 | "psr-0": { 144 | "Psr\\Log\\": "" 145 | } 146 | }, 147 | "notification-url": "https://packagist.org/downloads/", 148 | "license": [ 149 | "MIT" 150 | ], 151 | "authors": [ 152 | { 153 | "name": "PHP-FIG", 154 | "homepage": "http://www.php-fig.org/" 155 | } 156 | ], 157 | "description": "Common interface for logging libraries", 158 | "keywords": [ 159 | "log", 160 | "psr", 161 | "psr-3" 162 | ], 163 | "time": "2012-12-21 11:40:51" 164 | }, 165 | { 166 | "name": "symfony/process", 167 | "version": "v2.5.0", 168 | "target-dir": "Symfony/Component/Process", 169 | "source": { 170 | "type": "git", 171 | "url": "https://github.com/symfony/Process.git", 172 | "reference": "5d7d78e23894544740219e006320678cfa4cd45b" 173 | }, 174 | "dist": { 175 | "type": "zip", 176 | "url": "https://api.github.com/repos/symfony/Process/zipball/5d7d78e23894544740219e006320678cfa4cd45b", 177 | "reference": "5d7d78e23894544740219e006320678cfa4cd45b", 178 | "shasum": "" 179 | }, 180 | "require": { 181 | "php": ">=5.3.3" 182 | }, 183 | "type": "library", 184 | "extra": { 185 | "branch-alias": { 186 | "dev-master": "2.5-dev" 187 | } 188 | }, 189 | "autoload": { 190 | "psr-0": { 191 | "Symfony\\Component\\Process\\": "" 192 | } 193 | }, 194 | "notification-url": "https://packagist.org/downloads/", 195 | "license": [ 196 | "MIT" 197 | ], 198 | "authors": [ 199 | { 200 | "name": "Fabien Potencier", 201 | "email": "fabien@symfony.com", 202 | "homepage": "http://fabien.potencier.org", 203 | "role": "Lead Developer" 204 | }, 205 | { 206 | "name": "Symfony Community", 207 | "homepage": "http://symfony.com/contributors" 208 | } 209 | ], 210 | "description": "Symfony Process Component", 211 | "homepage": "http://symfony.com", 212 | "time": "2014-05-23 09:02:52" 213 | } 214 | ], 215 | "packages-dev": [ 216 | { 217 | "name": "phpunit/php-code-coverage", 218 | "version": "2.0.8", 219 | "source": { 220 | "type": "git", 221 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 222 | "reference": "58401826c8cfc8fd689b60026e91c337df374bca" 223 | }, 224 | "dist": { 225 | "type": "zip", 226 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/58401826c8cfc8fd689b60026e91c337df374bca", 227 | "reference": "58401826c8cfc8fd689b60026e91c337df374bca", 228 | "shasum": "" 229 | }, 230 | "require": { 231 | "php": ">=5.3.3", 232 | "phpunit/php-file-iterator": "~1.3.1", 233 | "phpunit/php-text-template": "~1.2.0", 234 | "phpunit/php-token-stream": "~1.2.2", 235 | "sebastian/environment": "~1.0.0", 236 | "sebastian/version": "~1.0.3" 237 | }, 238 | "require-dev": { 239 | "ext-xdebug": ">=2.1.4", 240 | "phpunit/phpunit": "~4.0.14" 241 | }, 242 | "suggest": { 243 | "ext-dom": "*", 244 | "ext-xdebug": ">=2.2.1", 245 | "ext-xmlwriter": "*" 246 | }, 247 | "type": "library", 248 | "extra": { 249 | "branch-alias": { 250 | "dev-master": "2.0.x-dev" 251 | } 252 | }, 253 | "autoload": { 254 | "classmap": [ 255 | "src/" 256 | ] 257 | }, 258 | "notification-url": "https://packagist.org/downloads/", 259 | "include-path": [ 260 | "" 261 | ], 262 | "license": [ 263 | "BSD-3-Clause" 264 | ], 265 | "authors": [ 266 | { 267 | "name": "Sebastian Bergmann", 268 | "email": "sb@sebastian-bergmann.de", 269 | "role": "lead" 270 | } 271 | ], 272 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 273 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 274 | "keywords": [ 275 | "coverage", 276 | "testing", 277 | "xunit" 278 | ], 279 | "time": "2014-05-26 14:55:24" 280 | }, 281 | { 282 | "name": "phpunit/php-file-iterator", 283 | "version": "1.3.4", 284 | "source": { 285 | "type": "git", 286 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 287 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" 288 | }, 289 | "dist": { 290 | "type": "zip", 291 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", 292 | "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", 293 | "shasum": "" 294 | }, 295 | "require": { 296 | "php": ">=5.3.3" 297 | }, 298 | "type": "library", 299 | "autoload": { 300 | "classmap": [ 301 | "File/" 302 | ] 303 | }, 304 | "notification-url": "https://packagist.org/downloads/", 305 | "include-path": [ 306 | "" 307 | ], 308 | "license": [ 309 | "BSD-3-Clause" 310 | ], 311 | "authors": [ 312 | { 313 | "name": "Sebastian Bergmann", 314 | "email": "sb@sebastian-bergmann.de", 315 | "role": "lead" 316 | } 317 | ], 318 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 319 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 320 | "keywords": [ 321 | "filesystem", 322 | "iterator" 323 | ], 324 | "time": "2013-10-10 15:34:57" 325 | }, 326 | { 327 | "name": "phpunit/php-text-template", 328 | "version": "1.2.0", 329 | "source": { 330 | "type": "git", 331 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 332 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" 333 | }, 334 | "dist": { 335 | "type": "zip", 336 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", 337 | "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", 338 | "shasum": "" 339 | }, 340 | "require": { 341 | "php": ">=5.3.3" 342 | }, 343 | "type": "library", 344 | "autoload": { 345 | "classmap": [ 346 | "Text/" 347 | ] 348 | }, 349 | "notification-url": "https://packagist.org/downloads/", 350 | "include-path": [ 351 | "" 352 | ], 353 | "license": [ 354 | "BSD-3-Clause" 355 | ], 356 | "authors": [ 357 | { 358 | "name": "Sebastian Bergmann", 359 | "email": "sb@sebastian-bergmann.de", 360 | "role": "lead" 361 | } 362 | ], 363 | "description": "Simple template engine.", 364 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 365 | "keywords": [ 366 | "template" 367 | ], 368 | "time": "2014-01-30 17:20:04" 369 | }, 370 | { 371 | "name": "phpunit/php-timer", 372 | "version": "1.0.5", 373 | "source": { 374 | "type": "git", 375 | "url": "https://github.com/sebastianbergmann/php-timer.git", 376 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" 377 | }, 378 | "dist": { 379 | "type": "zip", 380 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 381 | "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", 382 | "shasum": "" 383 | }, 384 | "require": { 385 | "php": ">=5.3.3" 386 | }, 387 | "type": "library", 388 | "autoload": { 389 | "classmap": [ 390 | "PHP/" 391 | ] 392 | }, 393 | "notification-url": "https://packagist.org/downloads/", 394 | "include-path": [ 395 | "" 396 | ], 397 | "license": [ 398 | "BSD-3-Clause" 399 | ], 400 | "authors": [ 401 | { 402 | "name": "Sebastian Bergmann", 403 | "email": "sb@sebastian-bergmann.de", 404 | "role": "lead" 405 | } 406 | ], 407 | "description": "Utility class for timing", 408 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 409 | "keywords": [ 410 | "timer" 411 | ], 412 | "time": "2013-08-02 07:42:54" 413 | }, 414 | { 415 | "name": "phpunit/php-token-stream", 416 | "version": "1.2.2", 417 | "source": { 418 | "type": "git", 419 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 420 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" 421 | }, 422 | "dist": { 423 | "type": "zip", 424 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", 425 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", 426 | "shasum": "" 427 | }, 428 | "require": { 429 | "ext-tokenizer": "*", 430 | "php": ">=5.3.3" 431 | }, 432 | "type": "library", 433 | "extra": { 434 | "branch-alias": { 435 | "dev-master": "1.2-dev" 436 | } 437 | }, 438 | "autoload": { 439 | "classmap": [ 440 | "PHP/" 441 | ] 442 | }, 443 | "notification-url": "https://packagist.org/downloads/", 444 | "include-path": [ 445 | "" 446 | ], 447 | "license": [ 448 | "BSD-3-Clause" 449 | ], 450 | "authors": [ 451 | { 452 | "name": "Sebastian Bergmann", 453 | "email": "sb@sebastian-bergmann.de", 454 | "role": "lead" 455 | } 456 | ], 457 | "description": "Wrapper around PHP's tokenizer extension.", 458 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 459 | "keywords": [ 460 | "tokenizer" 461 | ], 462 | "time": "2014-03-03 05:10:30" 463 | }, 464 | { 465 | "name": "phpunit/phpunit", 466 | "version": "4.1.3", 467 | "source": { 468 | "type": "git", 469 | "url": "https://github.com/sebastianbergmann/phpunit.git", 470 | "reference": "939cb801b3b2aa253aedd0b279f40bb8f35cec91" 471 | }, 472 | "dist": { 473 | "type": "zip", 474 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/939cb801b3b2aa253aedd0b279f40bb8f35cec91", 475 | "reference": "939cb801b3b2aa253aedd0b279f40bb8f35cec91", 476 | "shasum": "" 477 | }, 478 | "require": { 479 | "ext-dom": "*", 480 | "ext-json": "*", 481 | "ext-pcre": "*", 482 | "ext-reflection": "*", 483 | "ext-spl": "*", 484 | "php": ">=5.3.3", 485 | "phpunit/php-code-coverage": "~2.0", 486 | "phpunit/php-file-iterator": "~1.3.1", 487 | "phpunit/php-text-template": "~1.2", 488 | "phpunit/php-timer": "~1.0.2", 489 | "phpunit/phpunit-mock-objects": "~2.1", 490 | "sebastian/comparator": "~1.0", 491 | "sebastian/diff": "~1.1", 492 | "sebastian/environment": "~1.0", 493 | "sebastian/exporter": "~1.0", 494 | "sebastian/version": "~1.0", 495 | "symfony/yaml": "~2.0" 496 | }, 497 | "suggest": { 498 | "phpunit/php-invoker": "~1.1" 499 | }, 500 | "bin": [ 501 | "phpunit" 502 | ], 503 | "type": "library", 504 | "extra": { 505 | "branch-alias": { 506 | "dev-master": "4.1.x-dev" 507 | } 508 | }, 509 | "autoload": { 510 | "classmap": [ 511 | "src/" 512 | ] 513 | }, 514 | "notification-url": "https://packagist.org/downloads/", 515 | "include-path": [ 516 | "", 517 | "../../symfony/yaml/" 518 | ], 519 | "license": [ 520 | "BSD-3-Clause" 521 | ], 522 | "authors": [ 523 | { 524 | "name": "Sebastian Bergmann", 525 | "email": "sebastian@phpunit.de", 526 | "role": "lead" 527 | } 528 | ], 529 | "description": "The PHP Unit Testing framework.", 530 | "homepage": "http://www.phpunit.de/", 531 | "keywords": [ 532 | "phpunit", 533 | "testing", 534 | "xunit" 535 | ], 536 | "time": "2014-06-11 14:15:47" 537 | }, 538 | { 539 | "name": "phpunit/phpunit-mock-objects", 540 | "version": "2.1.4", 541 | "source": { 542 | "type": "git", 543 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 544 | "reference": "1a894a16b6c15fcdc5ef2b110f0e6233952c9b0f" 545 | }, 546 | "dist": { 547 | "type": "zip", 548 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/1a894a16b6c15fcdc5ef2b110f0e6233952c9b0f", 549 | "reference": "1a894a16b6c15fcdc5ef2b110f0e6233952c9b0f", 550 | "shasum": "" 551 | }, 552 | "require": { 553 | "php": ">=5.3.3", 554 | "phpunit/php-text-template": "~1.2" 555 | }, 556 | "require-dev": { 557 | "phpunit/phpunit": "~4.1" 558 | }, 559 | "suggest": { 560 | "ext-soap": "*" 561 | }, 562 | "type": "library", 563 | "extra": { 564 | "branch-alias": { 565 | "dev-master": "2.1.x-dev" 566 | } 567 | }, 568 | "autoload": { 569 | "classmap": [ 570 | "src/" 571 | ] 572 | }, 573 | "notification-url": "https://packagist.org/downloads/", 574 | "include-path": [ 575 | "" 576 | ], 577 | "license": [ 578 | "BSD-3-Clause" 579 | ], 580 | "authors": [ 581 | { 582 | "name": "Sebastian Bergmann", 583 | "email": "sb@sebastian-bergmann.de", 584 | "role": "lead" 585 | } 586 | ], 587 | "description": "Mock Object library for PHPUnit", 588 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 589 | "keywords": [ 590 | "mock", 591 | "xunit" 592 | ], 593 | "time": "2014-06-07 16:22:57" 594 | }, 595 | { 596 | "name": "sebastian/comparator", 597 | "version": "1.0.0", 598 | "source": { 599 | "type": "git", 600 | "url": "https://github.com/sebastianbergmann/comparator.git", 601 | "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2" 602 | }, 603 | "dist": { 604 | "type": "zip", 605 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2", 606 | "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2", 607 | "shasum": "" 608 | }, 609 | "require": { 610 | "php": ">=5.3.3", 611 | "sebastian/diff": "~1.1", 612 | "sebastian/exporter": "~1.0" 613 | }, 614 | "require-dev": { 615 | "phpunit/phpunit": "~4.1" 616 | }, 617 | "type": "library", 618 | "extra": { 619 | "branch-alias": { 620 | "dev-master": "1.0.x-dev" 621 | } 622 | }, 623 | "autoload": { 624 | "classmap": [ 625 | "src/" 626 | ] 627 | }, 628 | "notification-url": "https://packagist.org/downloads/", 629 | "license": [ 630 | "BSD-3-Clause" 631 | ], 632 | "authors": [ 633 | { 634 | "name": "Sebastian Bergmann", 635 | "email": "sebastian@phpunit.de", 636 | "role": "lead" 637 | }, 638 | { 639 | "name": "Jeff Welch", 640 | "email": "whatthejeff@gmail.com" 641 | }, 642 | { 643 | "name": "Volker Dusch", 644 | "email": "github@wallbash.com" 645 | }, 646 | { 647 | "name": "Bernhard Schussek", 648 | "email": "bschussek@2bepublished.at" 649 | } 650 | ], 651 | "description": "Provides the functionality to compare PHP values for equality", 652 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 653 | "keywords": [ 654 | "comparator", 655 | "compare", 656 | "equality" 657 | ], 658 | "time": "2014-05-02 07:05:58" 659 | }, 660 | { 661 | "name": "sebastian/diff", 662 | "version": "1.1.0", 663 | "source": { 664 | "type": "git", 665 | "url": "https://github.com/sebastianbergmann/diff.git", 666 | "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d" 667 | }, 668 | "dist": { 669 | "type": "zip", 670 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", 671 | "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", 672 | "shasum": "" 673 | }, 674 | "require": { 675 | "php": ">=5.3.3" 676 | }, 677 | "type": "library", 678 | "extra": { 679 | "branch-alias": { 680 | "dev-master": "1.1-dev" 681 | } 682 | }, 683 | "autoload": { 684 | "classmap": [ 685 | "src/" 686 | ] 687 | }, 688 | "notification-url": "https://packagist.org/downloads/", 689 | "license": [ 690 | "BSD-3-Clause" 691 | ], 692 | "authors": [ 693 | { 694 | "name": "Sebastian Bergmann", 695 | "email": "sebastian@phpunit.de", 696 | "role": "lead" 697 | }, 698 | { 699 | "name": "Kore Nordmann", 700 | "email": "mail@kore-nordmann.de" 701 | } 702 | ], 703 | "description": "Diff implementation", 704 | "homepage": "http://www.github.com/sebastianbergmann/diff", 705 | "keywords": [ 706 | "diff" 707 | ], 708 | "time": "2013-08-03 16:46:33" 709 | }, 710 | { 711 | "name": "sebastian/environment", 712 | "version": "1.0.0", 713 | "source": { 714 | "type": "git", 715 | "url": "https://github.com/sebastianbergmann/environment.git", 716 | "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a" 717 | }, 718 | "dist": { 719 | "type": "zip", 720 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/79517609ec01139cd7e9fded0dd7ce08c952ef6a", 721 | "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a", 722 | "shasum": "" 723 | }, 724 | "require": { 725 | "php": ">=5.3.3" 726 | }, 727 | "require-dev": { 728 | "phpunit/phpunit": "4.0.*@dev" 729 | }, 730 | "type": "library", 731 | "extra": { 732 | "branch-alias": { 733 | "dev-master": "1.0.x-dev" 734 | } 735 | }, 736 | "autoload": { 737 | "classmap": [ 738 | "src/" 739 | ] 740 | }, 741 | "notification-url": "https://packagist.org/downloads/", 742 | "license": [ 743 | "BSD-3-Clause" 744 | ], 745 | "authors": [ 746 | { 747 | "name": "Sebastian Bergmann", 748 | "email": "sebastian@phpunit.de", 749 | "role": "lead" 750 | } 751 | ], 752 | "description": "Provides functionality to handle HHVM/PHP environments", 753 | "homepage": "http://www.github.com/sebastianbergmann/environment", 754 | "keywords": [ 755 | "Xdebug", 756 | "environment", 757 | "hhvm" 758 | ], 759 | "time": "2014-02-18 16:17:19" 760 | }, 761 | { 762 | "name": "sebastian/exporter", 763 | "version": "1.0.1", 764 | "source": { 765 | "type": "git", 766 | "url": "https://github.com/sebastianbergmann/exporter.git", 767 | "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529" 768 | }, 769 | "dist": { 770 | "type": "zip", 771 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529", 772 | "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529", 773 | "shasum": "" 774 | }, 775 | "require": { 776 | "php": ">=5.3.3" 777 | }, 778 | "require-dev": { 779 | "phpunit/phpunit": "4.0.*@dev" 780 | }, 781 | "type": "library", 782 | "extra": { 783 | "branch-alias": { 784 | "dev-master": "1.0.x-dev" 785 | } 786 | }, 787 | "autoload": { 788 | "classmap": [ 789 | "src/" 790 | ] 791 | }, 792 | "notification-url": "https://packagist.org/downloads/", 793 | "license": [ 794 | "BSD-3-Clause" 795 | ], 796 | "authors": [ 797 | { 798 | "name": "Sebastian Bergmann", 799 | "email": "sebastian@phpunit.de", 800 | "role": "lead" 801 | }, 802 | { 803 | "name": "Jeff Welch", 804 | "email": "whatthejeff@gmail.com" 805 | }, 806 | { 807 | "name": "Volker Dusch", 808 | "email": "github@wallbash.com" 809 | }, 810 | { 811 | "name": "Adam Harvey", 812 | "email": "aharvey@php.net", 813 | "role": "Lead" 814 | }, 815 | { 816 | "name": "Bernhard Schussek", 817 | "email": "bschussek@2bepublished.at" 818 | } 819 | ], 820 | "description": "Provides the functionality to export PHP variables for visualization", 821 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 822 | "keywords": [ 823 | "export", 824 | "exporter" 825 | ], 826 | "time": "2014-02-16 08:26:31" 827 | }, 828 | { 829 | "name": "sebastian/version", 830 | "version": "1.0.3", 831 | "source": { 832 | "type": "git", 833 | "url": "https://github.com/sebastianbergmann/version.git", 834 | "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43" 835 | }, 836 | "dist": { 837 | "type": "zip", 838 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", 839 | "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", 840 | "shasum": "" 841 | }, 842 | "type": "library", 843 | "autoload": { 844 | "classmap": [ 845 | "src/" 846 | ] 847 | }, 848 | "notification-url": "https://packagist.org/downloads/", 849 | "license": [ 850 | "BSD-3-Clause" 851 | ], 852 | "authors": [ 853 | { 854 | "name": "Sebastian Bergmann", 855 | "email": "sebastian@phpunit.de", 856 | "role": "lead" 857 | } 858 | ], 859 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 860 | "homepage": "https://github.com/sebastianbergmann/version", 861 | "time": "2014-03-07 15:35:33" 862 | }, 863 | { 864 | "name": "symfony/yaml", 865 | "version": "v2.5.0", 866 | "target-dir": "Symfony/Component/Yaml", 867 | "source": { 868 | "type": "git", 869 | "url": "https://github.com/symfony/Yaml.git", 870 | "reference": "b4b09c68ec2f2727574544ef0173684281a5033c" 871 | }, 872 | "dist": { 873 | "type": "zip", 874 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/b4b09c68ec2f2727574544ef0173684281a5033c", 875 | "reference": "b4b09c68ec2f2727574544ef0173684281a5033c", 876 | "shasum": "" 877 | }, 878 | "require": { 879 | "php": ">=5.3.3" 880 | }, 881 | "type": "library", 882 | "extra": { 883 | "branch-alias": { 884 | "dev-master": "2.5-dev" 885 | } 886 | }, 887 | "autoload": { 888 | "psr-0": { 889 | "Symfony\\Component\\Yaml\\": "" 890 | } 891 | }, 892 | "notification-url": "https://packagist.org/downloads/", 893 | "license": [ 894 | "MIT" 895 | ], 896 | "authors": [ 897 | { 898 | "name": "Fabien Potencier", 899 | "email": "fabien@symfony.com", 900 | "homepage": "http://fabien.potencier.org", 901 | "role": "Lead Developer" 902 | }, 903 | { 904 | "name": "Symfony Community", 905 | "homepage": "http://symfony.com/contributors" 906 | } 907 | ], 908 | "description": "Symfony Yaml Component", 909 | "homepage": "http://symfony.com", 910 | "time": "2014-05-16 14:25:18" 911 | } 912 | ], 913 | "aliases": [], 914 | "minimum-stability": "stable", 915 | "stability-flags": [], 916 | "platform": { 917 | "php": ">=5.4.0" 918 | }, 919 | "platform-dev": [] 920 | } 921 | -------------------------------------------------------------------------------- /examples/node-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-node-server", 3 | "version": "0.0.0", 4 | "description": "", 5 | "author": "Cam Spiers", 6 | "main": "server.js", 7 | "scripts": { 8 | "start": "node server.js" 9 | }, 10 | "dependencies": { 11 | "react": "0.10.0", 12 | "express": "3.5.1", 13 | "node-jsx": "0.10.0" 14 | } 15 | } -------------------------------------------------------------------------------- /examples/node-server/server.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var app = express(); 5 | 6 | require('node-jsx').install(); 7 | app.use(express.urlencoded()); 8 | 9 | function validateRequest(data) { 10 | var errors = []; 11 | 12 | if (!data['renderType']) { 13 | errors.push('renderType empty'); 14 | } else if (['mountable', 'static'].indexOf(data['renderType']) < 0) { 15 | errors.push('Invalid renderType'); 16 | } 17 | 18 | if (!data['componentPath']) { 19 | errors.push('componentPath empty'); 20 | } 21 | 22 | if (!data['props']) { 23 | errors.push('props empty'); 24 | } 25 | 26 | return errors; 27 | } 28 | 29 | app.post('/', function(req, res){ 30 | var errors = validateRequest(req.body); 31 | 32 | if (errors.length) { 33 | res.send(errors); 34 | return; 35 | } 36 | 37 | var reactFunction; 38 | 39 | if (req.body['renderType'] === 'static') { 40 | reactFunction = 'renderComponentToStaticMarkup'; 41 | } else { 42 | reactFunction = 'renderComponentToString'; 43 | } 44 | 45 | var component = require(path.resolve(req.body['componentPath'])); 46 | var props = JSON.parse(req.body['props'] || '{}'); 47 | 48 | res.send(React[reactFunction](component(props))); 49 | }); 50 | -------------------------------------------------------------------------------- /examples/php-server/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "react/http": "~0.4", 4 | "docopt/docopt": "~0.6" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/php-server/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "4c7a7eac28748974dced21d89316a37b", 8 | "packages": [ 9 | { 10 | "name": "docopt/docopt", 11 | "version": "0.6.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/docopt/docopt.php.git", 15 | "reference": "ad2afba96771ab5d8c29c36d720461886b338d78" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/docopt/docopt.php/zipball/ad2afba96771ab5d8c29c36d720461886b338d78", 20 | "reference": "ad2afba96771ab5d8c29c36d720461886b338d78", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.0" 25 | }, 26 | "type": "library", 27 | "autoload": { 28 | "classmap": [ 29 | "src/docopt.php" 30 | ] 31 | }, 32 | "notification-url": "https://packagist.org/downloads/", 33 | "license": [ 34 | "MIT" 35 | ], 36 | "authors": [ 37 | { 38 | "name": "Blake Williams", 39 | "email": "code@shabbyrobe.org", 40 | "homepage": "http://docopt.org/", 41 | "role": "Developer" 42 | } 43 | ], 44 | "description": "Port of Python's docopt for PHP 5.3", 45 | "homepage": "http://github.com/docopt/docopt.php", 46 | "keywords": [ 47 | "cli", 48 | "docs" 49 | ], 50 | "time": "2013-01-24 10:17:13" 51 | }, 52 | { 53 | "name": "evenement/evenement", 54 | "version": "v2.0.0", 55 | "source": { 56 | "type": "git", 57 | "url": "https://github.com/igorw/evenement.git", 58 | "reference": "f6e843799fd4f4184d54d8fc7b5b3551c9fa803e" 59 | }, 60 | "dist": { 61 | "type": "zip", 62 | "url": "https://api.github.com/repos/igorw/evenement/zipball/f6e843799fd4f4184d54d8fc7b5b3551c9fa803e", 63 | "reference": "f6e843799fd4f4184d54d8fc7b5b3551c9fa803e", 64 | "shasum": "" 65 | }, 66 | "require": { 67 | "php": ">=5.4.0" 68 | }, 69 | "type": "library", 70 | "extra": { 71 | "branch-alias": { 72 | "dev-master": "2.0-dev" 73 | } 74 | }, 75 | "autoload": { 76 | "psr-0": { 77 | "Evenement": "src" 78 | } 79 | }, 80 | "notification-url": "https://packagist.org/downloads/", 81 | "license": [ 82 | "MIT" 83 | ], 84 | "authors": [ 85 | { 86 | "name": "Igor Wiedler", 87 | "email": "igor@wiedler.ch", 88 | "homepage": "http://wiedler.ch/igor/" 89 | } 90 | ], 91 | "description": "Événement is a very simple event dispatching library for PHP", 92 | "keywords": [ 93 | "event-dispatcher", 94 | "event-emitter" 95 | ], 96 | "time": "2012-11-02 14:49:47" 97 | }, 98 | { 99 | "name": "guzzle/parser", 100 | "version": "v3.9.1", 101 | "target-dir": "Guzzle/Parser", 102 | "source": { 103 | "type": "git", 104 | "url": "https://github.com/guzzle/parser.git", 105 | "reference": "6874d171318a8e93eb6d224cf85e4678490b625c" 106 | }, 107 | "dist": { 108 | "type": "zip", 109 | "url": "https://api.github.com/repos/guzzle/parser/zipball/6874d171318a8e93eb6d224cf85e4678490b625c", 110 | "reference": "6874d171318a8e93eb6d224cf85e4678490b625c", 111 | "shasum": "" 112 | }, 113 | "require": { 114 | "php": ">=5.3.2" 115 | }, 116 | "type": "library", 117 | "extra": { 118 | "branch-alias": { 119 | "dev-master": "3.7-dev" 120 | } 121 | }, 122 | "autoload": { 123 | "psr-0": { 124 | "Guzzle\\Parser": "" 125 | } 126 | }, 127 | "notification-url": "https://packagist.org/downloads/", 128 | "license": [ 129 | "MIT" 130 | ], 131 | "description": "Interchangeable parsers used by Guzzle", 132 | "homepage": "http://guzzlephp.org/", 133 | "keywords": [ 134 | "URI Template", 135 | "cookie", 136 | "http", 137 | "message", 138 | "url" 139 | ], 140 | "time": "2014-02-05 18:29:46" 141 | }, 142 | { 143 | "name": "react/event-loop", 144 | "version": "v0.4.1", 145 | "source": { 146 | "type": "git", 147 | "url": "https://github.com/reactphp/event-loop.git", 148 | "reference": "18c5297087ca01de85518e2b55078f444144aa1b" 149 | }, 150 | "dist": { 151 | "type": "zip", 152 | "url": "https://api.github.com/repos/reactphp/event-loop/zipball/18c5297087ca01de85518e2b55078f444144aa1b", 153 | "reference": "18c5297087ca01de85518e2b55078f444144aa1b", 154 | "shasum": "" 155 | }, 156 | "require": { 157 | "php": ">=5.4.0" 158 | }, 159 | "suggest": { 160 | "ext-event": "~1.0", 161 | "ext-libev": "*", 162 | "ext-libevent": ">=0.1.0" 163 | }, 164 | "type": "library", 165 | "extra": { 166 | "branch-alias": { 167 | "dev-master": "0.4-dev" 168 | } 169 | }, 170 | "autoload": { 171 | "psr-4": { 172 | "React\\EventLoop\\": "" 173 | } 174 | }, 175 | "notification-url": "https://packagist.org/downloads/", 176 | "license": [ 177 | "MIT" 178 | ], 179 | "description": "Event loop abstraction layer that libraries can use for evented I/O.", 180 | "keywords": [ 181 | "event-loop" 182 | ], 183 | "time": "2014-02-26 17:36:58" 184 | }, 185 | { 186 | "name": "react/http", 187 | "version": "v0.4.0", 188 | "source": { 189 | "type": "git", 190 | "url": "https://github.com/reactphp/http.git", 191 | "reference": "7b9d293b7a3f73acd840a341497e267d8562d637" 192 | }, 193 | "dist": { 194 | "type": "zip", 195 | "url": "https://api.github.com/repos/reactphp/http/zipball/7b9d293b7a3f73acd840a341497e267d8562d637", 196 | "reference": "7b9d293b7a3f73acd840a341497e267d8562d637", 197 | "shasum": "" 198 | }, 199 | "require": { 200 | "guzzle/parser": "~3.0", 201 | "php": ">=5.4.0", 202 | "react/socket": "0.4.*" 203 | }, 204 | "type": "library", 205 | "extra": { 206 | "branch-alias": { 207 | "dev-master": "0.4-dev" 208 | } 209 | }, 210 | "autoload": { 211 | "psr-4": { 212 | "React\\Http\\": "" 213 | } 214 | }, 215 | "notification-url": "https://packagist.org/downloads/", 216 | "license": [ 217 | "MIT" 218 | ], 219 | "description": "Library for building an evented http server.", 220 | "keywords": [ 221 | "http" 222 | ], 223 | "time": "2014-02-02 01:11:26" 224 | }, 225 | { 226 | "name": "react/socket", 227 | "version": "v0.4.2", 228 | "source": { 229 | "type": "git", 230 | "url": "https://github.com/reactphp/socket.git", 231 | "reference": "a6acf405ca53fc6cfbfe7c77778ededff46aa7cc" 232 | }, 233 | "dist": { 234 | "type": "zip", 235 | "url": "https://api.github.com/repos/reactphp/socket/zipball/a6acf405ca53fc6cfbfe7c77778ededff46aa7cc", 236 | "reference": "a6acf405ca53fc6cfbfe7c77778ededff46aa7cc", 237 | "shasum": "" 238 | }, 239 | "require": { 240 | "evenement/evenement": "~2.0", 241 | "php": ">=5.4.0", 242 | "react/event-loop": "0.4.*", 243 | "react/stream": "0.4.*" 244 | }, 245 | "type": "library", 246 | "extra": { 247 | "branch-alias": { 248 | "dev-master": "0.4-dev" 249 | } 250 | }, 251 | "autoload": { 252 | "psr-4": { 253 | "React\\Socket\\": "src" 254 | } 255 | }, 256 | "notification-url": "https://packagist.org/downloads/", 257 | "license": [ 258 | "MIT" 259 | ], 260 | "description": "Library for building an evented socket server.", 261 | "keywords": [ 262 | "Socket" 263 | ], 264 | "time": "2014-05-25 17:02:16" 265 | }, 266 | { 267 | "name": "react/stream", 268 | "version": "v0.4.1", 269 | "source": { 270 | "type": "git", 271 | "url": "https://github.com/reactphp/stream.git", 272 | "reference": "9db28e85a6fe7b57fad5e8c036f3434bcb8c8f60" 273 | }, 274 | "dist": { 275 | "type": "zip", 276 | "url": "https://api.github.com/repos/reactphp/stream/zipball/9db28e85a6fe7b57fad5e8c036f3434bcb8c8f60", 277 | "reference": "9db28e85a6fe7b57fad5e8c036f3434bcb8c8f60", 278 | "shasum": "" 279 | }, 280 | "require": { 281 | "evenement/evenement": "~2.0", 282 | "php": ">=5.4.0" 283 | }, 284 | "suggest": { 285 | "react/event-loop": "0.4.*", 286 | "react/promise": "~2.0" 287 | }, 288 | "type": "library", 289 | "extra": { 290 | "branch-alias": { 291 | "dev-master": "0.4-dev" 292 | } 293 | }, 294 | "autoload": { 295 | "psr-4": { 296 | "React\\Stream\\": "" 297 | } 298 | }, 299 | "notification-url": "https://packagist.org/downloads/", 300 | "license": [ 301 | "MIT" 302 | ], 303 | "description": "Basic readable and writable stream interfaces that support piping.", 304 | "keywords": [ 305 | "pipe", 306 | "stream" 307 | ], 308 | "time": "2014-03-30 17:19:02" 309 | } 310 | ], 311 | "packages-dev": [], 312 | "aliases": [], 313 | "minimum-stability": "stable", 314 | "stability-flags": [], 315 | "platform": [], 316 | "platform-dev": [] 317 | } 318 | -------------------------------------------------------------------------------- /examples/php-server/server.php: -------------------------------------------------------------------------------- 1 | on('request', function ($request, $response) use ($react) { 51 | $requestBody = ''; 52 | $headers = $request->getHeaders(); 53 | $contentLength = (int) $headers['Content-Length']; 54 | $receivedData = 0; 55 | 56 | $request->on('data', function ($data) use ( 57 | $react, 58 | $request, 59 | $response, 60 | &$requestBody, 61 | &$receivedData, 62 | $contentLength 63 | ) { 64 | $requestBody .= $data; 65 | $receivedData += strlen($data); 66 | if ($receivedData >= $contentLength) { 67 | parse_str($requestBody, $requestData); 68 | 69 | $errors = validateRequest($requestData); 70 | 71 | if (count($errors)) { 72 | $response->writeHead( 73 | 400, 74 | ['Content-Type' => 'application/json'] 75 | ); 76 | $response->end(json_encode($errors)); 77 | return; 78 | } 79 | 80 | $props = json_decode($requestData['props']); 81 | 82 | if ($requestData['renderType'] === 'static') { 83 | $reactFunction = 'renderStaticComponent'; 84 | } else { 85 | $reactFunction = 'renderMountableComponent'; 86 | } 87 | 88 | $markup = $react->$reactFunction( 89 | $requestData['componentPath'], 90 | $props 91 | ); 92 | 93 | $response->writeHead(200, array('Content-Type' => 'text/html')); 94 | $response->end($markup); 95 | } 96 | }); 97 | }); 98 | 99 | $socket->listen($args['-p'] ?: 3000); 100 | $loop->run(); -------------------------------------------------------------------------------- /examples/v8.php: -------------------------------------------------------------------------------- 1 | renderAutoMountingComponent('./TestComponent'), PHP_EOL; 10 | echo $v8->renderMountableComponent('./TestComponent'), PHP_EOL; 11 | echo $v8->renderStaticComponent('./TestComponesnt'), PHP_EOL; -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./tests/cases 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/React.php: -------------------------------------------------------------------------------- 1 | renderer = $renderer ?: new NullRenderer(); 35 | $this->fragmentProvider = $fragmentProvider ?: new SynchronousRequireProvider(); 36 | } 37 | 38 | /** 39 | * @param $componentPath 40 | * @param null $props 41 | * @param null $id 42 | * @return string 43 | */ 44 | public function renderAutoMountingComponent($componentPath, $props = null, $id = null) 45 | { 46 | $containerId = $id ?: uniqid(); 47 | 48 | return sprintf( 49 | "
%s
", 50 | $containerId, 51 | $this->renderer->renderMountableComponent($componentPath, $props), 52 | $this->fragmentProvider->getReact(), 53 | $this->fragmentProvider->getComponent($componentPath), 54 | json_encode($props), 55 | json_encode($containerId) 56 | ); 57 | } 58 | 59 | /** 60 | * @param $componentPath 61 | * @param array|void $props 62 | * @return string 63 | */ 64 | public function renderMountableComponent($componentPath, $props = null) 65 | { 66 | return $this->renderer->renderMountableComponent($componentPath, $props); 67 | } 68 | 69 | /** 70 | * @param $componentPath 71 | * @param array|void $props 72 | * @return string 73 | */ 74 | public function renderStaticComponent($componentPath, $props = null) 75 | { 76 | return $this->renderer->renderStaticComponent($componentPath, $props); 77 | } 78 | } -------------------------------------------------------------------------------- /src/ReactFactory.php: -------------------------------------------------------------------------------- 1 | fragmentProvider = $fragmentProvider; 21 | } 22 | 23 | /** 24 | * @return \ReactJS\RuntimeFragmentProvider\ProviderInterface 25 | */ 26 | public function getFragmentProvider() 27 | { 28 | return $this->fragmentProvider ?: new SynchronousRequireProvider(); 29 | } 30 | } -------------------------------------------------------------------------------- /src/Renderer/HTTPServerRenderer.php: -------------------------------------------------------------------------------- 1 | host = (string) $host; 55 | $this->port = (int) $port; 56 | $this->path = (string) $path; 57 | $this->ssl = (bool) $ssl; 58 | $this->logger = $logger; 59 | } 60 | 61 | /** 62 | * Renders a component that is able to be mounted via JavaScript in the browser 63 | * @param $componentPath 64 | * @param array|void $props 65 | * @return string 66 | */ 67 | public function renderMountableComponent($componentPath, $props = null) 68 | { 69 | return $this->render('mountable', $componentPath, $props); 70 | } 71 | 72 | /** 73 | * Renders a static component unable to be mounted via JavaScript 74 | * @param $componentPath 75 | * @param array|void $props 76 | * @return string 77 | */ 78 | public function renderStaticComponent($componentPath, $props = null) 79 | { 80 | return $this->render('static', $componentPath, $props); 81 | } 82 | 83 | /** 84 | * @param $reactRenderType 85 | * @param $componentPath 86 | * @param null $props 87 | * @return string 88 | */ 89 | private function render($reactRenderType, $componentPath, $props = null) 90 | { 91 | $markup = ''; 92 | 93 | $client = new Client(); 94 | 95 | try { 96 | $response = $client->post( 97 | sprintf( 98 | "http%s://%s:%s/%s", 99 | $this->ssl ? 's' : '', 100 | $this->host, 101 | $this->port, 102 | ltrim($this->path, '/') 103 | ), 104 | [ 105 | "body" => [ 106 | "renderType" => $reactRenderType, 107 | "componentPath" => $componentPath, 108 | "props" => json_encode($props) 109 | ] 110 | ] 111 | ); 112 | 113 | $markup = (string) $response->getBody(); 114 | } catch (RequestException $e) { 115 | if ($this->logger instanceof LoggerInterface) { 116 | $this->logger->error($e->getMessage()); 117 | } 118 | } 119 | 120 | return $markup; 121 | } 122 | } -------------------------------------------------------------------------------- /src/Renderer/LoggingRendererTrait.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 25 | } 26 | 27 | /** 28 | * @return \Psr\Log\LoggerInterface 29 | */ 30 | public function getLogger() 31 | { 32 | return $this->logger; 33 | } 34 | 35 | /** 36 | * @param string $logType 37 | */ 38 | public function setLogType($logType) 39 | { 40 | $this->logType = $logType; 41 | } 42 | 43 | /** 44 | * @return string 45 | */ 46 | public function getLogType() 47 | { 48 | return $this->logType; 49 | } 50 | 51 | /** 52 | * @param $message 53 | * @param array $context 54 | */ 55 | protected function log($message, array $context = []) 56 | { 57 | if ($this->logger instanceof LoggerInterface) { 58 | $this->logger->{$this->logType}($message, $context); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Renderer/NodeProcessRenderer.php: -------------------------------------------------------------------------------- 1 | setBin($bin); 28 | $this->setSourceFiles($sourceFiles); 29 | if ($fragmentProvider) { 30 | $this->setFragmentProvider($fragmentProvider); 31 | } 32 | if ($logger) { 33 | $this->setLogger($logger); 34 | } 35 | } 36 | 37 | /** 38 | * Renders a component that is able to be mounted via JavaScript in the browser 39 | * @param $componentPath 40 | * @param array|void $props 41 | * @return string 42 | */ 43 | public function renderMountableComponent($componentPath, $props = null) 44 | { 45 | return $this->getOutput( 46 | $this->getJavaScript( 47 | 'renderComponentToString', 48 | $componentPath, 49 | $props 50 | ) 51 | ); 52 | } 53 | 54 | /** 55 | * Renders a static component unable to be mounted via JavaScript 56 | * @param $componentPath 57 | * @param array|void $props 58 | * @return string 59 | */ 60 | public function renderStaticComponent($componentPath, $props = null) 61 | { 62 | return $this->getOutput( 63 | $this->getJavaScript( 64 | 'renderComponentToStaticMarkup', 65 | $componentPath, 66 | $props 67 | ) 68 | ); 69 | } 70 | 71 | /** 72 | * @param $reactFunction 73 | * @param $componentPath 74 | * @param null $props 75 | * @return string 76 | */ 77 | protected function getJavaScript($reactFunction, $componentPath, $props = null) 78 | { 79 | $fragmentProvider = $this->getFragmentProvider(); 80 | 81 | $javascript = []; 82 | 83 | foreach ($this->sourceFiles as $sourceFile) { 84 | $javascript[] = file_get_contents($sourceFile) . ";"; 85 | } 86 | 87 | $javascript[] = sprintf( 88 | "console.log(%s.%s(%s(%s)));", 89 | $fragmentProvider->getReact(), 90 | $reactFunction, 91 | $fragmentProvider->getComponent($componentPath), 92 | json_encode($props) 93 | ); 94 | 95 | return implode('', $javascript); 96 | } 97 | } -------------------------------------------------------------------------------- /src/Renderer/NullRenderer.php: -------------------------------------------------------------------------------- 1 | sourceFiles = $sourceFiles; 39 | } 40 | 41 | /** 42 | * @return array|\Traversable 43 | */ 44 | public function getSourceFiles() 45 | { 46 | return $this->sourceFiles; 47 | } 48 | } -------------------------------------------------------------------------------- /src/Renderer/StdInProcessRendererTrait.php: -------------------------------------------------------------------------------- 1 | bin = $bin; 25 | } 26 | 27 | /** 28 | * @return mixed 29 | */ 30 | public function getBin() 31 | { 32 | return $this->bin; 33 | } 34 | 35 | /** 36 | * @return \Symfony\Component\Process\Process 37 | */ 38 | protected function getProcess() 39 | { 40 | return new Process($this->bin); 41 | } 42 | 43 | /** 44 | * @param string $input 45 | * @return string 46 | */ 47 | protected function getOutput($input) 48 | { 49 | return $this->run($input)->getOutput(); 50 | } 51 | 52 | /** 53 | * @param string $input 54 | * @return \Symfony\Component\Process\Process 55 | */ 56 | protected function run($input) 57 | { 58 | $process = $this->getProcess(); 59 | $process->setInput($input); 60 | $process->run(); 61 | 62 | if ($error = $process->getErrorOutput()) { 63 | $this->log('Error during rendering', [ 64 | 'stderr' => $error 65 | ]); 66 | } 67 | return $process; 68 | } 69 | } -------------------------------------------------------------------------------- /src/Renderer/V8JsRenderer.php: -------------------------------------------------------------------------------- 1 | setSourceFiles($sourceFiles); 47 | if ($fragmentProvider) { 48 | $this->setFragmentProvider($fragmentProvider); 49 | } 50 | if ($logger) { 51 | $this->setLogger($logger); 52 | } 53 | $this->v8 = $v8 ?: new V8Js; 54 | } 55 | 56 | /** 57 | * Renders a component that is able to be mounted via JavaScript in the browser 58 | * @param $componentPath 59 | * @param array|void $props 60 | * @return string 61 | */ 62 | public function renderMountableComponent($componentPath, $props = null) 63 | { 64 | return $this->render("renderComponentToString", $componentPath, $props); 65 | } 66 | 67 | /** 68 | * Renders a static component unable to be mounted via JavaScript 69 | * @param $componentPath 70 | * @param array|void $props 71 | * @return string 72 | */ 73 | public function renderStaticComponent($componentPath, $props = null) 74 | { 75 | return $this->render("renderComponentToStaticMarkup", $componentPath, $props); 76 | } 77 | 78 | /** 79 | * @param $reactFunction 80 | * @param $componentPath 81 | * @param null $props 82 | * @return string 83 | * @throws \Exception 84 | */ 85 | private function render($reactFunction, $componentPath, $props = null) 86 | { 87 | $javascript = $this->getJavaScript($reactFunction, $componentPath, $props); 88 | 89 | $markup = ''; 90 | 91 | try { 92 | ob_start(); 93 | $markup = $this->v8->executeString($javascript); 94 | $loggableErrors = ob_get_clean(); 95 | 96 | if ($loggableErrors) { 97 | $this->log( 98 | "Errors in v8 javascript execution", 99 | ["errors" => $loggableErrors] 100 | ); 101 | } 102 | 103 | if (!is_string($markup)) { 104 | throw new RuntimeException("Value returned from v8 executeString isn't a string"); 105 | } 106 | } catch (\Exception $e) { 107 | $this->log($e->getMessage()); 108 | 109 | if ($reactFunction === 'renderComponentToStaticMarkup') { 110 | throw $e; 111 | } 112 | } 113 | 114 | return $markup; 115 | } 116 | 117 | /** 118 | * @param $reactFunction 119 | * @param $componentPath 120 | * @param $props 121 | * @return array 122 | */ 123 | protected function getJavaScript($reactFunction, $componentPath, $props) 124 | { 125 | $fragmentProvider = $this->getFragmentProvider(); 126 | 127 | $javascript = []; 128 | 129 | $javascript[] = "var console = { warn: print, error: print };"; 130 | 131 | // Clear any module loaders 132 | if (method_exists($this->v8, 'setModuleLoader') && $fragmentProvider instanceof SynchronousRequireProvider) { 133 | $javascript[] = "function require() {}"; 134 | $javascript[] = "var require = null;"; 135 | } 136 | 137 | foreach ($this->sourceFiles as $sourceFile) { 138 | $javascript[] = file_get_contents($sourceFile) . ";"; 139 | } 140 | 141 | $javascript[] = sprintf( 142 | "%s.%s(%s(%s));", 143 | $fragmentProvider->getReact(), 144 | $reactFunction, 145 | $fragmentProvider->getComponent($componentPath), 146 | json_encode($props) 147 | ); 148 | 149 | return implode("\n", $javascript); 150 | } 151 | } -------------------------------------------------------------------------------- /src/RuntimeFragmentProvider/GlobalObjectProvider.php: -------------------------------------------------------------------------------- 1 | renderer = new V8JsRenderer( 21 | [__DIR__ . '/../../fixtures/bundle.js'] 22 | ); 23 | } 24 | 25 | public function testRenderMountableComponent() 26 | { 27 | $markup = $this->renderer->renderMountableComponent('./TestComponent'); 28 | 29 | $this->assertContains( 30 | 'Some testing content', 31 | $markup 32 | ); 33 | 34 | $this->assertContains( 35 | 'data-reactid', 36 | $markup 37 | ); 38 | 39 | $this->assertContains( 40 | 'data-react-checksum', 41 | $markup 42 | ); 43 | } 44 | 45 | /** 46 | * @expectedException \V8JsScriptException 47 | * @expectedExceptionMessage Cannot find module './test' 48 | */ 49 | public function testStaticDoesThrowExceptionOnMissingComponent() 50 | { 51 | $this->renderer->renderStaticComponent('./test'); 52 | } 53 | 54 | public function testMountableDoesntThrowExceptionOnMissingComponent() 55 | { 56 | try { 57 | $this->assertEquals( 58 | '', 59 | $this->renderer->renderMountableComponent('./test') 60 | ); 61 | } catch (\Exception $e) { 62 | $this->fail('Exception thrown when meant not to'); 63 | } 64 | } 65 | 66 | public function testRenderStaticComponent() 67 | { 68 | $markup = $this->renderer->renderStaticComponent('./TestComponent'); 69 | 70 | $this->assertContains( 71 | 'Some testing content', 72 | $markup 73 | ); 74 | 75 | $this->assertNotContains( 76 | 'data-reactid', 77 | $markup 78 | ); 79 | 80 | $this->assertNotContains( 81 | 'data-react-checksum', 82 | $markup 83 | ); 84 | } 85 | } -------------------------------------------------------------------------------- /tests/fixtures/TestComponent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | module.exports = React.createClass( { 6 | render: function() { 7 | return React.DOM.span( null, "Some testing content" ); 8 | } 9 | } ); -------------------------------------------------------------------------------- /tests/fixtures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactjs-php-render-tests", 3 | "description": "React for unit tests", 4 | "version": "0.0.0", 5 | "dependencies": { 6 | "react": "~0.10", 7 | "browserify": "~3.26.0" 8 | }, 9 | "scripts": { 10 | "build": "NODE_ENV=production browserify -r react -r ./TestComponent > bundle.js" 11 | } 12 | } --------------------------------------------------------------------------------