├── .gitignore ├── LICENSE ├── composer.json ├── readme.md └── src ├── LICENSE ├── Request.php ├── Request ├── RequestInterface.php └── SlimRequest.php ├── Response.php ├── Response ├── ResponseInterface.php └── SlimResponse.php └── Server.php /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | **/nbproject/** 3 | /composer.lock 4 | /src/vendor/** 5 | /build/phpUnit/** 6 | !.gitignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Moisés Barquín 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mbarquin/reactphp-slim", 3 | "description": "Slim Request and Response extended to allow reactPHP serve a Slim framework instance", 4 | "license" : "MIT", 5 | "require": { 6 | "php": "^7.0", 7 | "react/http": "0.3.*", 8 | "slim/slim": "^3.1" 9 | }, 10 | "config": { 11 | "vendor-dir": "src/vendor" 12 | }, 13 | "autoload": { 14 | "psr-4": { "mbarquin\\reactSlim\\": "src/" } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | reactphp-slim 2 | ======== 3 | 4 | Introduction 5 | ------------ 6 | 7 | This library is created in order to use reactPHP as a HTTP server for Slim framework. It will launch a Slim\App process when a request is made, and at the same time it will transfer data from reactPHP native objects into Slim objects. With this, we will be able to create a basic react server for a Slim framework application. 8 | 9 | Data, cookies and file uploads transmission between react and Slim objects. You can access through slim native functions to uploaded files, data and cookies. 10 | 11 | ##Installation 12 | You can install the component in the following ways: 13 | 14 | * Use the official Github repository (https://github.com/mbarquin/reactphp-slim.git) 15 | * Use composer : composer require mbarquin/reactphp-slim --dev 16 | 17 | ##Usage 18 | After the composer autoload requirement a Slim\App should be instanced and prepared as usual. Slim\App can be bootstrapped and all dependencies can be injected as you like, after that, a reactphp-slim server should be instanced and call the run method in it, using slim\App as parameter. The reactphp-slim server will act as intermediary and will launch the slim application through the ``process`` method when requested, this method avoids the usual request and response bootstrap made by Slim. 19 | 20 | When uploading files, move_uploaded_files() probably won't work, use native object methods to move the file. 21 | 22 | ```php 23 | require '../vendor/autoload.php'; 24 | 25 | use \mbarquin\reactSlim; 26 | 27 | // We keep a new Slim app instance. 28 | $app = new \Slim\App(); 29 | 30 | // We add a closure to attend defined request routes 31 | $app->any('/hello/{name}', function ( 32 | \Slim\Http\Request $request, 33 | \Slim\Http\Response $response) { 34 | 35 | $name = $request->getAttribute('name'); 36 | 37 | $response->getBody()->write("Hello, $name"); 38 | $response->getBody()->write(print_r($request->getParsedBody(), true)); 39 | $response->getBody()->write(print_r($request->getCookieParams(), true)); 40 | $response->getBody()->write(print_r($request->getHeaders(), true)); 41 | // $response->getBody()->write(print_r($request->getUploadedFiles(), true)); 42 | 43 | return $response; 44 | }); 45 | 46 | $server = new \mbarquin\reactSlim\Server(); 47 | 48 | $server->withHost('192.168.67.1')->withPort(1337)->run($app); 49 | ``` 50 | 51 | \mbarquin\reactSlim\Server object is the class which is going to configure and launch a ReactPHP server. It has two main methods 52 | 53 | 54 | **withHost($string)** 55 | Sets the IP to be listened to 56 | 57 | **withPort($int)** 58 | Sets the port the server will be listening to, by default it will be set to 1337. 59 | 60 | **run(\Slim\App $app)** 61 | It launches the server process and wait until a request is made to launch the \Slim\App passed as parameter. 62 | 63 | 64 | ### v0.4.2 Setup 65 | This is the old setup to run the reactPHP server with a slimPHP application 66 | 67 | ```php 68 | require '../vendor/autoload.php'; 69 | 70 | use mbarquin\reactSlim; 71 | 72 | // We keep a new Slim app instance. 73 | $app = new \Slim\App(); 74 | 75 | // We add a closure to listen defined request routes 76 | $app->get('/hello/{name}', function ( 77 | \mbarquin\reactSlim\Request $request, 78 | \mbarquin\reactSlim\Response $response) { 79 | 80 | $name = $request->getAttribute('name'); 81 | $response->getBody()->write("Hello, $name"); 82 | 83 | return $response; 84 | }); 85 | 86 | // We create a closure to be attached to server request event. 87 | $serverCallback = function ( 88 | \React\Http\Request $request, 89 | \React\Http\Response $response) use ($app){ 90 | 91 | $slRequest = \mbarquin\reactSlim\Request::createFromReactRequest($request); 92 | $slResponse = new \mbarquin\reactSlim\Response(); 93 | 94 | $app->process($slRequest, $slResponse); 95 | 96 | $slResponse->setReactResponse($response, true); 97 | }; 98 | 99 | // We make the setup of the ReactPHP 100 | $loop = React\EventLoop\Factory::create(); 101 | $socket = new React\Socket\Server($loop); 102 | $http = new React\Http\Server($socket, $loop); 103 | 104 | // Ligamos la closure al evento request. 105 | $http->on('request', $serverCallback); 106 | 107 | echo "Server running at http://127.0.0.1:1337\n"; 108 | 109 | $socket->listen(1337); 110 | $loop->run(); 111 | ``` -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 Moisés Barquín 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/Request.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * PHP version 7.0 12 | * 13 | * @package reactSlim 14 | * @subpackage reactSlim 15 | * @author Moises Barquin 16 | * @copyright (c) 2016, Moisés Barquín 17 | * @version GIT: $Id$ 18 | */ 19 | namespace mbarquin\reactSlim; 20 | 21 | use Slim\Http\ { 22 | Headers as SlimPHPHeaders, 23 | Uri as SlimPHPUri, 24 | Request as SlimPHPRequest, 25 | RequestBody as SlimPHPRequestBody 26 | }; 27 | use React\Http\Request as ReactRequest; 28 | 29 | /** 30 | * Legacy request adapter class file for a React request object 31 | */ 32 | class Request extends SlimPHPRequest 33 | { 34 | /** 35 | * Creates a new request object from the data of a reactPHP request object 36 | * 37 | * @param ReactRequest $request ReactPHP native request object 38 | * 39 | * @return self 40 | */ 41 | static public function createFromReactRequest(ReactRequest $request) :self 42 | { 43 | $slimHeads = new SlimPHPHeaders(); 44 | foreach($request->getHeaders() as $reactHeadKey => $reactHead) { 45 | $slimHeads->add($reactHeadKey, $reactHead); 46 | if($reactHeadKey === 'Host') { 47 | $host = explode(':', $reactHead); 48 | if(count($host) === 1) { 49 | $host[1] = '80'; 50 | } 51 | } 52 | } 53 | 54 | $slimUri = new SlimPHPUri( 55 | 'http', 56 | $host[0], 57 | (int) $host[1], 58 | $request->getPath(), 59 | $request->getQuery() 60 | ); 61 | 62 | $cookies = []; 63 | $serverParams = $_SERVER; 64 | $serverParams['SERVER_PROTOCOL'] = 'HTTP/'.$request->getHttpVersion(); 65 | 66 | $slimBody = new SlimPHPRequestBody(); 67 | 68 | return new self( 69 | $request->getMethod(), 70 | $slimUri, 71 | $slimHeads, 72 | $cookies, 73 | $serverParams, 74 | $slimBody 75 | ); 76 | } 77 | } -------------------------------------------------------------------------------- /src/Request/RequestInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * PHP version 7.0 12 | * 13 | * @package reactSlim 14 | * @subpackage reactSlim 15 | * @author Moises Barquin 16 | * @copyright (c) 2016, Moisés Barquín 17 | * @version GIT: $Id$ 18 | */ 19 | namespace mbarquin\reactSlim\Request; 20 | 21 | use React\Http\Request as ReactRequest; 22 | 23 | /** 24 | * Contract to have a request to adapt a react request object to a new one 25 | */ 26 | interface RequestInterface 27 | { 28 | static public function createFromReactRequest(ReactRequest $request); 29 | } 30 | -------------------------------------------------------------------------------- /src/Request/SlimRequest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * PHP version 7.0 12 | * 13 | * @package reactSlim 14 | * @subpackage reactSlim 15 | * @author Moises Barquin 16 | * @copyright (c) 2016, Moisés Barquín 17 | * @version GIT: $Id$ 18 | */ 19 | namespace mbarquin\reactSlim\Request; 20 | 21 | use Slim\Http\{ 22 | Request as SlimPHPRequest, 23 | Headers as SlimPHPHeaders, 24 | Cookies as SlimPHPCookies, 25 | Uri as SlimPHPUri, 26 | Body as SlimPHPBody, 27 | UploadedFile as SlimPHPUploadedFile 28 | }; 29 | use React\Http\Request as ReactRequest; 30 | 31 | /** 32 | * Request adapter class file for a React request object 33 | */ 34 | class SlimRequest implements RequestInterface 35 | { 36 | 37 | const HEADERMULTI = 'multipart/form-data'; 38 | const BOUNDARYSPLIT = 'boundary='; 39 | const FIXEDBOUNDARY = '--'; 40 | const CONTENTDISPOSITION = "\r\nContent-Disposition:"; 41 | 42 | 43 | /** 44 | * Returns host name and port as array 45 | * 46 | * @param string $reactHead 47 | * 48 | * @return array 49 | */ 50 | static public function getHost(string $reactHead) :array 51 | { 52 | $host = explode(':', $reactHead); 53 | if (count($host) === 1) { 54 | $host[1] = '80'; 55 | } 56 | return $host; 57 | } 58 | 59 | /** 60 | * Creates a new request object from the data of a reactPHP request object 61 | * 62 | * @param ReactRequest $request ReactPHP native request object 63 | * @param string $body Content of received call 64 | * 65 | * @return SlimPHPRequest 66 | */ 67 | static public function createFromReactRequest(ReactRequest $request, string $body = '') :SlimPHPRequest 68 | { 69 | $slimHeads = new SlimPHPHeaders(); 70 | $cookies = []; 71 | $host = ['', 80]; 72 | 73 | foreach($request->getHeaders() as $reactHeadKey => $reactHead) { 74 | $slimHeads->add($reactHeadKey, $reactHead); 75 | switch($reactHeadKey) { 76 | case 'Host': 77 | $host = static::getHost($reactHead); 78 | break; 79 | case 'Cookie': 80 | $cookies = SlimPHPCookies::parseHeader($reactHead); 81 | break; 82 | } 83 | } 84 | 85 | $slimUri = new SlimPHPUri( 86 | 'http', 87 | $host[0], 88 | (int) $host[1], 89 | $request->getPath(), 90 | $request->getQuery() 91 | ); 92 | 93 | $serverParams = $_SERVER; 94 | $serverParams['SERVER_PROTOCOL'] = 'HTTP/'.$request->getHttpVersion(); 95 | 96 | $slimBody = static::getBody($body); 97 | return new SlimPHPRequest( 98 | $request->getMethod(), 99 | $slimUri, 100 | $slimHeads, 101 | $cookies, 102 | $serverParams, 103 | $slimBody 104 | ); 105 | } 106 | 107 | /** 108 | * Checks if request headers are partial upload headers 109 | * 110 | * @param SlimPHPRequest $slRequest Slim request object 111 | * 112 | * @return string 113 | */ 114 | static public function checkPartialUpload(SlimPHPRequest $slRequest) :string 115 | { 116 | if($slRequest->hasHeader('Content-Type') === true) { 117 | $contentType = $slRequest->getHeader('Content-Type'); 118 | 119 | if(stripos($contentType[0], static::HEADERMULTI) !== false) { 120 | $posBoundary = stripos($contentType[0], static::BOUNDARYSPLIT); 121 | $posBoundary = $posBoundary + strlen(static::BOUNDARYSPLIT); 122 | 123 | return static::FIXEDBOUNDARY.substr($contentType[0], $posBoundary); 124 | } 125 | } 126 | return ''; 127 | } 128 | 129 | /** 130 | * Writes temporary file into tmp folder and returns its name 131 | * 132 | * @param string $bodyPart Split part from request body 133 | * 134 | * @return string Temp file name 135 | */ 136 | public static function getTmpFile(string $bodyPart) :string 137 | { 138 | $temp_file = tempnam(sys_get_temp_dir(), 'React'); 139 | $initPos = strpos($bodyPart, "\r\n\r\n"); 140 | 141 | $handle = fopen($temp_file, "w"); 142 | 143 | fwrite ($handle, substr($bodyPart, $initPos+4)); 144 | fclose($handle); 145 | 146 | return $temp_file; 147 | } 148 | 149 | /** 150 | * Writes data into partial uploads array 151 | * 152 | * @param array $filePartialsInfo Data with current uploaded files 153 | * @param array $name HTML Input name 154 | * @param array $filename Original file name 155 | * @param array $contentType File content type defined by browser 156 | * @param string $temp_file Temp file path and name 157 | * 158 | * @return void 159 | */ 160 | static public function writeFilesArray( 161 | array &$filePartialsInfo, 162 | array $name, 163 | array $filename, 164 | array $contentType, 165 | string $temp_file 166 | ) { 167 | $index = $name[1]; 168 | 169 | $filePartialsInfo['files'][$index]['name'] = $filename[1]; 170 | $filePartialsInfo['files'][$index]['type'] = $contentType[1]; 171 | $filePartialsInfo['files'][$index]['tmp_name'] = $temp_file; 172 | 173 | $filePartialsInfo['last']['type'] = 'files'; 174 | $filePartialsInfo['last']['index'] = $index; 175 | } 176 | 177 | /** 178 | * Writes data into a new tmp file 179 | * 180 | * @param string $bodyPart Split part from body 181 | * @param string $filename Name and path of temp file 182 | * @return void 183 | */ 184 | static public function writeToTmpFile(string $bodyPart, string $filename) 185 | { 186 | $handle = fopen($filename, "a"); 187 | fwrite($handle, $bodyPart); 188 | fclose($handle); 189 | } 190 | 191 | /** 192 | * Build an uploaded file objects array(Collection) 193 | * 194 | * @param array $filePartialsInfo Data with current uploaded files 195 | * 196 | * @return array 197 | */ 198 | static public function getSlimFilesArray(array $filePartialsInfo) :array 199 | { 200 | $ret = []; 201 | foreach($filePartialsInfo['files'] as $name => $file) { 202 | $ret[$name] = new SlimPHPUploadedFile( 203 | $file['tmp_name'], 204 | $file['name'], 205 | $file['type'], 206 | $file['size'], 207 | UPLOAD_ERR_OK, 208 | false 209 | ); 210 | } 211 | 212 | return $ret; 213 | } 214 | 215 | /** 216 | * Checks parts headers, and parses data in it 217 | * 218 | * @param string $bodyPart Split part from body 219 | * @param array $filePartialsInfo Data with current uploaded files 220 | * 221 | * @return void 222 | */ 223 | static function parseBodyParts(string $bodyPart, array &$filePartialsInfo) 224 | { 225 | preg_match('/^'.static::CONTENTDISPOSITION.' (.*);/', $bodyPart, $contentDispo); 226 | 227 | if (count($contentDispo) > 0) { 228 | preg_match('/filename="(.*)"/', $bodyPart, $filename); 229 | if(isset($filename[1]) === true) { 230 | preg_match('/\r\nContent-Type: (.*)/', $bodyPart, $contentType); 231 | preg_match('/name=\"(.*)\";/', $bodyPart, $name); 232 | 233 | $temp_file = static::getTmpFile($bodyPart); 234 | static::writeFilesArray($filePartialsInfo, $name, $filename, $contentType, $temp_file); 235 | 236 | } else { 237 | preg_match('/name=\"(.*)\"/', $bodyPart, $name); 238 | $index = $name[1]; 239 | 240 | $initPos = strpos($bodyPart, "\r\n\r\n"); 241 | $filePartialsInfo['fields'][$index] = substr($bodyPart, $initPos+4); 242 | $filePartialsInfo['fields'][$index] = substr($filePartialsInfo['fields'][$index], 0, -2); 243 | } 244 | } else { 245 | if ($filePartialsInfo['last']['type'] === 'files') { 246 | static::writeToTmpFile( 247 | $bodyPart, 248 | $filePartialsInfo[$filePartialsInfo['last']['type']][$filePartialsInfo['last']['index']]['tmp_name'] 249 | ); 250 | } 251 | } 252 | } 253 | 254 | /** 255 | * Parses body parts after splitting it with boundary string 256 | * 257 | * @param string $body Body received from partial request 258 | * @param array $filePartialsInfo Data with current uploaded files 259 | * 260 | * @return bool 261 | */ 262 | static public function parseBody(string $body, array &$filePartialsInfo) :bool 263 | { 264 | $bodyParts = explode($filePartialsInfo['boundary'], $body); 265 | 266 | if (empty($bodyParts[0]) === true) { 267 | array_shift($bodyParts); 268 | } 269 | 270 | foreach($bodyParts as $piece) { 271 | if ($piece !== "--\r\n") { 272 | static::parseBodyParts($piece, $filePartialsInfo); 273 | } 274 | } 275 | 276 | if( is_array($bodyParts) === true && in_array("--\r\n", $bodyParts) === true) { 277 | static::setFileSizes($filePartialsInfo); 278 | return false; 279 | } elseif($bodyParts === '--') { 280 | static::setFileSizes($filePartialsInfo); 281 | return false; 282 | } 283 | return true; 284 | } 285 | 286 | /** 287 | * Populates file array with all file sizes 288 | * 289 | * @param array $filePartialsInfo Data with current uploaded files 290 | * 291 | * @return void 292 | */ 293 | static public function setFileSizes(array &$filePartialsInfo) 294 | { 295 | if( 296 | isset($filePartialsInfo['files']) === true 297 | && count($filePartialsInfo['files']) > 0 298 | ) { 299 | $keys = array_keys($filePartialsInfo['files']); 300 | foreach($keys as $index) { 301 | $filePartialsInfo['files'][$index]['size'] = filesize($filePartialsInfo['files'][$index]['tmp_name']); 302 | } 303 | } 304 | } 305 | 306 | /** 307 | * Returns a Slim body class with data from a react response 308 | * 309 | * @param string $body Content of received call 310 | * 311 | * @return SlimPHPBody 312 | */ 313 | static public function getBody(string $body) :SlimPHPBody 314 | { 315 | $stream = fopen('php://temp', 'w+'); 316 | if (empty($body) === false) { 317 | fwrite($stream, $body); 318 | } 319 | $slimBody = new SlimPHPBody($stream); 320 | return $slimBody; 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /src/Response.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | * 12 | * PHP version 7.0 13 | * 14 | * @package reactSlim 15 | * @subpackage reactSlim 16 | * @author Moises Barquin 17 | * @copyright (c) 2016, Moisés Barquín 18 | * @version GIT: $Id$ 19 | */ 20 | namespace mbarquin\reactSlim; 21 | 22 | use Slim\Http\Response as SlimPHPResponse; 23 | use React\Http\Response as ReactResponse; 24 | 25 | /** 26 | * Response adapter class 27 | * It performs the setup of a reactPHP response and finishes the communication 28 | */ 29 | class Response extends SlimPHPResponse 30 | { 31 | /** 32 | * It performs the setup of a reactPHP response from a SlimPHP response 33 | * object and finishes the communication 34 | * 35 | * @param ReactResponse $reactResp ReactPHP native response object 36 | * @param bool $endRequest If true, response flush will be finished 37 | * 38 | * @return void 39 | */ 40 | public function setReactResponse(ReactResponse $reactResp, bool $endRequest = false) 41 | { 42 | $reactResp->writeHead($this->getStatusCode(), $this->getHeaders()); 43 | $reactResp->write($this->getBody()); 44 | 45 | if ($endRequest === true) { 46 | $reactResp->end(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/Response/ResponseInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * PHP version 7.0 12 | * 13 | * @package reactSlim 14 | * @subpackage reactSlim 15 | * @author Moises Barquin 16 | * @copyright (c) 2016, Moisés Barquín 17 | * @version GIT: $Id$ 18 | */ 19 | namespace mbarquin\reactSlim\Response; 20 | 21 | use Slim\Http\Response as SlimPHPResponse; 22 | use React\Http\Response as ReactResponse; 23 | 24 | /** 25 | * Contract to have a request to adapt a react request object to a new one 26 | */ 27 | interface ResponseInterface 28 | { 29 | /** 30 | * It performs the setup of a reactPHP response from another response 31 | * object and finishes the communication 32 | * 33 | * @param ReactResponse $reactResp ReactPHP native response object 34 | * @param SlimPHPResponse $slimResponse SlimPHP native response object 35 | * @param boolean $endRequest If true, response flush will be finished 36 | * 37 | * @return void 38 | */ 39 | static public function setReactResponse( 40 | ReactResponse $reactResp, 41 | SlimPHPResponse $slimResponse, 42 | bool $endRequest = false 43 | ); 44 | 45 | /** 46 | * Returns a new response object instance 47 | * 48 | * @return SlimPHPResponse 49 | */ 50 | static public function createResponse() :SlimPHPResponse; 51 | } 52 | -------------------------------------------------------------------------------- /src/Response/SlimResponse.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | * 12 | * PHP version 7.0 13 | * 14 | * @package reactSlim 15 | * @subpackage reactSlim 16 | * @author Moises Barquin 17 | * @copyright (c) 2016, Moisés Barquín 18 | * @version GIT: $Id$ 19 | */ 20 | namespace mbarquin\reactSlim\Response; 21 | 22 | use React\Http\Response as ReactResponse; 23 | use Slim\Http\Response as SlimPHPResponse; 24 | 25 | /** 26 | * Response adapter class 27 | * It performs the setup of a reactPHP response and finishes the communication 28 | */ 29 | class SlimResponse implements ResponseInterface 30 | { 31 | /** 32 | * It performs the setup of a reactPHP response from a SlimPHP response 33 | * object and finishes the communication 34 | * 35 | * @param ReactResponse $reactResp ReactPHP native response object 36 | * @param SlimPHPResponse $slimResponse SlimPHP native response object 37 | * @param bool $endRequest If true, response flush will be finished 38 | * 39 | * @return void 40 | */ 41 | static function setReactResponse( 42 | ReactResponse $reactResp, 43 | SlimPHPResponse $slimResponse, 44 | bool $endRequest = false 45 | ) { 46 | $headers = static::reduceHeaders($slimResponse->getHeaders()); 47 | $reactResp->writeHead($slimResponse->getStatusCode(), $headers); 48 | 49 | $reactResp->write($slimResponse->getBody()); 50 | 51 | if ($endRequest === true) { 52 | $reactResp->end(); 53 | } 54 | } 55 | 56 | /** 57 | * Reduces slim headers array to be used on reactPHP 58 | * 59 | * @param array $headersArray Headers array given by slim 60 | * 61 | * @return array Ready 4 reactPHP array 62 | */ 63 | static public function reduceHeaders(array $headersArray) :array 64 | { 65 | $auxArray = []; 66 | foreach ($headersArray as $name => $value) { 67 | $myContent = ''; 68 | foreach($value as $text) { 69 | $myContent .= $text; 70 | } 71 | $auxArray[$name] = $myContent; 72 | } 73 | 74 | return $auxArray; 75 | } 76 | 77 | /** 78 | * Returns a new Slim response object instance 79 | * 80 | * @return SlimPHPResponse 81 | */ 82 | static public function createResponse() :SlimPHPResponse 83 | { 84 | return new SlimPHPResponse(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Server.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | * 11 | * PHP version 7.0 12 | * 13 | * @package reactSlim 14 | * @subpackage reactSlim 15 | * @author Moises Barquin 16 | * @copyright (c) 2016, Moisés Barquín 17 | * @version GIT: $Id$ 18 | */ 19 | namespace mbarquin\reactSlim; 20 | 21 | use \mbarquin\reactSlim\ { 22 | Request\RequestInterface as RequestAdapterInterface, 23 | Request\SlimRequest, 24 | Response\SlimResponse, 25 | Response\ResponseInterface as ResponseAdapterInterface 26 | }; 27 | use React\ { 28 | EventLoop\Factory as ReactEventLoopFactory, 29 | Http\Response as ReactResponse, 30 | Http\Request as ReactRequest, 31 | Http\Server as ReactHttpServer, 32 | Socket\Server as ReactSocketServer 33 | }; 34 | use Slim\App as SlimInstance; 35 | 36 | /** 37 | * Instantiates the setup of the reactPHP server and launches it 38 | */ 39 | class Server 40 | { 41 | /** 42 | * Sets which port will be listened 43 | * 44 | * @var int 45 | */ 46 | private $port = 1337; 47 | 48 | /** 49 | * Sets which ip will be listened 50 | * @var string 51 | */ 52 | private $host = '127.0.0.1'; 53 | 54 | /** 55 | * Array with info about partial file uploads 56 | * 57 | * @var array 58 | */ 59 | private $partials = []; 60 | 61 | /** 62 | * Sets the listened port 63 | * 64 | * @param int $port 65 | * 66 | * @return self 67 | */ 68 | public function withPort($port) :self 69 | { 70 | if (is_int($port) === true) { 71 | $this->port = $port; 72 | } 73 | 74 | return $this; 75 | } 76 | 77 | /** 78 | * Sets the listened ip 79 | * 80 | * @param int $ip 81 | * 82 | * @return self 83 | */ 84 | public function withHost($ip) :self 85 | { 86 | if (empty($ip) === false) { 87 | $this->host = $ip; 88 | } 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * Returns the two callbacks which will process the HTTP call 95 | * 96 | * @param SlimInstance $app Slim application instance 97 | * 98 | * @return callable 99 | */ 100 | private function getCallbacks(SlimInstance $app) :callable 101 | { 102 | return function ( 103 | ReactRequest $request, 104 | ReactResponse $response 105 | ) use ($app) { 106 | 107 | $request->on('data', function($body) use ($request, $response, $app) { 108 | $slRequest = SlimRequest::createFromReactRequest($request, $body); 109 | $boundary = SlimRequest::checkPartialUpload($slRequest); 110 | 111 | $slResponse = SlimResponse::createResponse(); 112 | 113 | if($boundary !== '') { 114 | if(isset($this->partials[$boundary]) === false) { 115 | $this->partials[$boundary]['boundary'] = $boundary; 116 | } 117 | $continue = SlimRequest::parseBody($body, $this->partials[$boundary]); 118 | if ($continue === false) { 119 | $filesArr = SlimRequest::getSlimFilesArray($this->partials[$boundary]); 120 | 121 | $lastRequest = $slRequest 122 | ->withUploadedFiles($filesArr) 123 | ->withParsedBody($this->partials[$boundary]['fields']); 124 | 125 | $slResponse = $app->process($lastRequest, $slResponse); 126 | SlimResponse::setReactResponse($response, $slResponse, true); 127 | } 128 | 129 | } else { 130 | $slResponse = $app->process($slRequest, $slResponse); 131 | SlimResponse::setReactResponse($response, $slResponse, true); 132 | } 133 | }); 134 | }; 135 | } 136 | 137 | /** 138 | * Checks Adapters and runs the server with the app 139 | * 140 | * @param SlimInstance $app Slim application instance 141 | * 142 | * @return void 143 | */ 144 | public function run(SlimInstance $app) 145 | { 146 | $serverCallback = $this->getCallbacks($app); 147 | 148 | // We make the setup of ReactPHP. 149 | $loop = ReactEventLoopFactory::create(); 150 | $socket = new ReactSocketServer($loop); 151 | $http = new ReactHttpServer($socket, $loop); 152 | 153 | // Link callback to the Request event. 154 | $http->on('request', $serverCallback); 155 | 156 | echo "Server running at http://".$this->host.":".$this->port."\n"; 157 | 158 | $socket->listen($this->port, $this->host); 159 | $loop->run(); 160 | } 161 | 162 | } 163 | --------------------------------------------------------------------------------