Skip to content

Initial support #1

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

Closed
wants to merge 37 commits into from
Closed

Initial support #1

wants to merge 37 commits into from

Conversation

dunglas
Copy link
Member

@dunglas dunglas commented May 21, 2015

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets n/a
License MIT
Doc PR symfony/symfony-docs#5331
  • Composer dependencies
  • Interfaces
  • Stub
  • HttpFoundationFactory implementation
  • DiactorosFactory implementation
  • StreamedResponse

*
* @author Kévin Dunglas <[email protected]>
*/
interface HttpFoundationFactoryInterface
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure we need interfaces here (there is no reason to reimplement the conversion in a different way IMO). And I would rather name them converters than factories

Copy link
Member Author

Choose a reason for hiding this comment

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

@stof I agree but it was to be consistent with the other side, and the other side needs an interface.

Copy link
Member

Choose a reason for hiding this comment

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

does it actually need one though ? If we want to make it usable with other PSR-7 implementation, we could just accept a constructor callable to instantiate the Request/Response rather than forcing to reimplement the whole conversion (which is not only about the constructor)

@stof
Copy link
Member

stof commented May 22, 2015

My idea was to have separate classes for the Request and the Response converters, because a typical use case will need to convert the Request one way and the Response the other way.

@dunglas
Copy link
Member Author

dunglas commented May 22, 2015

The userland API I was thinking about was:

public function myAction(Request $mySymfonyRequest)
{
    $psr7Request = $this->get('http_message_factory')->createRequest($mySymfonyRequest);
    $psr7Response = $somePsr7Middleware->execute($psr7Request)

    return $this->get('http_foundation_factory')->createResponse($psr7Response);
}

What do you think about it?

@dunglas
Copy link
Member Author

dunglas commented May 22, 2015

As you can see, the UploadedFile handling is tricky because PSR-7 doesn't give access to the original $_FILES['my_file']['tmp_name'] key.
My current implementation works but must be double-checked and enhanced concerning error handling, security and performances.

@stof
Copy link
Member

stof commented May 22, 2015

@dunglas I don't think we will register them as services. Using them in a controller looks really weird. The use case is more for middleware compatibily IMO (a zend stragility middleware wrapping HttpKernel or an HttpKernel wrapping a zend diactoros server for instance).
And this is also why I don't think it make sense to have the Request and Response converters in the same class as you will not need both anyway

/**
* {@inheritdoc}
*/
public function createUploadedFile(UploadedFile $symfonyUploadedFile)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this should be public

Copy link
Member Author

Choose a reason for hiding this comment

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

Don't you think it can be useful in many cases to convert from Symfony to PSR UploadedFile and vice versa?

Copy link
Member

Choose a reason for hiding this comment

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

Well, we could make it public later if we get requests about it. But for now, I would keep it private until we get more feedback (increasing the visibility of a method is easy. Decreasing it is forbidden outside major versions, and it is still a pain for major versions if we want to provide an easy upgrade)

@fabpot
Copy link
Member

fabpot commented May 22, 2015

I was more thinking about having proxy classes. Having factories and services seems overkill here. The "converters" are going to be used at the edges anyway, not in your core code.

@stof
Copy link
Member

stof commented May 22, 2015

@fabpot having proxy classes is possible for the HttpFoundation -> PSR-7 is possible (this is what https://github.com/Sam-Burns/psr7-symfony-httpfoundation started to do), but it is also a pain to ensure that we can never perform external mutation of the wrapped objects.
However, the opposite way is not possible (HttpFoundation does not define interfaces that we could reimplement on top of PSR-7).
And having only the 1-way adapter is useless as a typical usecase will need to convert the request one way and the response the other way.

@stof
Copy link
Member

stof commented May 22, 2015

And this library only implements the easy parts for now (uploaded files and the body stream are not yet adapted)

@dunglas
Copy link
Member Author

dunglas commented May 22, 2015

Agree with @stof, creating proxies will be a huge pain IMO.

The service was just an example. I'm thinking about a way to introduce progressively PSR-7 in the core of Symfony.

What do you think about the following:

Additionally to HttpFoundation\Request, allowing to inject Message\ServerRequestInterface in actions of controllers:

public function myAction(\Psr\Http\Message\ServerRequestInterface $psrRequest);

We will only need to adapt the following snippet https://github.com/symfony/symfony/blob/2.7/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php#L115 to convert the Symfony Request to the PSR one using the factory.

Similarly, it is possible to add methods returning instances of PSR ServerRequestInterface and ResponseInterface (using internally the factory) to Symfony\Component\HttpKernel\Event and Symfony\Component\HttpKernel\\GetResponseEvent.

It will improve drastically the interoperability of Symfony with other PSR-7 compliant libraries.

@jakzal
Copy link

jakzal commented May 22, 2015

@stof could you make a brain dump of what you had in mind to implement? :)

