Part of #2492955: [meta] PSR-7 support
Blocked by #2497691: Include Symfony PSR-7 bridge library

Problem/Motivation

Right now, if a Controller returns a PSR-7 Response object Drupal doesn't know what to do with it and barfs. (Or rather, HttpKernel will raise an error.) That's not good, when there's a very simple conversion library available.

Proposed resolution

Add a View listener that checks for any PSR-7 Response object as a controller result, and uses the bridge library linked above to convert it to a Symfony Response. No muss, no fuss.

The listener should have one dependency, the converter mentioned above. It should check if the controller result is an instanceof RequestInterface, and if so, call that library and set the result as the response on the event. If not, do nothing.

Tagging as Novice as this should be a simple task. Ask Crell with any questions.

Remaining tasks

Do it.

API changes

None

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Task because nothing is broken per se so it's not a bug, but not an entirely new feature as PSR-7 is a natural extension of our Symfony and broader PHP integration.
Issue priority Major because it's not release blocking (thus not critical) but strategically important for Drupal
Disruption None. There should be no API breakage from this issue, and the overlap of the patches here with work elsewhere is, as far as I'm aware, minimal to none.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

marvin_B8’s picture

cilefen’s picture

Status: Active » Needs review

The last submitted patch, 1: psr-7-response-view-2497693-1.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 1: psr-7-response-view-service-2497693-1.patch, failed testing.

Crell’s picture

  1. +++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
    @@ -0,0 +1,49 @@
    +        if (!$controllerResult instanceof ResponseInterface) {
    +            return;
    +        }
    +        $event->setResponse($this->httpFoundationFactory->createResponse($controllerResult));
    

    This could be simplified. if it is an instance of ResponseInterface, then do the setResponse() call. Else do nothing.

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
    @@ -0,0 +1,49 @@
    +        $events[KernelEvents::TERMINATE][] = array('onKernelView');
    

    This should be KernelEvents::VIEW. TERMINATE is a different event.

+++ b/core/core.services.yml
@@ -852,6 +863,13 @@ services:
+  route_converts_psr_response_to_httpfoundation:

That's a very long and unweidly service name. Please check the other event subscribers and follow their style. If all else fails, the class name makes a good starting point.

Please merge both patches into a single patch file. It doesn't work if each file is in its own diff.

We also need a test to verify that it works. One simple test should be sufficient. Have a controller in a test module that returns a ResponseInterface object, and make sure that when requesting that path everything works correctly. (Stop by #drupal-contribute if you need help setting that up.)

Also note that you won't be able to finish this issue until #2497691: Include Symfony PSR-7 bridge library is in, since that's how you'd have a PSR-7 response to return. :-) It's a bit jumping the gun to start with this issue, but thank you for doing so!

Also, check your whitespace. There are a number of lines that have trailing whitespace, and there should be one blank line between a method and the start of the docblock after it.

Thanks!

marvin_B8’s picture

FileSize
8.2 KB

Second try =)

marvin_B8’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 6: psr-7-response-view-2497693-6.patch, failed testing.

Crell’s picture

  1. +++ b/core/core.services.yml
    @@ -852,6 +863,13 @@ services:
    +      - { name: event_subscriber }
    +      ¶
    +  ¶
    

    core.services.yml and RouterTest.php still have trailing whitespace. I suggest setting your IDE to auto-trim whitespace on save. It makes life a lot easier. :-)

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
    @@ -0,0 +1,62 @@
    +  /**
    +   * The httpFoundation factory.
    +   *
    +   * @var HttpFoundationFactoryInterface
    +   */
    +  private $httpFoundationFactory;
    

    Drupal convention is to use protected, not private, for object member variables. There are a few exceptions, but this isn't one of them.

  3. +++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
    @@ -0,0 +1,62 @@
    +  public static function getSubscribedEvents() {
    +    $events[KernelEvents::VIEW][] = array('onKernelView');
    +    return $events;
    +  }
    

    This is a new class, so let's be consistent and use new-style [] syntax everywhere. I'm not sure if we need to specify a priority on this listener.

    Obviously none of this will work until the library is available. :-)

marvin_B8’s picture

FileSize
8.19 KB
joshi.rohit100’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 10: psr-7-response-view-2497693-10.patch, failed testing.

joshi.rohit100’s picture

  1. +++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
    @@ -0,0 +1,62 @@
    +   *
    +   * @var HttpFoundationFactoryInterface
    +   */
    

    Class name should be full namespaces

  2. +++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
    @@ -0,0 +1,62 @@
    +   *
    +   * @param HttpFoundationFactoryInterface $http_foundation_factory
    +   *    The httpFoundation factory.
    

    same here

And same for the other places as well.

marvin_B8’s picture

FileSize
8.31 KB
marvin_B8’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 14: psr-7-response-view-2497693-14.patch, failed testing.

Crell’s picture

The blocking issue is in, so this can move forward.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 14: psr-7-response-view-2497693-14.patch, failed testing.

joshi.rohit100’s picture

Status: Needs work » Needs review
FileSize
8.28 KB
966 bytes
dawehner’s picture

Nice to see some progress going on!

+++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
@@ -0,0 +1,62 @@
+    $events[KernelEvents::VIEW][] = ['onKernelView'];

