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!
1 | session_start(); |
2 |
3 | $name = $_GET [ 'name' ]; |
4 |
5 | echo $_SESSION [ 'name' ]; |
6 |
7 | $method = $_SERVER [ 'REQUEST_METHOD' ]; |
8 |
9 | $client_ip = $_SERVER [ 'REMOTE_ADDR' ]; |
01 | if ( $trust_proxy ) { |
02 | if (isset( $_SERVER [ 'HTTP_CLIENT_IP' ])) { |
03 | return $_SERVER [ 'HTTP_CLIENT_IP' ]; |
04 | } |
05 | if (isset( $_SERVER [ 'HTTP_X_FORWARDED_FOR' ])) { |
06 | $ips = explode ( ',' , $_SERVER [ 'HTTP_X_FORWARDED_FOR' ], 2); |
07 | return isset( $ips [0]) ? trim( $ips [0]) : '' ; |
08 | } |
09 | } |
10 | return $_SERVER [ 'REMOTE_ADDR' ]; |
01 | use Symfony\Component\HttpFoundation\Request; |
02 |
03 | $request = Request::createFromGlobals(); |
04 |
05 | $request = Request::create( '/hello.html' , 'GET' ); |
06 | $request ->overrideGlobals(); |
07 |
08 | $request ->query->get( 'name' , 'Default' ); |
09 | $request ->getSession()->get( 'name' ); |
10 | $request ->getPathInfo(); |
11 | $request ->getClientIp(); |
12 | Request::trustProxyData(); |
1 | header( 'HTTP/1.0 404 Not Found' ); |
2 | header( 'Content-Type: text/html; charset=UTF-8' ); |
3 |
4 | setcookie( 'name' , $name ); |
5 | $_SESSION [ 'name' ] = 'Larry' ; |
6 |
7 | echo 'Hello ' . $name ; |
1 | use Symfony\Component\HttpFoundation\Response; |
2 |
3 | $response = new Response( 'No page. :-(' , 404, array ( |
4 | 'Content-Type' => 'text/plain' , |
5 | )); |
6 |
7 | $response = new Response(); |
8 | $response ->setContent( 'Hello World' ); |
9 | $response ->send(); |
01 | use Symfony\Component\HttpFoundation\StreamedResponse; |
02 |
03 | $response = new StreamedResponse( function () { |
04 | echo 'foo' ; |
05 | flush (); |
06 | echo 'bar' ; |
07 | }); |
08 |
09 | // Later |
10 | $response ->send(); |
Like hooks, but
01 | use Symfony\Component\EventDispatcher\EventDispatcher; |
02 |
03 | $dispatcher = new EventDispatcher(); |
04 |
05 | // Somewhere... |
06 | $callable = function (Event $event ) { |
07 | // do something |
08 | }; |
09 | $dispatcher ->addListener( 'event_name' , $callable ); |
10 |
11 | // Equivalent of module_invoke_all(). |
12 | $dispatcher ->dispatch( 'event_name' , new Event()); |
01 | class PlusOneSubscribe implements EventSubscriberInterface { |
02 |
03 | public function onMyEvent(Event $event ) { |
04 | // Do stuff |
05 | } |
06 |
07 | static function getSubscribedEvents() { |
08 | $events [ 'event_name' ][] = array ( 'onMyEvent' ); |
09 | return $events ; |
10 | } |
11 | } |
12 |
13 | $dispatcher ->addSubscriber( new PlusOneSubscribe()); |
01 | namespace Symfony\Component\HttpKernel; |
02 |
03 | interface HttpKernelInterface { |
04 | /** |
05 | * Handles a request. |
06 | * |
07 | * @param Request $request |
08 | * A request instance |
09 | * @return $response |
10 | * A Response instance |
11 | */ |
12 | function handle(Request $request , $type = self::MASTER_REQUEST, $catch = true); |
13 | } |
01 | use Drupal\Core\DrupalKernel; |
02 | use Symfony\Component\HttpFoundation\Request; |
03 | use Symfony\Component\EventDispatcher\EventDispatcher; |
04 | use Symfony\Component\HttpKernel\Controller\ControllerResolver; |
05 |
06 | // Create a request object from the HTTPFoundation. |
07 | $request = Request::createFromGlobals(); |
08 |
09 | $dispatcher = new EventDispatcher(); |
10 | $resolver = new ControllerResolver(); |
11 |
12 | $kernel = new DrupalKernel( $dispatcher , $resolver ); |
13 | $response = $kernel ->handle( $request ); |
14 |
15 | $response ->prepare( $request ); |
16 | $response ->send(); |
17 | $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()
?)
01 | function example_route_info() { |
02 | $routes [ 'node_page' ] = array ( |
03 | 'route' => new Route( '/node/{node}' , array ( |
04 | '_controller' => 'NodeController:show' , |
05 | )), |
06 | // This gets objectified later... |
07 | 'access' => array ( |
08 | 'callback' => array ( 'function' => 'node_access' ), |
09 | 'arguments' => array ( 'view' ), |
10 | ), |
11 | ); |
12 | return $routes ; |
13 | } |
14 |
15 | class NodeController { |
16 | public function show(Node $node ) { |
17 | // ... |
18 | return new Response( $content ); |
19 | } |
20 | } |
01 | function example_route_info() { |
02 | $routes [ 'blog_list' ] = array ( |
03 | 'route' => new Route( '/blog/{page}' , array ( |
04 | '_controller' => 'BlogController:show' , |
05 | 'page' => 1, // Default value |
06 | ), array ( |
07 | 'page' => '\d+' , |
08 | '_method' => 'GET' , |
09 | )), |
10 | // This gets objectified later... |
11 | 'access' => array ( |
12 | 'callback' => array ( 'function' => 'node_access' ), |
13 | 'arguments' => array ( 'view' ), |
14 | ), |
15 | ); |
16 | return $routes ; |
17 | } |
18 |
19 | class BlogController { |
20 | public function show(Request $request , $page ) { |
21 | // Link to router items, not random URLs. |
22 | $link = $this ->generator->generate( 'node_page' , array ( 'node' => 5)); |
23 | // ... |
24 | return new Response( $content ); |
25 | } |
26 | } |
A large part of WSCCI is simply paying down years of
technical debt.
That means we can do other cool things.
drupal_set_message()