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.
GET /foo/bar.html HTTP/1.1 Host: example.com Accept: text/html ...
HTTP/1.1 200 OK Date: Fri, 18 May 2012 08:08:08 GMT Content-Length: 14 Content-Type: text/html Hello World!
session_start(); $name = $_GET['name']; echo $_SESSION['name']; $method = $_SERVER['REQUEST_METHOD']; $client_ip = $_SERVER['REMOTE_ADDR'];
if ($trust_proxy) { if (isset($_SERVER['HTTP_CLIENT_IP'])) { return $_SERVER['HTTP_CLIENT_IP']; } if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'], 2); return isset($ips[0]) ? trim($ips[0]) : ''; } } return $_SERVER['REMOTE_ADDR'];
use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); $request = Request::create('/hello.html', 'GET'); $request->overrideGlobals(); $request->query->get('name', 'Default'); $request->getSession()->get('name'); $request->getPathInfo(); $request->getClientIp(); Request::trustProxyData();
header('HTTP/1.0 404 Not Found'); header('Content-Type: text/html; charset=UTF-8'); setcookie('name', $name); $_SESSION['name'] = 'Larry'; echo 'Hello ' . $name;
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();
Like hooks, but
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());
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); }
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);
Method | Path | Accept header | Result |
---|---|---|---|
GET | node/1 | text/html | Node view page |
GET | node/1 | application/json | JSON-LD record |
POST | system/form/node_edit | text/html | Submit a node form and redirect |
PUT | node/1 | application/json | Overwrite the node exactly |
New routing syntax (farewell hook_menu()
?)
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); } }
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); } }
A large part of WSCCI is simply paying down years of
technical debt.
That means we can do other cool things.
drupal_set_message()