Skip to content

Create PSR-7 messages using PSR-17 factories #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ env:
global:
- PHPUNIT_FLAGS="-v"
- SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit"
- DEPENDENCIES="zendframework/zend-diactoros:^1.4.1"
- DEPENDENCIES="zendframework/zend-diactoros:^1.4.1 http-interop/http-factory-diactoros:^1.0"

matrix:
fast_finish: true
Expand All @@ -26,8 +26,11 @@ matrix:
dist: 'precise'
env: DEPENDENCIES=""
- php: 5.4
env: DEPENDENCIES="zendframework/zend-diactoros:^1.4.1"
- php: 5.5
env: DEPENDENCIES="zendframework/zend-diactoros:^1.4.1"
- php: 5.6
env: DEPENDENCIES="zendframework/zend-diactoros:^1.4.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these three lines needed? Global value is fine, right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Global value now includes http-interop/http-factory-diactoros:^1.0 which requires psr/http-factory:^1.0 which requires PHP >= 7.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okey. Good

- php: 7.0
- php: 7.1
- php: 7.2
Expand All @@ -36,9 +39,9 @@ matrix:
# Test LTS versions. This makes sure we do not use Symfony packages with version greater
# than 2 or 3 respectively.
- php: 7.2
env: DEPENDENCIES="symfony/lts:^2 symfony/force-lowest:~2.8.0 zendframework/zend-diactoros:^1.4.1"
env: DEPENDENCIES="$DEPENDENCIES symfony/lts:^2 symfony/force-lowest:~2.8.0"
- php: 7.2
env: DEPENDENCIES="symfony/lts:^3 symfony/force-lowest:~3.4.0 zendframework/zend-diactoros:^1.4.1"
env: DEPENDENCIES="$DEPENDENCIES symfony/lts:^3 symfony/force-lowest:~3.4.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these two changes needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to add http-interop/http-factory-diactoros:^1.0, so I prefer to make explicit that we are using here default dependencies combined with the symfony metapackages to force testing with LTS versions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But http-interop/http-factory-diactoros is already in the global. Right?
So no need to add it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if we overwrite the dependencies here without including the previous value, this configuration will mark DiactorosFactoryTest and PsrHttpFactoryTest as skipped because zendframework/zend-diactoros and http-interop/http-factory-diactoros won't be installed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hehe. I’ve been reviewing this PR multiple times but I’ve always read this change wrong.

Thank you for you explanation and your patience. Of course, this change is valid.


# Latest commit to master
- php: 7.2
Expand Down
2 changes: 1 addition & 1 deletion Factory/DiactorosFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public function createResponse(Response $symfonyResponse)
ob_start(function ($buffer) use ($stream) {
$stream->write($buffer);

return false;
return '';
});

$symfonyResponse->sendContent();
Expand Down
177 changes: 177 additions & 0 deletions Factory/PsrHttpFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\PsrHttpMessage\Factory;

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Psr\Http\Message\UploadedFileInterface;
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
* Builds Psr\HttpMessage instances using a PSR-17 implementation.
*
* @author Antonio J. García Lagar <[email protected]>
*/
class PsrHttpFactory implements HttpMessageFactoryInterface
{
private $serverRequestFactory;
private $streamFactory;
private $uploadedFileFactory;
private $responseFactory;

public function __construct(ServerRequestFactoryInterface $serverRequestFactory, StreamFactoryInterface $streamFactory, UploadedFileFactoryInterface $uploadedFileFactory, ResponseFactoryInterface $responseFactory)
{
$this->serverRequestFactory = $serverRequestFactory;
$this->streamFactory = $streamFactory;
$this->uploadedFileFactory = $uploadedFileFactory;
$this->responseFactory = $responseFactory;
}

/**
* {@inheritdoc}
*/
public function createRequest(Request $symfonyRequest)
{
$request = $this->serverRequestFactory->createServerRequest(
$symfonyRequest->getMethod(),
$symfonyRequest->getSchemeAndHttpHost().$symfonyRequest->getRequestUri(),
$symfonyRequest->server->all()
);

foreach ($symfonyRequest->headers->all() as $name => $value) {
$request = $request->withHeader($name, $value);
}

if (PHP_VERSION_ID < 50600) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the constant should be fully qualified, to allow the compiler to optimize this condition

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and do we even care about PHP 5.5 here ? AFAICT, the PSR factory does not support it

$body = $this->streamFactory->createStreamFromFile('php://temp', 'wb+');
$body->write($symfonyRequest->getContent());
} else {
$body = $this->streamFactory->createStreamFromResource($symfonyRequest->getContent(true));
}

$request = $request
->withBody($body)
->withUploadedFiles($this->getFiles($symfonyRequest->files->all()))
->withCookieParams($symfonyRequest->cookies->all())
->withQueryParams($symfonyRequest->query->all())
->withParsedBody($symfonyRequest->request->all())
;

foreach ($symfonyRequest->attributes->all() as $key => $value) {
$request = $request->withAttribute($key, $value);
}

return $request;
}

/**
* Converts Symfony uploaded files array to the PSR one.
*
* @param array $uploadedFiles
*
* @return array
*/
private function getFiles(array $uploadedFiles)
{
$files = array();

foreach ($uploadedFiles as $key => $value) {
if (null === $value) {
$files[$key] = $this->uploadedFileFactory->createUploadedFile($this->streamFactory->createStream(), 0, UPLOAD_ERR_NO_FILE);
continue;
}
if ($value instanceof UploadedFile) {
$files[$key] = $this->createUploadedFile($value);
} else {
$files[$key] = $this->getFiles($value);
}
}

return $files;
}

/**
* Creates a PSR-7 UploadedFile instance from a Symfony one.
*
* @param UploadedFile $symfonyUploadedFile
*
* @return UploadedFileInterface
*/
private function createUploadedFile(UploadedFile $symfonyUploadedFile)
{
return $this->uploadedFileFactory->createUploadedFile(
$this->streamFactory->createStreamFromFile(
$symfonyUploadedFile->getRealPath()
),
(int) $symfonyUploadedFile->getSize(),
$symfonyUploadedFile->getError(),
$symfonyUploadedFile->getClientOriginalName(),
$symfonyUploadedFile->getClientMimeType()
);
}

/**
* {@inheritdoc}
*/
public function createResponse(Response $symfonyResponse)
{
$response = $this->responseFactory->createResponse($symfonyResponse->getStatusCode());

if ($symfonyResponse instanceof BinaryFileResponse) {
$stream = $this->streamFactory->createStreamFromFile(
$symfonyResponse->getFile()->getPathname()
);
} else {
$stream = $this->streamFactory->createStreamFromFile('php://temp', 'wb+');
if ($symfonyResponse instanceof StreamedResponse) {
ob_start(function ($buffer) use ($stream) {
$stream->write($buffer);

return '';
});

$symfonyResponse->sendContent();
ob_end_clean();
} else {
$stream->write($symfonyResponse->getContent());
}
}

$response = $response->withBody($stream);

$headers = $symfonyResponse->headers->all();
$cookies = $symfonyResponse->headers->getCookies();
if (!empty($cookies)) {
$headers['Set-Cookie'] = array();

foreach ($cookies as $cookie) {
$headers['Set-Cookie'][] = $cookie->__toString();
}
}

foreach ($headers as $name => $value) {
$response = $response->withHeader($name, $value);
}

$protocolVersion = $symfonyResponse->getProtocolVersion();
$response = $response->withProtocolVersion($protocolVersion);

return $response;
}
}
Loading