├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── phpunit.xml.dist ├── server.php ├── src └── React │ └── ExampleChatWithRandomStranger │ ├── AppInterface.php │ ├── LoggingApp.php │ ├── PairApp.php │ └── TextApp.php └── tests ├── React └── ExampleChatWithRandomStranger │ ├── CallableStub.php │ ├── ConnectionStub.php │ ├── IntegrationTest.php │ ├── LoggingAppTest.php │ ├── PairAppTest.php │ └── TextAppTest.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | vendor 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | 7 | before_script: 8 | - composer self-update 9 | - composer install --dev --prefer-source 10 | 11 | script: bin/phpunit --coverage-text 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Igor Wiedler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React/ExampleChatWithRandomStranger 2 | 3 | Sample application where 2 people are randomly paired up who connect and are able to send messages to eachother. 4 | 5 | [![Build Status](https://secure.travis-ci.org/reactphp/ExampleChatWithRandomStranger.png?branch=master)](http://travis-ci.org/reactphp/ExampleChatWithRandomStranger) 6 | 7 | ## Install 8 | 9 | $ composer install 10 | 11 | ## Usage 12 | 13 | To start the ExampleChatWithRandomStranger server, run: 14 | 15 | $ php server.php 16 | 17 | In order to connect to it, use netcat: 18 | 19 | $ nc localhost 4000 20 | 21 | ## Tests 22 | 23 | $ bin/phpunit 24 | 25 | ## License 26 | 27 | MIT, see LICENSE. 28 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react/ExampleChatWithRandomStranger", 3 | "description": "ExampleChatWithRandomStranger sample application.", 4 | "keywords": ["chat"], 5 | "license": "MIT", 6 | "require": { 7 | "php": ">=5.4.0", 8 | "react/socket": "0.2.*", 9 | "monolog/monolog": "1.2.*" 10 | }, 11 | "require-dev": { 12 | "phpunit/phpunit": "3.7.*" 13 | }, 14 | "autoload": { 15 | "psr-0": { "React\\ExampleChatWithRandomStranger": "src/" } 16 | }, 17 | "config": { 18 | "bin-dir": "bin/" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "5666fb4f88b24c703cb0493d43850be5", 8 | "packages": [ 9 | { 10 | "name": "evenement/evenement", 11 | "version": "v1.0.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/igorw/evenement.git", 15 | "reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/igorw/evenement/zipball/fa966683e7df3e5dd5929d984a44abfbd6bafe8d", 20 | "reference": "fa966683e7df3e5dd5929d984a44abfbd6bafe8d", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.0" 25 | }, 26 | "type": "library", 27 | "autoload": { 28 | "psr-0": { 29 | "Evenement": "src" 30 | } 31 | }, 32 | "notification-url": "https://packagist.org/downloads/", 33 | "license": [ 34 | "MIT" 35 | ], 36 | "authors": [ 37 | { 38 | "name": "Igor Wiedler", 39 | "email": "igor@wiedler.ch", 40 | "homepage": "http://wiedler.ch/igor/" 41 | } 42 | ], 43 | "description": "Événement is a very simple event dispatching library for PHP 5.3", 44 | "keywords": [ 45 | "event-dispatcher" 46 | ], 47 | "time": "2012-05-30T15:01:08+00:00" 48 | }, 49 | { 50 | "name": "monolog/monolog", 51 | "version": "1.2.1", 52 | "source": { 53 | "type": "git", 54 | "url": "https://github.com/Seldaek/monolog.git", 55 | "reference": "d16496318c3e08e3bccfc3866e104e49cf25488a" 56 | }, 57 | "dist": { 58 | "type": "zip", 59 | "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d16496318c3e08e3bccfc3866e104e49cf25488a", 60 | "reference": "d16496318c3e08e3bccfc3866e104e49cf25488a", 61 | "shasum": "" 62 | }, 63 | "require": { 64 | "php": ">=5.3.0" 65 | }, 66 | "require-dev": { 67 | "mlehner/gelf-php": "1.0.*" 68 | }, 69 | "suggest": { 70 | "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", 71 | "ext-mongo": "Allow sending log messages to a MongoDB server", 72 | "mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server" 73 | }, 74 | "type": "library", 75 | "extra": { 76 | "branch-alias": { 77 | "dev-master": "1.3.x-dev" 78 | } 79 | }, 80 | "autoload": { 81 | "psr-0": { 82 | "Monolog": "src/" 83 | } 84 | }, 85 | "notification-url": "https://packagist.org/downloads/", 86 | "license": [ 87 | "MIT" 88 | ], 89 | "authors": [ 90 | { 91 | "name": "Jordi Boggiano", 92 | "email": "j.boggiano@seld.be", 93 | "homepage": "http://seld.be", 94 | "role": "Developer" 95 | } 96 | ], 97 | "description": "Logging for PHP 5.3", 98 | "homepage": "http://github.com/Seldaek/monolog", 99 | "keywords": [ 100 | "log", 101 | "logging" 102 | ], 103 | "time": "2012-08-29T11:53:20+00:00" 104 | }, 105 | { 106 | "name": "react/event-loop", 107 | "version": "v0.2.7", 108 | "target-dir": "React/EventLoop", 109 | "source": { 110 | "type": "git", 111 | "url": "https://github.com/reactphp/event-loop.git", 112 | "reference": "134b94dc555d320bd31253a215c8a65ef151a79a" 113 | }, 114 | "dist": { 115 | "type": "zip", 116 | "url": "https://api.github.com/repos/reactphp/event-loop/zipball/134b94dc555d320bd31253a215c8a65ef151a79a", 117 | "reference": "134b94dc555d320bd31253a215c8a65ef151a79a", 118 | "shasum": "" 119 | }, 120 | "require": { 121 | "php": ">=5.3.3" 122 | }, 123 | "suggest": { 124 | "ext-libev": "*", 125 | "ext-libevent": ">=0.0.5" 126 | }, 127 | "type": "library", 128 | "extra": { 129 | "branch-alias": { 130 | "dev-master": "0.2-dev" 131 | } 132 | }, 133 | "autoload": { 134 | "psr-0": { 135 | "React\\EventLoop": "" 136 | } 137 | }, 138 | "notification-url": "https://packagist.org/downloads/", 139 | "license": [ 140 | "MIT" 141 | ], 142 | "description": "Event loop abstraction layer that libraries can use for evented I/O.", 143 | "keywords": [ 144 | "event-loop" 145 | ], 146 | "time": "2013-01-05T11:41:26+00:00" 147 | }, 148 | { 149 | "name": "react/socket", 150 | "version": "v0.2.6", 151 | "target-dir": "React/Socket", 152 | "source": { 153 | "type": "git", 154 | "url": "https://github.com/reactphp/socket.git", 155 | "reference": "21e3fe670b2f18e3c6b2cb73f14e1c58fe7bca84" 156 | }, 157 | "dist": { 158 | "type": "zip", 159 | "url": "https://api.github.com/repos/reactphp/socket/zipball/21e3fe670b2f18e3c6b2cb73f14e1c58fe7bca84", 160 | "reference": "21e3fe670b2f18e3c6b2cb73f14e1c58fe7bca84", 161 | "shasum": "" 162 | }, 163 | "require": { 164 | "evenement/evenement": "1.0.*", 165 | "php": ">=5.3.3", 166 | "react/event-loop": "0.2.*", 167 | "react/stream": "0.2.*" 168 | }, 169 | "type": "library", 170 | "extra": { 171 | "branch-alias": { 172 | "dev-master": "0.2-dev" 173 | } 174 | }, 175 | "autoload": { 176 | "psr-0": { 177 | "React\\Socket": "" 178 | } 179 | }, 180 | "notification-url": "https://packagist.org/downloads/", 181 | "license": [ 182 | "MIT" 183 | ], 184 | "description": "Library for building an evented socket server.", 185 | "keywords": [ 186 | "Socket" 187 | ], 188 | "time": "2012-12-14T00:58:14+00:00" 189 | }, 190 | { 191 | "name": "react/stream", 192 | "version": "v0.2.6", 193 | "target-dir": "React/Stream", 194 | "source": { 195 | "type": "git", 196 | "url": "https://github.com/reactphp/stream.git", 197 | "reference": "34c1059cffb44873440135e9a86fe9ae9caf5acd" 198 | }, 199 | "dist": { 200 | "type": "zip", 201 | "url": "https://api.github.com/repos/reactphp/stream/zipball/34c1059cffb44873440135e9a86fe9ae9caf5acd", 202 | "reference": "34c1059cffb44873440135e9a86fe9ae9caf5acd", 203 | "shasum": "" 204 | }, 205 | "require": { 206 | "evenement/evenement": "1.0.*", 207 | "php": ">=5.3.3" 208 | }, 209 | "suggest": { 210 | "react/event-loop": "0.2.*", 211 | "react/promise": "1.0.*" 212 | }, 213 | "type": "library", 214 | "extra": { 215 | "branch-alias": { 216 | "dev-master": "0.2-dev" 217 | } 218 | }, 219 | "autoload": { 220 | "psr-0": { 221 | "React\\Stream": "" 222 | } 223 | }, 224 | "notification-url": "https://packagist.org/downloads/", 225 | "license": [ 226 | "MIT" 227 | ], 228 | "description": "Basic readable and writable stream interfaces that support piping.", 229 | "keywords": [ 230 | "pipe", 231 | "stream" 232 | ], 233 | "time": "2012-12-14T00:58:14+00:00" 234 | } 235 | ], 236 | "packages-dev": [ 237 | { 238 | "name": "phpunit/php-code-coverage", 239 | "version": "1.2.18", 240 | "source": { 241 | "type": "git", 242 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 243 | "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b" 244 | }, 245 | "dist": { 246 | "type": "zip", 247 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", 248 | "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b", 249 | "shasum": "" 250 | }, 251 | "require": { 252 | "php": ">=5.3.3", 253 | "phpunit/php-file-iterator": ">=1.3.0@stable", 254 | "phpunit/php-text-template": ">=1.2.0@stable", 255 | "phpunit/php-token-stream": ">=1.1.3,<1.3.0" 256 | }, 257 | "require-dev": { 258 | "phpunit/phpunit": "3.7.*@dev" 259 | }, 260 | "suggest": { 261 | "ext-dom": "*", 262 | "ext-xdebug": ">=2.0.5" 263 | }, 264 | "type": "library", 265 | "extra": { 266 | "branch-alias": { 267 | "dev-master": "1.2.x-dev" 268 | } 269 | }, 270 | "autoload": { 271 | "classmap": [ 272 | "PHP/" 273 | ] 274 | }, 275 | "notification-url": "https://packagist.org/downloads/", 276 | "include-path": [ 277 | "" 278 | ], 279 | "license": [ 280 | "BSD-3-Clause" 281 | ], 282 | "authors": [ 283 | { 284 | "name": "Sebastian Bergmann", 285 | "email": "sb@sebastian-bergmann.de", 286 | "role": "lead" 287 | } 288 | ], 289 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 290 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 291 | "keywords": [ 292 | "coverage", 293 | "testing", 294 | "xunit" 295 | ], 296 | "time": "2014-09-02T10:13:14+00:00" 297 | }, 298 | { 299 | "name": "phpunit/php-file-iterator", 300 | "version": "1.4.2", 301 | "source": { 302 | "type": "git", 303 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 304 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" 305 | }, 306 | "dist": { 307 | "type": "zip", 308 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 309 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 310 | "shasum": "" 311 | }, 312 | "require": { 313 | "php": ">=5.3.3" 314 | }, 315 | "type": "library", 316 | "extra": { 317 | "branch-alias": { 318 | "dev-master": "1.4.x-dev" 319 | } 320 | }, 321 | "autoload": { 322 | "classmap": [ 323 | "src/" 324 | ] 325 | }, 326 | "notification-url": "https://packagist.org/downloads/", 327 | "license": [ 328 | "BSD-3-Clause" 329 | ], 330 | "authors": [ 331 | { 332 | "name": "Sebastian Bergmann", 333 | "email": "sb@sebastian-bergmann.de", 334 | "role": "lead" 335 | } 336 | ], 337 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 338 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 339 | "keywords": [ 340 | "filesystem", 341 | "iterator" 342 | ], 343 | "time": "2016-10-03T07:40:28+00:00" 344 | }, 345 | { 346 | "name": "phpunit/php-text-template", 347 | "version": "1.2.1", 348 | "source": { 349 | "type": "git", 350 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 351 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 352 | }, 353 | "dist": { 354 | "type": "zip", 355 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 356 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 357 | "shasum": "" 358 | }, 359 | "require": { 360 | "php": ">=5.3.3" 361 | }, 362 | "type": "library", 363 | "autoload": { 364 | "classmap": [ 365 | "src/" 366 | ] 367 | }, 368 | "notification-url": "https://packagist.org/downloads/", 369 | "license": [ 370 | "BSD-3-Clause" 371 | ], 372 | "authors": [ 373 | { 374 | "name": "Sebastian Bergmann", 375 | "email": "sebastian@phpunit.de", 376 | "role": "lead" 377 | } 378 | ], 379 | "description": "Simple template engine.", 380 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 381 | "keywords": [ 382 | "template" 383 | ], 384 | "time": "2015-06-21T13:50:34+00:00" 385 | }, 386 | { 387 | "name": "phpunit/php-timer", 388 | "version": "1.0.9", 389 | "source": { 390 | "type": "git", 391 | "url": "https://github.com/sebastianbergmann/php-timer.git", 392 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" 393 | }, 394 | "dist": { 395 | "type": "zip", 396 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 397 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 398 | "shasum": "" 399 | }, 400 | "require": { 401 | "php": "^5.3.3 || ^7.0" 402 | }, 403 | "require-dev": { 404 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 405 | }, 406 | "type": "library", 407 | "extra": { 408 | "branch-alias": { 409 | "dev-master": "1.0-dev" 410 | } 411 | }, 412 | "autoload": { 413 | "classmap": [ 414 | "src/" 415 | ] 416 | }, 417 | "notification-url": "https://packagist.org/downloads/", 418 | "license": [ 419 | "BSD-3-Clause" 420 | ], 421 | "authors": [ 422 | { 423 | "name": "Sebastian Bergmann", 424 | "email": "sb@sebastian-bergmann.de", 425 | "role": "lead" 426 | } 427 | ], 428 | "description": "Utility class for timing", 429 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 430 | "keywords": [ 431 | "timer" 432 | ], 433 | "time": "2017-02-26T11:10:40+00:00" 434 | }, 435 | { 436 | "name": "phpunit/php-token-stream", 437 | "version": "1.2.2", 438 | "source": { 439 | "type": "git", 440 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 441 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" 442 | }, 443 | "dist": { 444 | "type": "zip", 445 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", 446 | "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", 447 | "shasum": "" 448 | }, 449 | "require": { 450 | "ext-tokenizer": "*", 451 | "php": ">=5.3.3" 452 | }, 453 | "type": "library", 454 | "extra": { 455 | "branch-alias": { 456 | "dev-master": "1.2-dev" 457 | } 458 | }, 459 | "autoload": { 460 | "classmap": [ 461 | "PHP/" 462 | ] 463 | }, 464 | "notification-url": "https://packagist.org/downloads/", 465 | "include-path": [ 466 | "" 467 | ], 468 | "license": [ 469 | "BSD-3-Clause" 470 | ], 471 | "authors": [ 472 | { 473 | "name": "Sebastian Bergmann", 474 | "email": "sb@sebastian-bergmann.de", 475 | "role": "lead" 476 | } 477 | ], 478 | "description": "Wrapper around PHP's tokenizer extension.", 479 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 480 | "keywords": [ 481 | "tokenizer" 482 | ], 483 | "time": "2014-03-03T05:10:30+00:00" 484 | }, 485 | { 486 | "name": "phpunit/phpunit", 487 | "version": "3.7.38", 488 | "source": { 489 | "type": "git", 490 | "url": "https://github.com/sebastianbergmann/phpunit.git", 491 | "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6" 492 | }, 493 | "dist": { 494 | "type": "zip", 495 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6", 496 | "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6", 497 | "shasum": "" 498 | }, 499 | "require": { 500 | "ext-ctype": "*", 501 | "ext-dom": "*", 502 | "ext-json": "*", 503 | "ext-pcre": "*", 504 | "ext-reflection": "*", 505 | "ext-spl": "*", 506 | "php": ">=5.3.3", 507 | "phpunit/php-code-coverage": "~1.2", 508 | "phpunit/php-file-iterator": "~1.3", 509 | "phpunit/php-text-template": "~1.1", 510 | "phpunit/php-timer": "~1.0", 511 | "phpunit/phpunit-mock-objects": "~1.2", 512 | "symfony/yaml": "~2.0" 513 | }, 514 | "require-dev": { 515 | "pear-pear.php.net/pear": "1.9.4" 516 | }, 517 | "suggest": { 518 | "phpunit/php-invoker": "~1.1" 519 | }, 520 | "bin": [ 521 | "composer/bin/phpunit" 522 | ], 523 | "type": "library", 524 | "extra": { 525 | "branch-alias": { 526 | "dev-master": "3.7.x-dev" 527 | } 528 | }, 529 | "autoload": { 530 | "classmap": [ 531 | "PHPUnit/" 532 | ] 533 | }, 534 | "notification-url": "https://packagist.org/downloads/", 535 | "include-path": [ 536 | "", 537 | "../../symfony/yaml/" 538 | ], 539 | "license": [ 540 | "BSD-3-Clause" 541 | ], 542 | "authors": [ 543 | { 544 | "name": "Sebastian Bergmann", 545 | "email": "sebastian@phpunit.de", 546 | "role": "lead" 547 | } 548 | ], 549 | "description": "The PHP Unit Testing framework.", 550 | "homepage": "http://www.phpunit.de/", 551 | "keywords": [ 552 | "phpunit", 553 | "testing", 554 | "xunit" 555 | ], 556 | "time": "2014-10-17T09:04:17+00:00" 557 | }, 558 | { 559 | "name": "phpunit/phpunit-mock-objects", 560 | "version": "1.2.3", 561 | "source": { 562 | "type": "git", 563 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 564 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" 565 | }, 566 | "dist": { 567 | "type": "zip", 568 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", 569 | "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", 570 | "shasum": "" 571 | }, 572 | "require": { 573 | "php": ">=5.3.3", 574 | "phpunit/php-text-template": ">=1.1.1@stable" 575 | }, 576 | "suggest": { 577 | "ext-soap": "*" 578 | }, 579 | "type": "library", 580 | "autoload": { 581 | "classmap": [ 582 | "PHPUnit/" 583 | ] 584 | }, 585 | "notification-url": "https://packagist.org/downloads/", 586 | "include-path": [ 587 | "" 588 | ], 589 | "license": [ 590 | "BSD-3-Clause" 591 | ], 592 | "authors": [ 593 | { 594 | "name": "Sebastian Bergmann", 595 | "email": "sb@sebastian-bergmann.de", 596 | "role": "lead" 597 | } 598 | ], 599 | "description": "Mock Object library for PHPUnit", 600 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 601 | "keywords": [ 602 | "mock", 603 | "xunit" 604 | ], 605 | "time": "2013-01-13T10:24:48+00:00" 606 | }, 607 | { 608 | "name": "symfony/yaml", 609 | "version": "v2.8.20", 610 | "source": { 611 | "type": "git", 612 | "url": "https://github.com/symfony/yaml.git", 613 | "reference": "93ccdde79f4b079c7558da4656a3cb1c50c68e02" 614 | }, 615 | "dist": { 616 | "type": "zip", 617 | "url": "https://api.github.com/repos/symfony/yaml/zipball/93ccdde79f4b079c7558da4656a3cb1c50c68e02", 618 | "reference": "93ccdde79f4b079c7558da4656a3cb1c50c68e02", 619 | "shasum": "" 620 | }, 621 | "require": { 622 | "php": ">=5.3.9" 623 | }, 624 | "type": "library", 625 | "extra": { 626 | "branch-alias": { 627 | "dev-master": "2.8-dev" 628 | } 629 | }, 630 | "autoload": { 631 | "psr-4": { 632 | "Symfony\\Component\\Yaml\\": "" 633 | }, 634 | "exclude-from-classmap": [ 635 | "/Tests/" 636 | ] 637 | }, 638 | "notification-url": "https://packagist.org/downloads/", 639 | "license": [ 640 | "MIT" 641 | ], 642 | "authors": [ 643 | { 644 | "name": "Fabien Potencier", 645 | "email": "fabien@symfony.com" 646 | }, 647 | { 648 | "name": "Symfony Community", 649 | "homepage": "https://symfony.com/contributors" 650 | } 651 | ], 652 | "description": "Symfony Yaml Component", 653 | "homepage": "https://symfony.com", 654 | "time": "2017-05-01T14:31:55+00:00" 655 | } 656 | ], 657 | "aliases": [], 658 | "minimum-stability": "stable", 659 | "stability-flags": [], 660 | "prefer-stable": false, 661 | "prefer-lowest": false, 662 | "platform": { 663 | "php": ">=5.4.0" 664 | }, 665 | "platform-dev": [] 666 | } 667 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | ./tests/React/ 10 | 11 | 12 | 13 | 14 | 15 | ./src/ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | pushHandler(new Monolog\Handler\StreamHandler(STDOUT)); 14 | 15 | $app = new LoggingApp( 16 | new TextApp(new PairApp()), 17 | $logger 18 | ); 19 | 20 | $loop = React\EventLoop\Factory::create(); 21 | $socket = new React\Socket\Server($loop); 22 | 23 | $i = 0; 24 | $names = ['Alice', 'Bob', 'Carol', 'Dave', 'Erin', 'Frank', 'Eve', 25 | 'Mallory', 'Oscar', 'Peggy', 'Trent', 'Walter']; 26 | 27 | $socket->on('connection', function ($conn) use (&$i, $names, $app) { 28 | $conn->id = isset($names[$i]) ? $names[$i] : $i; 29 | $app->connect($conn); 30 | $i++; 31 | }); 32 | 33 | $socket->listen(4000); 34 | $loop->run(); 35 | -------------------------------------------------------------------------------- /src/React/ExampleChatWithRandomStranger/AppInterface.php: -------------------------------------------------------------------------------- 1 | app = $app; 16 | $this->logger = $logger; 17 | } 18 | 19 | public function connect(ConnectionInterface $conn) 20 | { 21 | $this->logger->info(sprintf("New connection %s", $conn->id)); 22 | 23 | $conn->on('end', function () use ($conn) { 24 | $this->logger->info(sprintf("Connection %s disconnected", $conn->id)); 25 | }); 26 | 27 | $conn->on('pipe', function ($source) use ($conn) { 28 | if (!empty($conn->pipeLogged) || !empty($source->pipeLogged)) { 29 | return; 30 | } 31 | 32 | $this->logger->info(sprintf("Pairing up connection %s with waiting connection %s", 33 | $source->id, $conn->id)); 34 | 35 | $conn->pipeLogged = $conn->pipeLogged = true; 36 | }); 37 | 38 | $this->app->connect($conn); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/React/ExampleChatWithRandomStranger/PairApp.php: -------------------------------------------------------------------------------- 1 | waiting; 14 | 15 | if (null === $waiting || !$waiting->isReadable()) { 16 | $this->waiting = $conn; 17 | $conn->emit('wait'); 18 | return; 19 | } 20 | 21 | $conn->pipe($waiting)->pipe($conn); 22 | 23 | $this->waiting = null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/React/ExampleChatWithRandomStranger/TextApp.php: -------------------------------------------------------------------------------- 1 | app = $app; 14 | } 15 | 16 | public function connect(ConnectionInterface $conn) 17 | { 18 | $conn->write(sprintf("Hello %s!\n", $conn->id)); 19 | 20 | $conn->on('wait', function () use ($conn) { 21 | $conn->write("Please wait until a partner connects.\n"); 22 | }); 23 | 24 | $conn->on('pipe', function ($source) use ($conn) { 25 | $message = "You are now talking to %s.\n"; 26 | $conn->write(sprintf($message, $source->id)); 27 | }); 28 | 29 | $this->app->connect($conn); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/React/ExampleChatWithRandomStranger/CallableStub.php: -------------------------------------------------------------------------------- 1 | closed; 18 | } 19 | 20 | public function pause() 21 | { 22 | } 23 | 24 | public function resume() 25 | { 26 | } 27 | 28 | public function pipe(WritableStreamInterface $dest, array $options = array()) 29 | { 30 | Util::pipe($this, $dest, $options); 31 | 32 | return $dest; 33 | } 34 | 35 | public function isWritable() 36 | { 37 | return !$this->closed; 38 | } 39 | 40 | public function write($data) 41 | { 42 | $this->data .= $data; 43 | } 44 | 45 | public function end($data = null) 46 | { 47 | $this->write($data); 48 | $this->close(); 49 | } 50 | 51 | public function close() 52 | { 53 | if ($this->closed) { 54 | return; 55 | } 56 | 57 | $this->closed = true; 58 | $this->emit('end', array($this)); 59 | $this->emit('close', array($this)); 60 | $this->removeAllListeners(); 61 | } 62 | 63 | public function getRemoteAddress() 64 | { 65 | return '127.0.0.1'; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/React/ExampleChatWithRandomStranger/IntegrationTest.php: -------------------------------------------------------------------------------- 1 | handler = new TestHandler(); 18 | 19 | $this->logger = new Logger('ExampleChatWithRandomStranger'); 20 | $this->logger->pushHandler($this->handler); 21 | 22 | $this->app = new LoggingApp( 23 | new TextApp(new PairApp()), 24 | $this->logger 25 | ); 26 | } 27 | 28 | /** @test */ 29 | public function connectingOneUserShouldMakeHimWait() 30 | { 31 | $alice = new ConnectionStub(); 32 | $alice->id = 'Alice'; 33 | 34 | $this->app->connect($alice); 35 | 36 | $this->assertConnectionData($alice, [ 37 | 'Hello Alice!', 38 | 'Please wait until a partner connects.', 39 | ]); 40 | 41 | $this->assertLogs([ 42 | 'New connection Alice', 43 | ]); 44 | } 45 | 46 | /** @test */ 47 | public function connectingTwoUsersShouldPairThemUp() 48 | { 49 | list($alice, $bob) = $this->createAliceAndBob(); 50 | 51 | $this->app->connect($alice); 52 | $this->app->connect($bob); 53 | 54 | $this->emitConnectionData($alice, 'Hallo Bob, wie geht es dir?'); 55 | $this->emitConnectionData($bob, 'Je ne comprends pas!'); 56 | 57 | $this->assertConnectionData($alice, [ 58 | 'Hello Alice!', 59 | 'Please wait until a partner connects.', 60 | 'You are now talking to Bob.', 61 | 'Je ne comprends pas!', 62 | ]); 63 | 64 | $this->assertConnectionData($bob, [ 65 | 'Hello Bob!', 66 | 'You are now talking to Alice.', 67 | 'Hallo Bob, wie geht es dir?', 68 | ]); 69 | 70 | $this->assertLogs([ 71 | 'New connection Alice', 72 | 'New connection Bob', 73 | 'Pairing up connection Bob with waiting connection Alice', 74 | ]); 75 | } 76 | 77 | /** @test */ 78 | public function disconnectingAliceShouldDisconnectBob() 79 | { 80 | list($alice, $bob) = $this->createAliceAndBob(); 81 | 82 | $this->app->connect($alice); 83 | $this->app->connect($bob); 84 | 85 | $alice->close(); 86 | 87 | $this->assertClosed($alice); 88 | $this->assertClosed($bob); 89 | } 90 | 91 | /** @test */ 92 | public function disconnectingBobShouldDisconnectAlice() 93 | { 94 | list($alice, $bob) = $this->createAliceAndBob(); 95 | 96 | $this->app->connect($alice); 97 | $this->app->connect($bob); 98 | 99 | $bob->close(); 100 | 101 | $this->assertClosed($alice); 102 | $this->assertClosed($bob); 103 | } 104 | 105 | private function createAliceAndBob() 106 | { 107 | $alice = new ConnectionStub(); 108 | $alice->id = 'Alice'; 109 | 110 | $bob = new ConnectionStub(); 111 | $bob->id = 'Bob'; 112 | 113 | return array($alice, $bob); 114 | } 115 | 116 | private function emitConnectionData(ConnectionInterface $conn, $data) 117 | { 118 | $conn->emit('data', array($data."\n")); 119 | } 120 | 121 | private function assertClosed(ConnectionInterface $conn) 122 | { 123 | $this->assertFalse($conn->isReadable()); 124 | $this->assertFalse($conn->isWritable()); 125 | } 126 | 127 | private function assertConnectionData(ConnectionInterface $conn, $expected) 128 | { 129 | $data = implode("\n", $expected)."\n"; 130 | $this->assertSame($data, $conn->data); 131 | } 132 | 133 | private function assertLogs($expected) 134 | { 135 | $getMessage = function ($record) { return $record['message']; }; 136 | $logs = array_map($getMessage, $this->handler->getRecords()); 137 | $this->assertSame($expected, $logs); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tests/React/ExampleChatWithRandomStranger/LoggingAppTest.php: -------------------------------------------------------------------------------- 1 | handler = new TestHandler(); 18 | 19 | $this->logger = new Logger('ExampleChatWithRandomStranger'); 20 | $this->logger->pushHandler($this->handler); 21 | 22 | $this->appMock = $this->getMock('React\ExampleChatWithRandomStranger\AppInterface'); 23 | 24 | $this->app = new LoggingApp( 25 | $this->appMock, 26 | $this->logger 27 | ); 28 | } 29 | 30 | /** @test */ 31 | public function connectShouldDelegateToDecoratedApp() 32 | { 33 | $alice = new ConnectionStub(); 34 | $alice->id = 'Alice'; 35 | 36 | $this->appMock 37 | ->expects($this->once()) 38 | ->method('connect') 39 | ->with($alice); 40 | 41 | $this->app->connect($alice); 42 | } 43 | 44 | /** @test */ 45 | public function connectShouldLogNewConnection() 46 | { 47 | $alice = new ConnectionStub(); 48 | $alice->id = 'Alice'; 49 | 50 | $this->app->connect($alice); 51 | 52 | $this->assertLogs([ 53 | 'New connection Alice', 54 | ]); 55 | } 56 | 57 | /** @test */ 58 | public function disconnectShouldLogDisconnection() 59 | { 60 | $alice = new ConnectionStub(); 61 | $alice->id = 'Alice'; 62 | 63 | $this->app->connect($alice); 64 | 65 | $alice->close(); 66 | 67 | $this->assertLogs([ 68 | 'New connection Alice', 69 | 'Connection Alice disconnected', 70 | ]); 71 | } 72 | 73 | /** @test */ 74 | public function connectingTwoUsersShouldLogPipingOnce() 75 | { 76 | $alice = new ConnectionStub(); 77 | $alice->id = 'Alice'; 78 | 79 | $bob = new ConnectionStub(); 80 | $bob->id = 'Bob'; 81 | 82 | $this->app->connect($alice); 83 | $this->app->connect($bob); 84 | 85 | $alice->pipe($bob)->pipe($alice); 86 | 87 | $this->assertLogs([ 88 | 'New connection Alice', 89 | 'New connection Bob', 90 | 'Pairing up connection Alice with waiting connection Bob', 91 | ]); 92 | } 93 | 94 | private function assertLogs($expected) 95 | { 96 | $getMessage = function ($record) { return $record['message']; }; 97 | $logs = array_map($getMessage, $this->handler->getRecords()); 98 | $this->assertSame($expected, $logs); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tests/React/ExampleChatWithRandomStranger/PairAppTest.php: -------------------------------------------------------------------------------- 1 | app = new PairApp(); 13 | } 14 | 15 | /** @test */ 16 | public function connectingOneUserShouldMakeHimWait() 17 | { 18 | $alice = new ConnectionStub(); 19 | $alice->id = 'Alice'; 20 | $alice->on('wait', $this->expectCallableOnce()); 21 | 22 | $this->app->connect($alice); 23 | } 24 | 25 | /** @test */ 26 | public function connectingTwoUsersShouldPairThemUp() 27 | { 28 | $alice = new ConnectionStub(); 29 | $alice->id = 'Alice'; 30 | $alice->on('pipe', $this->expectCallableOnce()); 31 | 32 | $bob = new ConnectionStub(); 33 | $bob->id = 'Bob'; 34 | $bob->on('pipe', $this->expectCallableOnce()); 35 | 36 | $this->app->connect($alice); 37 | $this->app->connect($bob); 38 | } 39 | 40 | /** @test */ 41 | public function connectingThreeUsersShouldMakeTheThirdOneWait() 42 | { 43 | $alice = new ConnectionStub(); 44 | $alice->id = 'Alice'; 45 | 46 | $bob = new ConnectionStub(); 47 | $bob->id = 'Bob'; 48 | 49 | $carol = new ConnectionStub(); 50 | $carol->id = 'Carol'; 51 | $carol->on('wait', $this->expectCallableOnce()); 52 | 53 | $this->app->connect($alice); 54 | $this->app->connect($bob); 55 | $this->app->connect($carol); 56 | } 57 | 58 | /** @test */ 59 | public function connectingFourUsersShouldPairThemUpInPairs() 60 | { 61 | $alice = new ConnectionStub(); 62 | $alice->id = 'Alice'; 63 | 64 | $bob = new ConnectionStub(); 65 | $bob->id = 'Bob'; 66 | 67 | $carol = new ConnectionStub(); 68 | $carol->id = 'Carol'; 69 | $carol->on('pipe', $this->expectCallableOnce()); 70 | 71 | $dan = new ConnectionStub(); 72 | $dan->id = 'Dan'; 73 | $dan->on('pipe', $this->expectCallableOnce()); 74 | 75 | $this->app->connect($alice); 76 | $this->app->connect($bob); 77 | $this->app->connect($carol); 78 | $this->app->connect($dan); 79 | } 80 | 81 | protected function expectCallableOnce() 82 | { 83 | $mock = $this->createCallableMock(); 84 | $mock 85 | ->expects($this->once()) 86 | ->method('__invoke'); 87 | 88 | return $mock; 89 | } 90 | 91 | protected function createCallableMock() 92 | { 93 | return $this->getMock('React\ExampleChatWithRandomStranger\CallableStub'); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tests/React/ExampleChatWithRandomStranger/TextAppTest.php: -------------------------------------------------------------------------------- 1 | appMock = $this->getMock('React\ExampleChatWithRandomStranger\AppInterface'); 15 | $this->app = new TextApp($this->appMock); 16 | } 17 | 18 | /** @test */ 19 | public function connectShouldDelegateToDecoratedApp() 20 | { 21 | $alice = new ConnectionStub(); 22 | $alice->id = 'Alice'; 23 | 24 | $this->appMock 25 | ->expects($this->once()) 26 | ->method('connect') 27 | ->with($alice); 28 | 29 | $this->app->connect($alice); 30 | } 31 | 32 | /** @test */ 33 | public function connectShouldGreetUser() 34 | { 35 | $alice = new ConnectionStub(); 36 | $alice->id = 'Alice'; 37 | 38 | $this->app->connect($alice); 39 | 40 | $this->assertConnectionData($alice, [ 41 | 'Hello Alice!', 42 | ]); 43 | } 44 | 45 | /** @test */ 46 | public function connectionWaitEventShouldTriggerWaitMessage() 47 | { 48 | $alice = new ConnectionStub(); 49 | $alice->id = 'Alice'; 50 | 51 | $this->app->connect($alice); 52 | 53 | $alice->emit('wait'); 54 | 55 | $this->assertConnectionData($alice, [ 56 | 'Hello Alice!', 57 | 'Please wait until a partner connects.', 58 | ]); 59 | } 60 | 61 | /** @test */ 62 | public function connectionPipeShouldTriggerTalkingToMessage() 63 | { 64 | $alice = new ConnectionStub(); 65 | $alice->id = 'Alice'; 66 | 67 | $bob = new ConnectionStub(); 68 | $bob->id = 'Bob'; 69 | 70 | $this->app->connect($alice); 71 | $this->app->connect($bob); 72 | 73 | $alice->pipe($bob)->pipe($alice); 74 | 75 | $this->assertConnectionData($alice, [ 76 | 'Hello Alice!', 77 | 'You are now talking to Bob.', 78 | ]); 79 | 80 | $this->assertConnectionData($bob, [ 81 | 'Hello Bob!', 82 | 'You are now talking to Alice.', 83 | ]); 84 | } 85 | 86 | private function assertConnectionData(ConnectionInterface $conn, $expected) 87 | { 88 | $data = implode("\n", $expected)."\n"; 89 | $this->assertSame($data, $conn->data); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add('React\ExampleChatWithRandomStranger', __DIR__); 5 | --------------------------------------------------------------------------------