Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge c557ee6 into 26efe24
Browse files Browse the repository at this point in the history
  • Loading branch information
ezimuel committed Jun 6, 2018
2 parents 26efe24 + c557ee6 commit ca92d0b
Show file tree
Hide file tree
Showing 8 changed files with 353 additions and 3 deletions.
11 changes: 10 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,45 @@ matrix:
- php: 7
env:
- DEPS=lowest
- INSTALL_SWOOLE=true
- php: 7
env:
- DEPS=locked
- CHECK_CS=true
- TEST_COVERAGE=true
- INSTALL_SWOOLE=true
- php: 7
env:
- DEPS=latest
- INSTALL_SWOOLE=true
- php: 7.1
env:
- DEPS=lowest
- INSTALL_SWOOLE=true
- php: 7.1
env:
- DEPS=locked
- INSTALL_SWOOLE=true
- php: 7.1
env:
- DEPS=latest
- INSTALL_SWOOLE=true
- php: 7.2
env:
- DEPS=lowest
- INSTALL_SWOOLE=true
- php: 7.2
env:
- DEPS=locked
- INSTALL_SWOOLE=true
- php: 7.2
env:
- DEPS=latest
- INSTALL_SWOOLE=true

before_install:
- if [[ $TEST_COVERAGE != 'true' && "$(php --version | grep xdebug -ci)" -ge 1 ]]; then phpenv config-rm xdebug.ini || return 0 ; fi
- travis_retry composer self-update
- if [[ $INSTALL_SWOOLE == 'true' ]]; then ./.travis/install_swoole.sh ; fi

install:
- travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs
Expand Down
10 changes: 10 additions & 0 deletions .travis/install_swoole.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
pecl install swoole << EOF
`#enable debug/trace log support? [no] :`
`#enable sockets supports? [no] :`y
`#enable openssl support? [no] :`
`#enable http2 support? [no] :`
`#enable async-redis support? [no] :`
`#enable mysqlnd support? [no] :`
`#enable postgresql coroutine client support? [no] :`
EOF
54 changes: 54 additions & 0 deletions doc/book/swoole.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Swoole
use Zend\Diactoros\ServerRequestFactory;