+++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
@@ -0,0 +1,102 @@
+  private function createEventMock($controller_result) {
...
+  private function createFactoryMock() {

We don't use private functions, let's make them protected

joshi.rohit100’s picture

Crell’s picture

Lookin' great! Just a few minor points, all of them nitpicks:

  1. +++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
    @@ -0,0 +1,62 @@
    +    $events[KernelEvents::VIEW][] = ['onKernelView'];
    

    Note that not setting a priority means that it gets a priority of 0, which is pretty low. However, in this case we want that because this is a likely-rare subscriber, so only initializing it late (after the more common render-based listeners have a try) makes sense to me.

    No change needed here, just calling it out.

  2. +++ b/core/modules/system/tests/modules/router_test_directory/src/TestControllers.php
    @@ -12,6 +12,9 @@
    +use Zend\Diactoros\Response as PSRResponse;
    

    Nit: Alias to PsrResponse, not PSRResponse.

  3. +++ b/core/modules/system/tests/modules/router_test_directory/src/TestControllers.php
    @@ -102,6 +105,13 @@ public function test21() {
    +    return $response;
    

    I believe the version of Diactoros we included has utility subclasses available, so you can just use return new HtmlResponse('test123'); and it will handle creating the stream for you. It should still be a valid test of this patch.

    (And that would mean no need to alias the class above, invalidating my previous comment.)

  4. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,102 @@
    +    $event->expects($this->once())->method('setResponse')->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Response'));
    

    Nit: Please align the method calls vertically to make it easier to read. To wit:

    $event
      ->expects(...)
      ->method(...)
      ->with(...);
    

    (Same in a few other places.)

  5. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,102 @@
    +    $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent', array(), array(), '', NULL);
    

    As long as we're going to need another reroll anyway, let's use new-style arrays exclusively.

joshi.rohit100’s picture

Point 2, 3, 4, 5 is done.
point - 1 not sure what should be the priority here.

dawehner’s picture

Yeah I would vote for something later like -32

Crell’s picture

Status: Needs review » Reviewed & tested by the community

I was more indicating that the current code was fine. The order is not critical as there's no dependencies, and the default puts it after most of our common stuff anyway I think.

So, RTBC! (Darling it's better with those four letters...)

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. This issue summary needs to reference the beta evaluation that's in the meta issue as all tasks are supposed to have beta evaluations.
  2. The change does exactly what it is meant to. Just found a few minor nits below
  3. +++ b/core/lib/Drupal/Core/EventSubscriber/PsrResponseSubscriber.php
    @@ -0,0 +1,62 @@
    +   * Do the conversion if applicable and update the response of the event.
    

    Converts a PSR-7 response to a Symfony response.

  4. +++ b/core/modules/system/src/Tests/Routing/RouterTest.php
    @@ -199,6 +199,15 @@ public function testRouterMatching() {
    +   * Tests that a psr-7 response work.
    

    psr should be in capitals and work should be works

  5. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,109 @@
    +   *   The tested psr7 response subscriber.
    ...
    +   *    The mocked factory.
    

    These lines are not necessary.

  6. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,109 @@
    +   * @var \Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface
    

    This is a mock so needs a type hint like:
    \Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface|\PHPUnit_Framework_MockObject_MockObject

  7. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,109 @@
    +    $this->httpFoundationFactoryMock = $this->createFactoryMock();
    ...
    +  /**
    +   * Sets up an httpFoundationFactory.
    +   *
    +   * @return \Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface
    +   *    A mock object to test.
    +   */
    +  protected function createFactoryMock() {
    +    $factory = $this->getMock('Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface', [], [], '', NULL);
    +    $factory
    +      ->expects($this->any())
    +      ->method('createResponse')
    +      ->will($this->returnValue($this->getMock('Symfony\Component\HttpFoundation\Response')));
    +    return $factory;
    +  }
    

    Might as well do this in the test setUp. The method to create the httpFoundationFactory is not really necessary.

  8. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,109 @@
    +    $event = $this->createEventMock([]);
    ...
    +    $event = $this->createEventMock(NULL);
    ...
    +   * @param object $controller_result
    +   *    The return Object.
    

    $controller result is mixed.

  9. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,109 @@
    +   * @return \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent
    

    @return \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent|\PHPUnit_Framework_MockObject_MockObject

  10. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,109 @@
    +      ->expects($this->any())
    

    Can this be $this->once()?

  11. +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/PsrResponseSubscriberTest.php
    @@ -0,0 +1,109 @@
    +      ->will($this->returnValue($controller_result));
    ...
    +      ->will($this->returnValue($this->getMock('Symfony\Component\HttpFoundation\Response')));
    

    ->willReturn($controller_result)

borisson_’s picture

Status: Needs work » Needs review
FileSize
4.15 KB
7.88 KB

This fixes the nits from #27.

marvin_B8’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me, so RTBC

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 28: add_psr_7_to_symfony-2497693-28.patch, failed testing.

Status: Needs work » Needs review
Crell’s picture

Status: Needs review » Reviewed & tested by the community

Looks like bot being silly.

alexpott’s picture

#27.1 still needs addressing.

borisson_’s picture

Issue summary: View changes

Added beta evaluation from meta issue.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Thanks for addressing my feedback and adding the beta evaluation to the issue summary. Committed 3349d70 and pushed to 8.0.x. Thanks!

  • alexpott committed 3349d70 on 8.0.x
    Issue #2497693 by marvin_B8, joshi.rohit100, borisson_, Crell: Add PSR-7...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.