The following is an over-all architectural plan and quasi-roadmap based on discussions surrounding Blocks TNG and Context at DrupalCon SF 2010. Special thanks to co-editors and reviewers Jeff Eaton, Earl Miles, and Dmitri Gaskin for their feedback to date.
- Centralzing all "context" into a single object rather than scattered about various globals, collector functions, and statics.
- Allow portions of core to be segmented into easily testable parts.
- Allow for centralized, lazy-loaded, pluggable access to "rich context" that is not currently available in any standard fashion. (Current OG, etc.)
- Convert core's limited routing system (hook_menu) into a flexible, pluggable dispatch system.
- Provide an in-core page building mechanism built around enhanced, context-sensitive blocks modeled on the Panels module.
- Leverage the centralized context system to streamline and simplify "abnormal state" conditions, such as the installer, update script, Drush, and database failures.
- Provide an in-core mechanism for non-web-page responses to HTTP requests, including AHAH fragments, JSON, ReST, SOAP, etc. This effectively turns into "services in core".
Implementation will proceed in multiple phases. Each phase is designed to be a discrete atomic upgrade. Although the overall goal is to implement all phases eventually, Drupal will be stable and functional should development stop or be delayed between any two phases while still be a marked improvement over prior phases.
It is possible that not all phases will happen during the Drupal 8 development cycle. That is OK. As long as we are not in mid-phase when Drupal 8 goes into code freeze we will be left with a stable and greatly improved system.
Phase 1: Context Object
During bootstrap, we create a context object instance of a class with a defined interface. This object will initially contain information derived from the HTTP request itself. The context object will be responsible for ensuring sane defaults are returned in unusual cases.
- GET parameters
- POST parameters if any
- Cookie values
- Session information
- $_SERVER information
- Additional information available directly from the HTTP request or equivalent
It will also include the following derived information that other code may assume is always present. The context object will be responsible for ensuring sane defaults are returned in unusual cases.
- Current language
- Requested path (both raw and de-aliased)
The context object will also offer centralized access to the following derived contextual information that MAY be lazy-loaded on request.
- Current user (Because this is explicitly driven by session/cookie in the Request)
- Level-1 loaded entities (i.e., what is currently loaded by %node and similar menu callbacks)
- Pager state
- Others that may be found during implementation
The context object will not be alterable. Once a given element of context has been determined, it will remain constant for the remainder of that context object's lifetime. The context object will, however, provide a mechanism to wrap itself in a mock context object that allows for overriding of selected context elements at creation time only. This mechanism is modeled on the mocking mechanism present in many testing frameworks. The wrapped context object may then be passed on to other routines which will then not know that those elements have been mocked.
A global accessor mechanism, most likely a drupal_get_context() function, will be provided. This accessor will do nothing except return the current context object so that modules and core APIs may act upon the context as appropriate.
The drupal_get_context() function will be marked deprecated immediately upon its addition. It will be documented as a transitional mechanism only.
Where relevant and appropriate, various systems and routines will be built to accept a context object as a function parameter or constructor parameter rather than accessing the global object. That will allow those systems to be successfully mocked, which makes them inherently more unit testable and maintainable. This pattern will also be used by the Blocks TNG effort (Phase 5, below).
All existing uses of one-off context in core will be removed in favor of the global context object. These include, but are not limited to, arg(), drupal_get_normal_path(), the global $user and $language variables, the $pager_* globals, all references to PHP super globals ($_GET, $_POST, etc.), and numerous other instances of scattered context. This functionality will be available in a more robust and abstracted form in the context object. Once all uses of such scattered context are expunged, the existing mechanisms will be removed. (That is, arg() will be removed from core in this step. There will then be a party that includes ponies.)
A backport of the context object will be maintained for Drupal 7 such that forward-looking modules may leverage it instead of the existing disparate mechanisms, allowing for cleaner code and a smoother transition to Drupal 8 when the time comes.
The Drupal 7 implementation of the context object will likely be a simple wrapper around the existing functions and globals in many cases. However, as long as the Interface for the object remains consistent between the Drupal 8 core and Drupal 7 contrib implementations that is not a problem.
Phase 2: Extended context
A registration system will be provided to allow modules to register components with the context object that will answer additional contextual requests on demand, such as:
- Current Organic Group(s)
- Current user-blog
- Book module navigation location
- Forum hierarchy
- Any others that a module chooses to register itself for.
The exact mechanism for such registration has not yet been defined. The context object will be responsible for ensuring sane defaults are returned in unusual cases or if a given additional context is not registered.
Phase 3: Display controllers
The second phase involves introducing a display controller mechanism that controls the handling of request responses. It is an evolution of the delivery callback present in Drupal 7, providing a much more robust set of options and operating far earlier in the page request process. It involves splitting what is currently hook_menu and menu_execute_active_handler() into separate, pluggable Dispatcher and Display Controller. This means that we can completely swap out hook_menu for something else.
The general mechanism is as follows:
- Drupal builds the context object as above.
- The context object is passed to a Dispatcher routine that, based on the context, selects the appropriate Display Controller object and configuration for the Display Controller. The Dispatcher may leverage a variety of information made available by the context object as necessary, including GET parameters, URL fragments, HTTP headers, and the ReST standard.
- Drupal will call the appropriate method on the Display Controller object to generate a string, which will be returned to the requestor. This may be a full HTML page, HTML fragment, JSON object, SOAP response, ReST response, or any other response type. The Display Controller may also specify additional HTTP headers to be included in the response via another appropriate method.
Each Display Controller implementation is responsible for generating the appropriate response based on the supplied context and configuration.
Initially we will need a display controller that returns a complete HTML page based on hook_menu information. It is essentially the current system replicated in an object.
As a side effect of the dispatcher, the bulk of the services module becomes supported directly in core. The services module should serve as inspiration for how to implement the dispatcher properly. Use of ReSTful standards here is recommended.
Core should support the following additional display controllers that may be developed in parallel once the system is in place:
- Form submission handling based on POST data.
- JSON object response. (Necessary for auto-complete functionality.)
The exact interaction and separation between the Dispatcher and Display Controller requires further investigation.
Providing clean support for tools like AtomPub, PubSubhubbub, CSV output, and so on becomes an exercise for contrib, or for Tim O'Reilly.
Phase 4: Clean up hacky systems
Convert install.php, update.php, authorize.php and similar alternate environments to use a custom display controller and context object. This will provide a substantial simplification of all of the above systems.
Potentially a "detect broken state" check could also route off to a special maintenance mode display controller.
Phase 5: Blocks TNG
The fifth phase involves implementing a new block-centric display controller using the system developed in phase 2. This system is intended to replace the hook_menu-based implementation. It will also by nature provide support for block-specific AHAH responses.
This will involve conversion of blocks into classed objects with configuration objects, inspired by Panels Content Panes. It may be advisable to rename blocks to "components" or some other name in order to provide more designer-friendly terminology. The net result of this phase includes considerably more power in core as well as a layout mechanism that is closer to common designer workflow.
Phase 6: Hooks and the Rabbit Hole
Add to the context object a method named ->invokeHook($hook_name, array $args = array(), $context = NULL).
Hooks will then be modified to accept a context object. If the invokeHook() method is called with a context object (mocked or otherwise), it will be passed to the specified hooks. If not, the context object will pass itself to the hook.
As hooks are converted to accept a context object, the drupal_get_context() function will be deprecated. Eventually it will be removed.
module_invoke_all() will eventually be deprecated and removed.
invokeHook() will also support mocking as appropriate.
Phase 7: Functionality access
Selected core systems will be given accessor methods on the context object, which will become the primary access to those systems. Examples of possible mechanisms include:
Phase 8: Profit
We have the biggest party in the history of open source development. With Ewoks dancing.