[Swoole](https://www.swoole.co.uk/) is an async programming Framework for PHP
that can be used to create high performance HTTP server applications, e.g. web
APIs. We provided the support of Swoole in `zend-diactoros` using two methods to
convert a [Swoole\Http\Request](http://php.net/manual/en/class.swoole-http-request.php)
in a [PSR-7 Request](https://www.php-fig.org/psr/psr-7/#32-psrhttpmessagerequestinterface)
and a [PSR-7 Response](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface)
in a [Swoole\Http\Response](http://php.net/manual/en/class.swoole-http-response.php).

To convert a Swoole request in PSR-7 we provided the following static function:

```
Zend\Diactoros\ServerRequestFactory::fromSwoole(swoole_http_request $request)
```

Where `$request` is an instance of `swoole_http_request` (alias of
`Swoole\Http\Request`).

To convert a PSR-7 response in a Swoole response we built a specific emitter,
`Zend\Diactoros\Response\SwooleEmitter`. If you can use this emitter instead of
`Zend\Diactoros\Response\SapiEmitter`.

You need to instantiate the `SwooleEmitter` passing a `swoole_http_response`
object in the constructor.

You can execute Swoole with [Expressive](https://getexpressive.org/) using the
following server implementation:

```php
use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\Response\SwooleEmitter;

$http = new swoole_http_server("127.0.0.1", 9501);

$http->on("start", function ($server) {
echo "Swoole http server is started at http://127.0.0.1:9501\n";
});

// Bootstrap of zend-expressive
$container = require 'config/container.php';
$app = $container->get(\Zend\Expressive\Application::class);

$http->on("request", function ($request, $response) use ($app) {
$psr7Request = ServerRequestFactory::fromSwoole($request);
$psr7Response = $app->handle($psr7Request);

$emitter = new SwooleEmitter($response);
$emitter->emit($psr7Response);
});

$http->start();
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pages:
- "Emitting Responses": emitting-responses.md
- Serialization: serialization.md
- API: api.md
- Swoole: swoole.md
site_name: zend-diactoros
site_description: 'zend-diactoros: PSR-7 HTTP message implementation'
repo_url: 'https://github.com/zendframework/zend-diactoros'
Expand Down
88 changes: 88 additions & 0 deletions src/Response/SwooleEmitter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php
/**
* @see https://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Diactoros\Response;

use Psr\Http\Message\ResponseInterface;
use swoole_http_response;

/**
* @deprecated since 1.8.0. The package zendframework/zend-httphandlerrunner
* now provides this functionality.
*/
class SwooleEmitter implements EmitterInterface
{
use SapiEmitterTrait;

/**
* @see https://www.swoole.co.uk/docs/modules/swoole-http-server/methods-properties#swoole-http-response-write
*/
const CHUNK_SIZE = 2097152; // 2 MB

private $swooleResponse;

public function __construct(swoole_http_response $swooleResponse)
{
$this->swooleResponse = $swooleResponse;
}

/**
* Emits a response for the Swoole environment.
*
* @return void
*/
public function emit(ResponseInterface $response)
{
$this->emitStatusCode($response);
$this->emitHeaders($response);
$this->emitBody($response);
}

/**
* Emit the status code
*
* @return void
*/
private function emitStatusCode(ResponseInterface $response)
{
$this->swooleResponse->status($response->getStatusCode());
}

/**
* Emit the headers
*
* @return void
*/
private function emitHeaders(ResponseInterface $response)
{
foreach ($response->getHeaders() as $name => $values) {
$name = $this->filterHeader($name);
$this->swooleResponse->header($name, implode(', ', $values));
}
}

/**
* Emit the message body.
*
* @return void
*/
private function emitBody(ResponseInterface $response)
{
$body = $response->getBody();
$body->rewind();

if ($body->getSize() <= static::CHUNK_SIZE) {
$this->swooleResponse->end($body->getContents());
return;
}

while (! $body->eof()) {
$this->swooleResponse->write($body->read(static::CHUNK_SIZE));
}
$this->swooleResponse->end();
}
}
50 changes: 49 additions & 1 deletion src/ServerRequestFactory.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<?php
/**
* @see https://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
* @copyright Copyright (c) 2015-2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Diactoros;

use InvalidArgumentException;
use Psr\Http\Message\UploadedFileInterface;
use RuntimeException;
use stdClass;
use swoole_http_request;
use UnexpectedValueException;

use function array_change_key_case;
Expand Down Expand Up @@ -99,6 +101,52 @@ public static function fromGlobals(
);
}

/**
* Create a request from a Swoole request
*
* @param swoole_http_request $request
* @return ServerRequest
* @throws RuntimeException if Swoole is not installed
*/

public static function fromSwoole(swoole_http_request $request)
{
if (! extension_loaded('swoole')) {
throw new Exception\RuntimeException('Swoole extension is not installed!');
}
$get = isset($request->get) ? $request->get : [];
$post = isset($request->post) ? $request->post : [];
$cookie = isset($request->cookie) ? $request->cookie : [];
$files = isset($request->files) ? $request->files : [];

$server = [];
foreach ($request->server as $key => $value) {
$server[strtoupper($key)] = $value;
}

$headers = [];
foreach ($request->header as $name => $value) {
$headers[str_replace('-', '_', $name)] = $value;
}

$body = new Stream('php://temp', 'wb+');
$body->write($request->rawContent());
$body->rewind();

return new ServerRequest(
$server,
static::normalizeFiles($files),
static::marshalUriFromServer($server, $headers),
$server['REQUEST_METHOD'],
$body,
$headers,
$cookie,
$get,
$post,
static::marshalProtocolVersion($server)
);
}

/**
* Access a value in an array, returning a default value if not found
*
Expand Down
109 changes: 109 additions & 0 deletions test/Response/SwooleEmitterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php
/**
* @see https://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/

namespace ZendTest\Diactoros\Response;

use PHPUnit\Framework\TestCase;
use swoole_http_response;
use Zend\Diactoros\Response;
use Zend\Diactoros\Response\SwooleEmitter;

class SwooleEmitterTest extends TestCase
{
public function setUp()
{
if (! extension_loaded('swoole')) {
$this->markTestSkipped('The Swoole extension is not available');
}
$this->swooleResponse = $this->prophesize(swoole_http_response::class);
$this->emitter = new SwooleEmitter($this->swooleResponse->reveal());
}

public function testEmit()
{
$response = (new Response())
->withStatus(200)
->withAddedHeader('Content-Type', 'text/plain');
$response->getBody()->write('Content!');

$this->emitter->emit($response);

$this->swooleResponse
->status(200)
->shouldHaveBeenCalled();
$this->swooleResponse
->header('Content-Type', 'text/plain')
->shouldHaveBeenCalled();
$this->swooleResponse
->end('Content!')
->shouldHaveBeenCalled();
}

public function testMultipleHeaders()
{
$response = (new Response())
->withStatus(200)
->withHeader('Content-Type', 'text/plain')
->withHeader('Content-Length', '256');

$this->emitter->emit($response);

$this->swooleResponse
->status(200)
->shouldHaveBeenCalled();
$this->swooleResponse
->header('Content-Type', 'text/plain')
->shouldHaveBeenCalled();
$this->swooleResponse
->header('Content-Length', '256')
->shouldHaveBeenCalled();
}

public function testMultipleSetCookieHeaders()
{
$response = (new Response())
->withStatus(200)
->withAddedHeader('Set-Cookie', 'foo=bar')
->withAddedHeader('Set-Cookie', 'bar=baz');

$this->emitter->emit($response);

$this->swooleResponse
->status(200)
->shouldHaveBeenCalled();
$this->swooleResponse
->header('Set-Cookie', 'foo=bar, bar=baz')
->shouldHaveBeenCalled();
}

public function testEmitWithBigContentBody()
{
$content = base64_encode(random_bytes(SwooleEmitter::CHUNK_SIZE)); // CHUNK_SIZE * 1.33333
$response = (new Response())
->withStatus(200)
->withAddedHeader('Content-Type', 'text/plain');
$response->getBody()->write($content);

$this->emitter->emit($response);

$this->swooleResponse
->status(200)
->shouldHaveBeenCalled();
$this->swooleResponse
->header('Content-Type', 'text/plain')
->shouldHaveBeenCalled();
$this->swooleResponse
->write(substr($content, 0, SwooleEmitter::CHUNK_SIZE))
->shouldHaveBeenCalled();
$this->swooleResponse
->write(substr($content, SwooleEmitter::CHUNK_SIZE))
->shouldHaveBeenCalled();
$this->swooleResponse
->end()
->shouldHaveBeenCalled();
}
}
Loading

0 comments on commit ca92d0b

Please sign in to comment.