Web Services and Context Symfony
Core Initiative

by Larry Garfield

Wednesday 22 August 2012

@Crell

Web Services and Context Core Initiative (WSCCI)

WSCCI

The original plan...

The Web Services and Context Core Initiative (WSCCI) aims to transform Drupal from a first-class CMS to a first-class REST server with a first-class CMS on top of it. To do that, we must give Drupal a unified, powerful context system that will support smarter, context- sensitive, easily cacheable block-centric layouts and non-page responses using a robust unified plugin mechanism.

Huh?

REST-First

Development

"Pages" are a special case of REST

Blocks in Drupal 7

Blocks in Drupal 7

Drupal 7 page flow

Blocks/Requests in Drupal 8

Blocks in Drupal 8
Symfony2

Where did Symfony2 come from?

What is Symfony2?

Symfony2 Components

  • Reusable set of stand-alone libraries
  • Solid Object-Oriented toolset

Symfony2 Framework

  • Application framework
  • Build on top of the Components

So what are we using?

Symfony2 + Drupal 8 = Drufony

Drupal 8 page flow (we hope)

HttpKernelInterface

    namespace Symfony\Component\HttpKernel;

    interface HttpKernelInterface {
      /**
       * Handles a request.
       *
       * @param Request $request
       *   A request instance
       * @return $response
       *   A Response instance
       */
      function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
    }
    

HttpKernel

Kernel workflow

Excited yet?

I hope so, because we need your help...

What have we gotten done...

What have we gotten done...

  1. Request/Response
  2. Kernel-based workflow
  3. Event Dispatcher
  4. HttpKernel-based request handling
  5. Dependency Injection Container

We have all this awesome, but we're not really using it yet

If we stop now, we have a horribly over-engineered mess with few benefits

What do we still have to do...

What do we still have to do

  1. Finish the new Routing system (still needs ACL)
  2. Upgrade Ajax system to real-REST
  3. Advanced Content Negotiation
  4. Entity Property API unification (see @fago and @dixon_)
  5. Symfony sessions
  6. HTTP-based caching
  7. Block URLs (needed for ESI/partial page caching)
  8. Port non-HTML requests to new router (e.g. autocomplete)
  9. SCOTCH-support (figure out page/block conversion to controllers)
  10. Wither menu links?

What do we still want to do

  1. Entities Serialization (JSON-LD)
  2. Outgoing HTTP requests (Guzzle?)
  3. Path alias refactoring
  4. Implement Generators (route-backed url())
  5. Improved BinaryStreamedResponse
  6. Convert more systems to DI
  7. Convert low-level hooks to events
  8. Process form POST earlier to avoid partial page render
  9. Reduce bootstrap to just settings.php and legacy function loading
  10. Most of these 30 tagged issues

Volunteers?

Addtional background...

Symfony2's response API

  use Symfony\Component\HttpFoundation\Response;

  $response = new Response('No page. :-(', 404, array(
    'Content-Type' => 'text/plain',
  ));

  $response = new Response();
  $response->setContent('Hello World');
  $response->send();
  
    use Symfony\Component\HttpFoundation\StreamedResponse;

    $response = new StreamedResponse(function () {
      echo 'foo';
      flush();
      echo 'bar';
    });

    // Later
    $response->send();

    

EventDispatcher

EventDispatcher

Like hooks, but

EventDispatcher

  use Symfony\Component\EventDispatcher\EventDispatcher;

  $dispatcher = new EventDispatcher();

  // Somewhere...
  $callable = function (Event $event) {
    // do something
  };
  $dispatcher->addListener('event_name', $callable);

  // Equivalent of module_invoke_all().
  $dispatcher->dispatch('event_name', new Event());
  
    class PlusOneSubscribe implements EventSubscriberInterface {

      public function onMyEvent(Event $event) {
        // Do stuff
      }

      static function getSubscribedEvents() {
        $events['event_name'][] = array('onMyEvent');
        return $events;
      }
    }

    $dispatcher->addSubscriber(new PlusOneSubscribe());
    

HttpKernel

Routing

HttpKernel

index.php (before the Container)

    use Drupal\Core\DrupalKernel;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\EventDispatcher\EventDispatcher;
    use Symfony\Component\HttpKernel\Controller\ControllerResolver;

    // Create a request object from the HTTPFoundation.
    $request = Request::createFromGlobals();

    $dispatcher = new EventDispatcher();
    $resolver = new ControllerResolver();

    $kernel = new DrupalKernel($dispatcher, $resolver);
    $response = $kernel->handle($request);

    $response->prepare($request);
    $response->send();
    $kernel->terminate($request, $response);
    

So what changes?

Controllers

Routing

New routing syntax (farewell hook_menu()?)

Warning, early untested prototype
    function example_route_info() {
      $routes['node_page'] = array(
        'route' => new Route('/node/{node}', array(
          '_controller' => 'NodeController:show',
        )),
        // This gets objectified later...
        'access' => array(
          'callback' => array('function' => 'node_access'),
          'arguments' => array('view'),
        ),
      );
      return $routes;
    }

    class NodeController {
      public function show(Node $node) {
        // ...
        return new Response($content);
      }
    }
  

Routing (fancy)

    function example_route_info() {
      $routes['blog_list'] = array(
        'route' => new Route('/blog/{page}', array(
          '_controller' => 'BlogController:show',
          'page' => 1, // Default value
        ), array(
          'page' => '\d+',
          '_method' => 'GET',
        )),
        // This gets objectified later...
        'access' => array(
          'callback' => array('function' => 'node_access'),
          'arguments' => array('view'),
        ),
      );
      return $routes;
    }

    class BlogController {
      public function show(Request $request, $page) {
        // Link to router items, not random URLs.
        $link = $this->generator->generate('node_page', array('node' => 5));
        // ...
        return new Response($content);
      }
    }
  

Side-benefits

(Please help clean up old crap!)

A large part of WSCCI is simply paying down years of
technical debt.
That means we can do other cool things.

Things like... SCOTCH

Things like... Deployment

[*] The management will not be held responsible if "easy" is insufficiently easy for your client.

Collaboration

Work upstream

Streaming

Odds and ends

On the Drupal side...

Other issues

Questions?

http://groups.drupal.org/wscci