@stof
Copy link
Member

stof commented May 22, 2015

@dunglas returning a PSR request from events would be a pain, because listeners are allowed to mutate the request, and we would then have to provide a way to set the updated PSR request in the event, which would then have to mutate the existing HttpFoundation Request. Too much trouble IMO.

@stof
Copy link
Member

stof commented May 22, 2015

@jakzal My idea was 2 have 4 converter classes (Request/Response, both ways => 4 needed).
This is approximately what @dunglas started except he put the Request and Response conversion in the same class (which does not match the needs of actual use cases) and that I don't think interfaces are useful here (I don't see a reason to reimplement the whole conversion yourselves just to use a different PSR-7 implementation)

@dunglas
Copy link
Member Author

dunglas commented May 22, 2015

@stof I've no strong opinion against splitting factories for request / response, lets do that if every one agree, but I cannot find the problem with the following:

$httpFoundationFactory = new \Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory();
$psrFactory = new \Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory();

$response = new \Zend\Diactoros\Response();

$done = function($request, $reponse) {
   $symfonyResponse = $httpFoundationFactory->createResponse($reponse);
  // Do the something the Symfony way
};

$server = new \Zend\Diactoros\Server(
    function ($psrFactory->createRequest($symfonyRequest), $response, $done) {
        $response->getBody()->write("Hello world!");    },
    $request,
    $response
);

@dunglas
Copy link
Member Author

dunglas commented May 27, 2015

Added cookies support.

@dunglas
Copy link
Member Author

dunglas commented May 27, 2015

All issues raised by @stof (thank you!) should be fixed now.

$request = new ServerRequest(
$server,
DiactorosRequestFactory::normalizeFiles($this->getFiles($symfonyRequest->files->all())),
DiactorosRequestFactory::marshalUriFromServer($server, $headers),
Copy link
Member

Choose a reason for hiding this comment

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

why not reusing $symfonyRequest->getRequestUri() ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because Symfony sometimes generates bad URIs. For instance with the request I build in the test, the generated URL is http://:/ (because some $_SERVER parameters are missing).

Copy link
Member

Choose a reason for hiding this comment

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

I like @stof's general idea - we should use as much information from Symfony's Request as possible, since it's already calculated there and I expect information to be the same. For example, what if the scheme is http in Symfony's Request, but due to some x-forwarded-proto differences (perhaps you don't have something a proxy trusted in Symfony's Request), Diactoros reads the untrusted x-forwarded-proto and treats it as https.

Copy link
Member Author

Choose a reason for hiding this comment

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

Why not. So I'll fix the test with the correct $_SEVER keys but if we got some complaints about strange bugs here we should fix HttpFoundation directly.

});

$symfonyResponse->sendContent();
ob_end_clean();
Copy link
Member

Choose a reason for hiding this comment

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

This means that the streamed content will in fact be read into memory, correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because I use php://temp, after 2Mo the data will be stored in a temporary file on disk.

@dunglas
Copy link
Member Author

dunglas commented May 27, 2015

  • BinaryFileResponse support
  • Use the URI of HttpFoundation

@fabpot
Copy link
Member

fabpot commented May 29, 2015

Looks good to me.

@dunglas Anything else before merging and releasing 0.1?

@dunglas
Copy link
Member Author

dunglas commented May 29, 2015

Looks good to me too for this one.

The last thing to do is deciding the strategy for cookie handling (copy/pasting BrowserKit, making BrowserKit a dependency or letting that as is but this can be done in a future version.

I've made some tests today with the standard edition of Symfony 2.6 + SensioFrameworkExtraBundle master and it works well.

@fabpot
Copy link
Member

fabpot commented May 29, 2015

Thank you @dunglas.

@fabpot fabpot closed this in 3f8977e May 29, 2015
@fabpot
Copy link
Member

fabpot commented May 29, 2015

v 0.1 is released now: https://packagist.org/packages/symfony/psr-http-message-bridge

@dunglas Can you update the PR on the bundle?

@dunglas
Copy link
Member Author

dunglas commented May 29, 2015

Done in fc8241c. Travis is running.

@weaverryan
Copy link
Member

Wow, congrats! This certainly exceeded my expectations in speed. 🍻 for @dunglas

@malisetti
Copy link

Congratulations @dunglas

weaverryan added a commit to symfony/symfony-docs that referenced this pull request Jun 19, 2015
This PR was merged into the 2.7 branch.

Discussion
----------

[PSR-7] Bridge documentation

| Q             | A
| ------------- | ---
| Doc fix?      | no
| New docs?     | yes
| Applies to    | 2.7 symfony/psr-http-message-bridge#1
| Fixed tickets | n/a

Documents the PSR-7 Bridge. As there was no previous doc for bridges, I created a structure but not sur if this is a good idea.

Commits
-------

695fe21 [PSR-7] Bridge documentation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants