
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()