Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="toc"><ac:parameter ac:name="maxLevel">2</ac:parameter></ac:macro>
<p>This document serves as several proposals combined, and consists of the<br />
following:</p>

<ul>
<li>An overview of various issues and requirements</li>
<li>Requirements (as bullet points)</li>
<li>Proposed Interfaces</li>
<li>Use Cases</li>
</ul>

<p>The focus of this proposal is on the following:</p>

<ul>
<li>Basic interfaces for the ZF2 MVC</li>
<li>How these interfaces may interact</li>
</ul>

<p>It is <strong>not</strong> intended to discuss the following:</p>

<ul>
<li><em>Where</em> code will exist within the filesystem in projects</li>
<li>Final MVC <em>implementations</em>. We will create separate proposals for the default implementations once this proposal is accepted.</li>
</ul>

<p>In this proposal, we are specifically keeping the following themes from the ZF2 requirements in mind:</p>

<ul>
<li>General goals:
<ul>
<li>Easing the learning curve</li>
<li>Improving baseline performance</li>
<li>Make extending the framework (trivially) simple</li>
</ul>
</li>
<li>Developer goals:
<ul>
<li>Programming by contract</li>
<li>Simplification</li>
<li>Favoring of the explicit</li>
</ul>
</li>
</ul>

<p>The primary takeaways from this proposal (or set of proposals) should be that the ZF2 MVC:</p>

<ul>
<li><strong>must</strong> be built with both simplicity and extensibility in mind.</li>
<li><strong>should</strong> provide the basic building blocks for creating custom MVC layers; other systems utilizing those building blocks <strong>should</strong> retain compatibility with these systems and shipped implementations.</li>
<li><strong>will</strong> provide a simple, standard MVC solution (though details on that will come in later proposals).</li>
</ul>

<h2>Overview</h2>

<p>ZF's MVC has grown organically. While the basic, shipped MVC works for many, there are number of limitations that cause issues for those wanting to deviate from the standard workflow.</p>

<h3>Page Controllers</h3>

<p>In some cases, page controllers (vs. a front controller with many action controllers) are desired. In this paradigm, a separate end-point would be created for a particular controller (or multiple endpoints for multiple controllers), and it would then handle all requests to that end-point. (Page controllers were especially common pre-frameworks.)</p>

<p>Currently, ZF1 is not terribly friendly in this regard. While action controllers <em>can</em> be instantiated on their own, there are a number of soft dependencies on the front controller – e.g. redirects and URL generation typically require pulling the router from the front controller, layout rendering assumes a front controller plugin, etc. While many of these obstacles may be overcome, page controllers should be much easier to create than they currently are.</p>

<h3>No Action Controller Interface</h3>

<p>If you wish to attach a controller to the MVC, it <strong>must</strong> extend <code>Zend_Controller_Action</code>, as that's the type tested against, and provides the only blueprint or abstract implementation of a controller in the framework. This is problematic on many levels:</p>

<ul>
<li>If you do not want the various features of <code>Zend_Controller_Action</code> – action helper integration, <code>_forward()</code>, <code>_redirect()</code>, front controller parameter injection, etc. – there's no way to disable that functionality, as it's baked in. You cannot simply substitute an alternate implementation.</li>
<li>If you want to alter how action dispatching works, you must fight both the dispatcher and the action controller.
<ul>
<li>The dispatcher sends an "action" argument to the action controller's <code>dispatch()</code> method – but it's actually a method name; you have to pull the actual action name from the request object. This means, again, fighting the framework. This smells of poor interface definition.</li>
<li>A number of developers and organizations have requested the ability to pass arguments to action controllers. This is actually possible – but, again, not easily achievable due to the structure of the framework. In this case, the abstract controller is too limiting for those extending the framework, and smells of restrictive interface definition.</li>
<li>If you want to use a different convention than <code><action>Action()</code> for dispatchable methods, again, you must pull the action name from the request object. This makes it difficult to mix-in existing service objects and/or controllers from other MVC frameworks.</li>
</ul>
</li>
</ul>

<p>Basically, we need some sort of action controller interface that allows for both the default, defined use case, as well as alternate implementations to exist side-by-side.</p>

<h3>Poorly Composed Responsibilities</h3>

<p>Continuing on the subject of <code>Zend_Controller_Action</code>, the class currently has too many responsibilities: </p>

<ul>
<li>View composition, initialization, and rendering</li>
<li>Redirection</li>
<li>Forwarding additional controllers</li>
<li>Helper broker (this is actually not terrible, as it composes the helper broker; however, access to the broker is not ideal)</li>
<li>Composition of application parameters</li>
<li>Hook management and lifecycle</li>
<li>Action dispatch</li>
</ul>

<p>The base action controller interface should only define the minimum requirements for executing an action controller, and everything else should be either:</p>

<ul>
<li>Composed in (either via controller or setter injection)</li>
<li>Part of application logic</li>
</ul>

<p>At its basic level, an action controller is a Strategy or Command pattern – the front controller executes the action controller and returns a response. As such, anything outside that scope is likely suspect in terms of design.</p>

<h3>Services</h3>

<p>ZF1 created a number of server classes: AMF, JSON, XML-RPC, SOAP (we won't talk about REST). These all follow the PHP SoapServer API:</p>

<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
$server = new Server;
$server->setClass($classOrObject, $optionalNamespace); // optionally multiple times
$server->addFunction($functionName, $optionalNamespace); // optionally multiple times
$response = $server->handle();
echo $response;
]]></ac:plain-text-body></ac:macro>

<p>This API is simple and effective. We've discovered one or two limitations – it would be better to always require a request object to `handle()`, for instance, but overall, it works.</p>

<p>There are some cases, however, where the approach is limited:</p>

<ul>
<li>Request/Response objects in the services are limited in functionality; if you need to set or get additional headers, you're on your own.</li>
<li>Authentication and Authorization are items often automated in the MVC – which leads to writing extra code for service endpoints in order to make use of these.</li>
<li>No hooks. Having the ability to tie into things like pre/post-handle would be useful for automating things such as authentication/authorization. Currently, while you <em>can</em> program around these, there's no easily re-usable, pluggable way of doing so.</li>
<li>Inability to configure; setting classes/objects/functions, request and response classes, etc. all must be done manually, instead of via options and/or configuration classes.</li>
</ul>

<p>These often lead to developers wrapping services within the ZF MVC layer. This, however, leads to additional issues:</p>

<ul>
<li>Each server is basically an MVC in its own right. This leads to duplication of objects (request, response), duplication of logic (dispatching controller + dispatching service), and degradation of performance (Often 5-20x better performance is had by using a standalone service).</li>
<li>No ties/hooks into MVC; you end up needing to disable things like layouts, views, etc. manually.</li>
</ul>

<p>Making services capable of being configurable, first-class citizens of the MVC would simplify service creation and exposure within applications.</p>

<h3>Dispatching</h3>

<p><code>Zend_Controller_Dispatcher_Standard</code> is, frankly, an atrocity. While a dispatcher interface is defined, the front controller, the dispatcher itself, and several other classes all utilize methods that are not part of the interface. This makes it incredibly difficult to drop in alternate dispatch mechanisms.</p>

<p>Why would you want to do that?</p>

<ul>
<li>Micro-MVCs, or performance-optimized MVCs. Often, a business need may require performance optimizations that the default ZF MVC is simply incapable of providing.</li>
<li>Alternate controller strategies. If you don't want to follow the convention of <code><name>Controller::<name>Action()</code>, you have to replace both the dispatcher and action controller strategies – and modifying the dispatcher is difficult.
<ul>
<li>One use case for changing the strategy is RESTful controllers. It doesn't make much sense to have separate actions for each request method – a switch statement and an interface for resources could accomplish this much more quickly.</li>
<li>If you replace the dispatcher mechanism, this means you cannot re-use action controllers between projects, as one may assume the standard dispatcher, the other a custom dispatcher.</li>
</ul>
</li>
</ul>

<p>In order to be re-usable, the dispatcher interface <strong>must</strong> contain only the methods necessary for dispatching, and a way to configure itself.</p>

<h3>Too Many Hook Points</h3>

<p>Right now, we have three layers of hooks within the ZF MVC (from outside -> in): </p>

<ul>
<li>Front Controller plugins</li>
<li>Action Helper hooks</li>
<li>Action Controller hooks</li>
</ul>

<p>One common issue on the ZF mailing lists is when and where to register automated processes. There are four answers:</p>

<ul>
<li>Front Controller plugins, if they don't <em>need</em> to be aware of the action controller, and need to be run on every request.</li>
<li>Action Helper hooks, if they <em>may</em> need to be aware of the action controller, and likely need to be run on every request.</li>
<li>A base action controller extension class, in the init() or pre/postDispatch() hook methods, if action controller awareness <em>must</em> be present, and you want to selectively apply hooks to all or some controllers.</li>
<li>A specific action controller's hook methods, for ultimate specificity.</li>
</ul>

<p>This presents a number of problems:</p>

<ul>
<li>Education. Developers must learn the entire life cycle of the standard MVC in order to make good use of it.</li>
<li>Different mechanisms have different specificity. This can be a good thing, but it can also lead to edge cases quite quickly.</li>
<li>Each registers with the system in a different way.</li>
</ul>

<p>Hooks:</p>

<ul>
<li><strong>must</strong> be standardized:</li>
<li><strong>must</strong> allow opt-in behavior. One controller may require hooks, while another might not. An example: service end-points likely may not need view and layout automation, while normal controllers might.</li>
<li><strong>must</strong> be implemented in a way that they may be composed in. In other words, neither front nor action controllers should require methods for registering hooks, but rather utilize a collaborator to do so. This allows easy mocking, the ability to setup hooks via extension classes, or runtime configurability.</li>
</ul>

<h3>Usage outside ZF applications</h3>

<p>Related to page controllers, it's often useful to integrate Zend Framework within other systems, such as WordPress, Drupal, Joomla, TikiWiki, etc. These systems often provide hook systems that allow you to execute code on-demand. As many ZF applications may utilize a full variety of MVC features, including views, request and response headers, etc., embedding and wrapping the ZF MVC within hooks can be an expedient way to accomplish this task. However, this tends to:</p>

<ul>
<li>introduce negative performance impact</li>
<li>introduce complicated configuration and setup</li>
</ul>

<p>Wrapping ZF MVC functionality <strong>should</strong> be easy to accomplish.</p>

<h3>Alternate View Strategies</h3>

<p>Currently, ZF only offers a single view strategy, based on using PHP as a template language. Alternate view strategies may be utilized by implementing <code>Zend_View_Interface</code>, but, as with the dispatcher, many methods assume and call more functionality than the view interface defines.</p>

<p>Additionally, some strategies are not easily supported:</p>

<ul>
<li>"Code-Behind". In this paradigm, individual template pages are called by the front controller, and the templates "wire in" code that will handle specific events, usually through tags:
<ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
<% Page CodeFile="Application/Controller/Service.php"
inherits="Application\Controller\Service" %>
]]></ac:plain-text-body></ac:macro>
This would load the appropriate file, inherit code, and execute it. Currently, this sort of strategy is impossible without introducing an entirely new MVC layer.</li>
<li>Hierarchical MVC. HMVC's primary strength is that it allows for creation of complex content by executing many different MVC triads. As an example, a call to one controller may initiate calls to two additional controllers, each of which may also execute additional controllers; the results will then be aggregated and passed to a view. This is different than "forwarding" in the current system, as the entire request/response cycle of a given controller happens <em>within</em> the body of a single action. This allows manipulating the response to determine what is relevant to the current request.</li>
<li>Strict segregation of templates from development. The default MVC keeps all view scripts under the same tree as the given module. This means that it's more difficult to segregate access easily for designers. Often, it would be easier to put all view scripts for the entire application under a single directory, and give designers access only to that directory. While possible with the view renderer, it's non-trivial.
<ul>
<li>Additionally, using PHP as the templating language is not optimal for many designers; the ability to utilize a standard template engine would often be easier for purposes of training.</li>
</ul>
</li>
<li>Transform views are not supported currently. Transform views are an alternative to Two Step Views, and often simpler for designers to utilize.</li>
</ul>

<h3>Configuration and Injection of Resources</h3>

<p>How do resources make their way into controllers, views, etc.? This question was largely answered in ZF1 with the introduction of <code>Zend_Application</code>. However, this integration is incomplete.</p>

<ul>
<li>The bootstrap is injected into the front controller singleton</li>
<li>The front controller injects the bootstrap into controllers</li>
<li>All other classes retrieve the bootstrap from the front controller singleton</li>
</ul>

<p>Two solutions present themselves:</p>

<ul>
<li>A dependency injection container</li>
<li>A service locator</li>
</ul>

<p>Either solution is possible with the current implementation of <code>Zend_Application_Bootstrap</code>; it does not specify what type of "container" is used internally.</p>

<p>The question is fundamentally this, however: how should controllers, models, and views get their dependencies/resources? Should we expect the front controller/dispatcher to inject them, or to pass a service locator to them so that they may pull them themselves?</p>

<p>Alternately, should the ZF MVC interfaces care?</p>

<h3>Performance</h3>

<p>ZF's MVC performance has (mostly) steadily declined since the 1.0 release (with the exception of the 1.7 release, when a performance audit was performed). In large part, this is due to additional functionality:</p>

<ul>
<li>Adding layouts added another view rendering phase to the typical request</li>
<li>Adding Zend_Application and the bootstrap lifecycle</li>
</ul>

<p>In performance profiling, the ZF team has discovered that the primary source of the bottlenecks is in the !PluginLoader implementation, which (a) has many stat calls, (b) doesn't cache between instances, and (c) differs between components. We have done a large amount of work during the Autoloading & Plugin Loading milestone to correct this situation, but are looking for additional areas we can improve performance in the MVC layer.</p>

<h3>Modules</h3>

<p>The current approach to modules in ZF is very much "tacked on". Due to how they work, it's quite difficult to simply "drop in" third-party code and expect it to work. While this is somewhat better since the introduction of Zend_Application, the requirement to execute a bootstrap for each module that requires autoloading leads to additional performance degradation.</p>

<p>Modules <strong>should</strong> be considered "first-class citizens" of Zend Framework.</p>

<h2>Requirements</h2>

<ul>
<li>Basics
<ul>
<li>The basic building blocks of the MVC <strong>must</strong> include standard Request and Response interfaces.
<ul>
<li>The default implementation <strong>must</strong> provide HTTP-aware implementations of the Request and Response interfaces.</li>
</ul>
</li>
<li>The basic responsibility of the MVC and Server classes <strong>must</strong> be to convert a Request object into a Response object.</li>
<li>The primary goals are to:
<ul>
<li>Create interfaces and loosely coupled objects for creating MVC implementations; and</li>
<li>Create one or more default implementations.</li>
</ul>
</li>
</ul>
</li>
<li>Request object
<ul>
<li><strong>Must</strong> handle both request <em>metadata</em> and optionally <em>content</em>.</li>
<li><strong>Must</strong> provide an HTTP-aware Request implementation:
<ul>
<li>Aware of Request-Method</li>
<li>Aware of Superglobals ($_GET, $_POST, $_SERVER, $_ENV, $_FILES, cookies, sessions, etc.)</li>
<li>Aware of HTTP request headers</li>
<li><strong>Should</strong> be aware enough of Content-Type and Request-Method to be able to auto-populate $_PUT and $_POST from the raw request body for any format supported by Zend\Serializer.</li>
</ul>
</li>
</ul>
</li>
<li>Response object
<ul>
<li><strong>Must</strong> handle both response content an optionally <em>metadata</em>.</li>
<li><strong>Must</strong> provide an HTTP-aware Response implementation:
<ul>
<li><strong>Must</strong> be capable of aggregating and emitting HTTP response headers and status codes.</li>
<li><strong>Should</strong> be capable of manipulating Cache-Control headers</li>
<li><strong>Should</strong> be capable of aggregating and emitting cookies</li>
<li><strong>Might</strong> be "View-aware", such that getting a View Model or View Renderer as the content would auto-render the view when the response is emitted.</li>
</ul>
</li>
</ul>
</li>
<li>Headers
<ul>
<li><strong>Must</strong> provide interfaces and implementations for representing invidivual HTTP headers as well as collections of headers</li>
</ul>
</li>
<li>Cookies
<ul>
<li><strong>Should</strong> provide interfaces and implementations for representing cookies and collections of cookies</li>
</ul>
</li>
<li>Parameters
<ul>
<li><strong>Must</strong> provide interfaces and implementations for representing metadata objects (including the superglobals).</li>
<li>The Parameters interface <strong>must</strong> allow for array access of metadata.</li>
</ul>
</li>
<li>Page Controllers:
<ul>
<li><strong>Must</strong> be able to create page controllers with ZF.</li>
<li><strong>Must</strong> be able to utilize page controllers outside the front controller paradigm.</li>
<li>URL generation <strong>should</strong> be de-coupled from the front controller</li>
<li>Controller "redirection" and/or "forwarding" <strong>must</strong> be de-coupled from the front controller</li>
<li><strong>Must</strong> be able to configure services/resources and inject easily, preferably via the constructor.</li>
<li>Ideally, Action Controllers <strong>should</strong> be re-usable as standalone Page controllers.</li>
</ul>
</li>
<li>Action Controllers
<ul>
<li>ZF2 <strong>must</strong> offer a simple, strong interface for action controllers.
<ul>
<li>The action controller interface <strong>must not</strong> require front controller interaction.</li>
<li>Action controller dependencies <strong>must</strong> be injectable, either through explicit setters or the constructor; the interface should not dictate anything that would prevent this.</li>
<li>The action controller interface <strong>must</strong> specify a single method for dispatching itself.</li>
</ul>
</li>
<li>Action controllers <strong>must</strong> allow the flexibility to re-define the conventions for resolving actions.
<ul>
<li>ZF2 <strong>must</strong> offer a default implementation with the recommended convention</li>
</ul>
</li>
<li>Action controllers <strong>must</strong> allow the flexibility to determine whether or not action methods will expect arguments.</li>
<li>Action controllers <strong>should</strong> <strong>optionally</strong> allow lifecycle management, preferably via signal slots and/or action helpers.
<ul>
<li>The default implementation <strong>should</strong> allow configuring lifecycle management both statically and via constructor injection.</li>
</ul>
</li>
</ul>
</li>
<li>Server classes
<ul>
<li><strong>Must</strong> implement the same basic interface as action controllers, in order to allow them to be composed as part of an MVC application.</li>
<li><strong>Must</strong> utilize the same base Request and Response interfaces, and <strong>should</strong> decorate these in order to allow de/serialization of the protocol format.</li>
<li><strong>Should</strong> provide lifecycle hooks, likely in the form of !SignalSlots.
<ul>
<li>Hooks <strong>should</strong> be injectable/configurable</li>
<li>Hooks <strong>should</strong> allow static configuration as well as per-instance.</li>
<li>Hooks for servers <strong>should not</strong> utilize the same signals as action controllers, to help prevent things such as View/Layout injection.</li>
</ul>
</li>
</ul>
</li>
<li>Router
<ul>
<li>The basic "Route" interface <strong>must</strong>:
<ul>
<li>Provide the ability to match a Request object to metadata tokens.</li>
<li>Provide the ability to assemble a URL/Request based on metadata.</li>
</ul>
</li>
<li>A new "RouteStack" interface <strong>should</strong> be developed, allowing the ability to provide a stack of routes to test for matches.</li>
<li>Routing <strong>could</strong> write matched metadata to the Request object metadata.
<ul>
<li>This is under debate. However, doing so would simplify most workflows for the MVC. The main question is whether this should overwrite such metadata, or whether the RouteStack implementation would return a "RouteResult" object that it would then register as a single metadatum of the result.</li>
</ul>
</li>
</ul>
</li>
<li>Dispatcher
<ul>
<li><strong>Must</strong> follow the same basic interface as action controllers and server implementations.</li>
<li>The dispatcher <strong>must</strong> be responsible for mapping a controller name to a class that will handle it, and <strong>optionally</strong> injecting configuration.</li>
<li>The dispatcher <strong>may</strong> be omitted, depending on the MVC created.</li>
<li><strong>Should</strong> return a response as soon as it has one.</li>
</ul>
</li>
<li>Front Controller
<ul>
<li><strong>Must not</strong> implement a singleton pattern.</li>
<li><strong>Should</strong> be responsible simply for:
<ul>
<li>Router management</li>
<li>Dispatcher management</li>
<li>Dispatching the request</li>
</ul>
</li>
<li>Application Configuration <strong>should</strong> be injected into the child objects.</li>
<li><strong>Should</strong> provide lifecycle hooks around the router and dispatcher.</li>
<li><strong>Should</strong> return a response as soon as it has one.</li>
</ul>
</li>
<li>Lifecycle hooks
<ul>
<li>All areas where hooks are used <strong>must</strong> use the same mechanism</li>
<li>In most cases, static configuration as well as per-instance configuration <strong>should</strong> be supported.</li>
</ul>
</li>
<li>Application configuration
<ul>
<li>Application-wide (or commonly used) resources <strong>must</strong> be configurable</li>
<li>Dispatchable objects (controllers, dispatchers, servers, etc.) <strong>should</strong> accept application configuration, and "wire themselves" based on that configuration.</li>
<li>Configuration management <strong>must</strong> handle dependencies.</li>
<li>Configuration management <strong>must</strong> lazy load when requested (i.e., it should not preload all resources)</li>
</ul>
</li>
<li>Performance
<ul>
<li>Performance of the MVC layer must be as fast as possible, while still supporting required features such as layouts, lifecycle management, and application configuration.</li>
</ul>
</li>
<li>Modules
<ul>
<li>Design of any shipped MVC implementations <strong>must</strong> encourage module re-use, and <strong>must</strong> support modules as basic MVC infrastructure.</li>
</ul>
</li>
</ul>

<h2>Proposed Interfaces</h2>

<p>In examining the requirements, a standard interface for page/action controllers, dispatchers, service providers, and front controllers emerged, essentially a Command pattern:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface Dispatchable
{
/**

  • Dispatch a request
    *
  • Typically, if something dispatchable returns a Response object, that
  • Response will be used. Systems could be built, however, that introspect
  • the return value in order to determine what type of Response to build.
  • On the other end of the spectrum, we could pass a Response object, and
  • expect the code to interact with and populate it.
    *
  • @return mixed
    */
    public function dispatch(Request $request, Response $response = null);
    }
    ]]></ac:plain-text-body></ac:macro>

<p>Basically, by making each of these components "dispatchable", we get a number of advantages:</p>

<ul>
<li>Controllers may be used either standalone, or within a larger application context (cronjobs, message handlers that consume controllers, etc.). This provides the ability to create standalone page controllers,</li>
<li>Server classes may be configured and attached to a front controller. In this case, they can become first-class citizens of the MVC, instead of something tacked on or wrapped.</li>
<li>A front controller implementation need only offer the ability to broker controllers to dispatch. This leads to the ability to slipstream custom implementations that are tuned for specific needs.</li>
</ul>

<p>To this end, we propose the following interfaces:</p>

<ul>
<li>Message interface (metadata and body content)</li>
<li>Request interface (extends Message)</li>
<li>Response interface (extends Message, provides a way to emit itself)</li>
<li>Dispatchable interface (takes a Request object, and optimally returns a Response object)</li>
<li>Route interface (takes a request, and decomposes it into a Result)</li>
</ul>

<p>The basic use case then becomes:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$dispatchable = new DispatchableObject();
$response = $dispatchable->dispatch(new Request, Response $response = null);
$response->emit();
]]></ac:plain-text-body></ac:macro>

<h3>Interfaces</h3>

<p>Interface definitions have been written and committed to the following repository:</p>

<ul>
<li><a class="external-link" href="https://github.com/weierophinney/fig-library">https://github.com/weierophinney/fig-library</a></li>
</ul>

<p>with concrete implementations in the "Zend" tree of the "feature/zf-implementation" branch. Below are the basic definitions.</p>

<ul>
<li>Message
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface Message
{
public function setMetadata($spec, $value = null);
public function getMetadata($key = null);
public function setContent($content);
public function getContent();
}
]]></ac:plain-text-body></ac:macro></li>
<li>Request
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface Request extends Message { }
]]></ac:plain-text-body></ac:macro></li>
<li>Response
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface Response extends Message
{
public function emit();
}
]]></ac:plain-text-body></ac:macro></li>
<li>Dispatchable
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface Dispatchable
{
/**

  • Ideally, returns a Response object.
    */
    public function dispatch(Request $request);
    }
    ]]></ac:plain-text-body></ac:macro></li>
    </ul>

<p>Additionally, HTTP-aware versions of the Request and Response interfaces are proposed:</p>

<ul>
<li>HttpRequest
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Fig\Http;

use Fig\Request;

interface HttpRequest extends Request
{
/* accessors for various superglobals */
public function query($name = null, $default = null);
public function post($name = null, $default = null);
public function cookie($name = null, $default = null);
public function file($name = null);
public function server($name = null, $default = null);
public function env($name = null, $default = null);
public function headers($name = null);

/* URI decomposition */
public function getRequestUri(); // Return a URI instance

/* script name */
public function getScriptName();

/* Capabilities
*

  • These may be pushed into an additional interface.
    */
    public function getMethod();
    public function getETags();
    public function getPreferredLanguage(array $locales = null);
    public function getLanguages();
    public function getCharsets();
    public function getAcceptableContentTypes();
    public function isNoCache();
    public function isFlashRequest();
    public function isSecure();
    public function isXmlHttpRequest();

/* potential method tests */
public function isDelete();
public function isGet();
public function isHead();
public function isOptions();
public function isPost();
public function isPut();
}
]]></ac:plain-text-body></ac:macro></li>
<li>ModifiableHttpRequest
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Fig\Http;

use Fig\Parameters;

interface ModifiableHttpRequest extends HttpRequest
{
/* mutators for various superglobals */
public function setQuery(Parameters $query);
public function setPost(Parameters $post);
public function setCookies(Parameters $cookies);
public function setFiles(Parameters $files); // Maybe separate component for Files?
public function setServer(Parameters $server);
public function setEnv(Parameters $env);
public function setHeaders(HttpRequestHeaders $headers);
public function setRawBody($string);

/* Capabilities
*

  • These may be pushed into an additional interface.
    */
    public function setMethod($method);

/* creational capabilities */
public static function create(Uri $uri, $method = 'get' /** .. more args */);
}
]]></ac:plain-text-body></ac:macro></li>
<li>Uri
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Zend\Uri;

class Uri
{
public function getScheme();
public function getHost();
public function getPort();
public function getPath();
public function getQueryString();
public function getBaseUrl();

public function setScheme($scheme);
public function setHost($host);
public function setPort($port);
public function setPath($path);
public function setQueryString($stringOrArray);
public function setBaseUrl(Uri $uri);

public function isValid();
public function __toString();
}
]]></ac:plain-text-body></ac:macro></li>
<li>HttpResponse
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Fig\Http;

use Fig\Response;

interface HttpResponse extends Response
{
public function __construct($content = '', $status = 200, $headers = null);

public function sendHeaders();
public function sendContent();
// public function emit(); // send both headers and content; defined in Fig\Response

/* mutators and accessors */
public function getHeaders();
public function setHeaders(HttpResponseHeaders $headers);
}
]]></ac:plain-text-body></ac:macro></li>
<li>HttpHeaders
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Fig\Http;

use Iterator,
ArrayAccess,
Countable;

/*

  • In most cases, extend SplQueue, and then override where necessary
    */
    interface HttpHeaders extends Iterator, ArrayAccess, Countable
    {
    /*
  • General mutators and accessors
    *
  • These are items that are technically part of the response headers, but
  • not individual headers themselves.
    */
    public function getProtocolVersion(); // HTTP 1.0, 1.1
    public function setProtocolVersion($version);

/**

  • Adding headers
    *
  • Also: requires overriding push, unshift to ensure values are of correct
  • type.
    *
  • Typically, $header will be of type HttpHeader, but this allows addHeader()
  • to operate as a factory. Suggestion is to allow HttpHeader objects, arrays,
  • or all 3 arguments.
    *
  • @param string|array|HttpHeader $header
  • @param null|string $content
  • @param bool $replace
  • @return HttpHeaders
    */
    public function addHeader($header, $content = null, $replace = false);

/**

  • Allow adding multiple headers at once
    *
  • Implementation can vary – could be key/value pairs, array of HttpHeader
  • objects, array of arrays, etc – or combination thereof.
    *
  • @param mixed $headers
  • @return HttpHeaders
    */
    public function addHeaders($headers);

/*

  • Retrieve named header; returns either false or a queue of headers.
  • has() tests for headers
    */
    public function get($type);
    public function has($type);

/**

  • Representation of headers as string
    */
    public function __toString();

/**

  • Populate object and headers from string
    *
  • Accepts text representing all headers, and splits it into either a
  • status or request line and all provided headers.
  • @param string $string
  • @return HttpHeaders
    */
    public function fromString($string);
    }
    ]]></ac:plain-text-body></ac:macro></li>
    <li>HttpHeader
    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    namespace Fig\Http;

interface HttpHeader
{
public function __construct($header, $value = null, $replace = false);

/* mutators */
public function setType($type);
public function setValue($value);
public function replace($flag = null); // also acts as mutator

/* accessors */
public function getType();
public function getValue();

/* behaviors */
public function send();
public function __toString();
}
]]></ac:plain-text-body></ac:macro></li>
<li>HttpRequestHeaders
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Fig\Http;

interface HttpRequestHeaders extends HttpHeaders
{
/* Request-Line-related accessors/mutators */
public function getMethod(); // GET, POST, PUT, etc.
public function getUri();
public function setMethod($method); // GET, POST, PUT, etc.
public function setUri($uri);
public function renderRequestLine(); // render the "request line" portion of the header
}
]]></ac:plain-text-body></ac:macro></li>
<li>HttpResponseHeaders
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Fig\Http;

/**

  • Represents the HTTP Response headers
    *
  • Describes a collection of response headers. As such, it also ensures a status
  • code and optional status message.
    */
    interface HttpResponseHeaders extends HttpHeaders
    {
    public function getStatus(); // Returns HttpStatus instance

/* Sending headers, and testing sent status */
public function send(); // actually send headers
public function sent(); // return boolean headers sent status

/* Specialized response header(s) */
public function setRedirect($url, $code = 302);

/* Methods occurring below here need to be discussed */

/* Potential specialized mutators * /
public function expire();
public function setClientTtl($seconds);
public function setEtag($etag = null, $weak = false);
public function setExpires($date = null);
public function setLastModified($date = null);
public function setMaxAge($value);
public function setNotModified();
public function setPrivate($value);
public function setSharedMaxAge($value);
public function setTtl($seconds);
public function setVary($headers, $replace = true);

/* Potential specialized conditionals * /
public function hasVary(); // Vary header present? (should )
public function isCacheable();
public function isFresh();
public function isNotModified(HttpRequest $request);
public function isValidateable();
public function mustRevalidate();

/* Potential specialized accessors * /
public function getAge() ;
public function getEtag();
public function getExpires();
public function getLastModified();
public function getMaxAge();
public function getTtl();
public function getVary();
*/
}
]]></ac:plain-text-body></ac:macro></li>
<li>HttpStatus
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Fig\Http;

interface HttpStatus
{
public function getStatusCode(); // 200, 301, etc.
public function getStatusMessage();
public function setStatusCode($code, $text = null);

/* Testing header status */
public function isRedirect(); // 3XX status and/or Location header?
public function isClientError(); // 4XX status?
public function isEmpty(); // 201, 204, or 304 status?
public function isForbidden(); // 403 status?
public function isInformational(); // 1XX status?
public function isInvalid(); // <100 or >= 600 status?
public function isNotFound(); // 404 status?
public function isOk(); // 200 status?
public function isServerError(); // 5XX status?
public function isSuccessful(); // 2XX status?
}
]]></ac:plain-text-body></ac:macro></li>
<li>Parameters
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace Fig;

use ArrayAccess,
Countable,
Serializable,
Traversable;

/*

  • Basically, an ArrayObject. You could simply define something like:
  • class QueryParams extends ArrayObject implements Parameters {}
  • and have 90% of the functionality
    */
    interface Parameters extends ArrayAccess, Countable, Serializable, Traversable
    {
    public function __construct(array $values = null);

/* Allow deserialization from standard array */
public function fromArray(array $values);

/* Allow deserialization from raw body; e.g., for PUT requests */
public function fromString($string);

/* Allow serialization back to standard array */
public function toArray();

/* Allow serialization to query format; e.g., for PUT or POST requests */
public function toString();
}
]]></ac:plain-text-body></ac:macro></li>
</ul>

<p>Routing is an aspect of the MVC that would be affected by these interfaces. The following interfaces would be used within the routing subcomponent:</p>

<ul>
<li>Route
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface Route
{
public function setRequest(Request $request);
public function match(Request $request);
public function assemble($name, array $params = array(), array $options = array());
}
]]></ac:plain-text-body></ac:macro></li>
<li>RouteStack
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
/**

  • An implementation would likely extend SplStack and/or Iterator + ArrayAccess
    */
    interface RouteStack extends Route
    {
    public function setRequest(Request $request);
    public function addRoute(Route $route);
    public function addRoutes($routes);
    public function getCurrentRoute();
    }
    ]]></ac:plain-text-body></ac:macro></li>
    </ul>

<h2>Use Cases</h2>

<p>The following are examples covering a variety of use cases. </p>

<p>We are discussing currently what exact form <code>$options</code> will take. It <em>will</em> be a service locator of some sort; we are split on whether it will also be a dependency injection container (even if we don't, we will likely make the definition flexible enough to allow using one). For now, focus on the use cases surrounding the interfaces.</p>

<h3>Page Controller</h3>

<p>One often requested feature is the ability to use action controllers as standalone page controllers. As the <code>Dispatchable</code> interface simply requires that a <code>Request</code> object be passed, this becomes a trivial use case.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$request = new Request();
$request->setMetadata('action', 'foo');

$controller = new SomePageController($options);
$response = $controller->dispatch($request);
$response->emit();
]]></ac:plain-text-body></ac:macro>

<h3>Front Controller</h3>

<p>The Zend Framework 1.0 front controller is actually relatively simple. It's <code>dispatch()</code> method currently simply routes and then dispatches the request. That paradigm could be used again to provide a simple ZF1 emulation layer:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$controller = new FrontController($options);
$response = $controller->dispatch(new Request);
$reponse->emit();
]]></ac:plain-text-body></ac:macro>

<p>In the above, the assumption is that <code>$options</code> would contain dependencies – such as a dispatcher, router, etc – and that these would be configured as well. The dispatcher would look like this:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$dispatcher = new Dispatcher($options);
return $dispatcher->dispatch($request);
]]></ac:plain-text-body></ac:macro>

<p>In other words, it, too, would implement the <code>Dispatchable</code> interface, and be responsible for determining what <code>Dispatchable</code> objects attached to it should be invoked.</p>

<p>This leaves great flexibility of implementation – anything <code>Dispatchable</code> can be used as an action controller.</p>

<p>Internally, plugins would be swapped out in favor of <code>SignalSlots</code>, giving some additional flexibility of implementation, as well as simplifying the front controller implementation.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$controller->signals()->connect('router.start', new RouteInitializer())
->connect('router.stop', new ModuleInitializer())
->connect('dispatch.start', new LayoutSwitcher());
]]></ac:plain-text-body></ac:macro>

<p>Basically, the front controller would simply become a Facade for managing the workflow of other objects.</p>

<h3>Server Classes</h3>

<p>ZF currently provides a number of Server classes: <code>Zend_Json_Server</code>, <code>Zend_XmlRpc_Server</code>, <code>Zend_Amf_Server</code>, etc. These would all follow the same interface:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$server = new Server($options);
$response = $server->dispatch($request);
$response->emit();
]]></ac:plain-text-body></ac:macro>

<p>These would likely use either extensions to the base <code>Request</code>/<code>Response</code> classes provided, or <em>decorate</em> them in order to provide de/serialization of the protocol for which they are responsible. (Decoration offers the most flexibility for differing use cases.)</p>

<p>Because they implement <code>Dispatchable</code>, they could be mixed in as just another dispatchable within a Front Controller implementation, or executed in standalone endpoints:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$server = new SoapServer($options);
$server->setClass($someClass, $namespace);

// Tell the dispatcher that the "soap" controller refers to the $server instance
$mvc->getDispatcher()->register('soap', $server);
]]></ac:plain-text-body></ac:macro>

<h3>Micro or Custom MVC stacks</h3>

<p>When dealing with either small sites, or large sites with specific performance requirements, rolling a custom MVC stack is often useful. The current ZF1 implementation does not make this easy. The proposed interfaces <em>do</em>, however. Consider the following, callback-oriented dispatcher implementation:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
use Zend\Stdlib\Dispatchable,
Zend\Http\Response as HttpResponse,
Fig\Request,
Fig\Response;

class Dispatcher extends Dispatchable
{
protected $controllers;

public function attach($spec, $callback = null)
{
if (is_array($spec) || $spec instanceof \Traversable) {
foreach ($spec as $controller => $callback)

Unknown macro: { $this->attach($controller, $callback); }

return $this;
}

if (!is_scalar($spec))

Unknown macro: { throw new InvalidArgumentException('Spec must be scalar or traversable'); }

if (!is_callable($callback))

Unknown macro: { throw new InvalidArgumentException('Callback must be callable'); }

$this->controllers[$spec] = $callback;
return $this;
}

/**

  • Dispatch a request
  • @param Request $request
  • @return Response
    */
    public function dispatch(Request $request)
    {
    if (!$controller = $request->getMetadata('controller'))
    Unknown macro: { return new PageNotFoundResponse( '<h1>Page not found</h1>' ); }

if (!array_key_exists($controller, $this->controllers))

Unknown macro: { return new PageNotFoundResponse('<h1>Page not found</h1>'); }

$handler = $this->controllers[$controller];
$response = $handler($request);

if (is_string($response))

Unknown macro: { return new HttpResponse($response); }

if (!is_object($response))

Unknown macro: { return new ApplicationErrorResponse('<h1>An error occurred</h1>'); }

if (!$response instanceof Response) {
if (!method_exists($response, '__toString'))

Unknown macro: { return new ApplicationErrorResponse('<h1>An error occurred</h1>'); }

return new HttpResponse($response->__toString());
}
return $response;
}
}
]]></ac:plain-text-body></ac:macro>

<p>Don't worry about the various objects referenced; the main thing to understand is that it's using those same MVC building blocks: <code>Request</code>, <code>Response</code>, <code>Dispatchable</code>. In action, it looks like this:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
use Zend\Controller\Router,
Zend\Http\Request;

$request = new Request;

$router = new Router;
/*

  • Configure some routes here. We'll assume we've somehow configured routes
  • mapping the following controllers:
  • - homepage
  • - foo
  • - rest
  • - foobar
    */
    $router->route($request);

$dispatcher = new Dispatcher();
$dispatcher
->attach('homepage', function($request) {
// Simply returning a string:
return '<h1>Welcome</h1> <p>Welcometo our site!</p>';
})
->attach('foo', function($request) {
// Simply returning a string:
return '<h1>Foo!</h1>';
})
->attach('rest', function($request) {
// Example of a "REST" service...
switch ($request->getMethod()) {
case 'GET':
if (!$id = $request->query('id', false))

Unknown macro: { // We have a "list operation"... // Assume we somehow grab the list and create a response return $response; }

// We have an ID – fetch it and return the page
break;
case 'POST':
// Create document and return a response
break;
case 'PUT':
if (!$id = $request->query('id', false))

Unknown macro: { // No ID in the query string means no document! // Return a failure response }

// We have an ID – fetch and update from PUT params, and
// return a response
break;
case 'DELETE':
if (!$id = $request->query('id', false))

// We have an ID – delete, and // return a response
break;
default:
return new ApplicationErrorResponse('Unknown Method');
break;
}
})
->attach('foobar', function($request) {
// Curry in controllers to allow them to be lazy-loaded, and to ensure we
// get a response object back (Dispatcher will take care of that).
$controller = new FooBarController();
return $controller->dispatch($request);
});

$response = $dispatcher->dispatch($request);
$response->emit();
]]></ac:plain-text-body></ac:macro>

<p>The same MVC building blocks are combined in this example to produce something that looks quite different from the ZF1 MVC – yet can pick and choose pieces from the ZF2 MVC and interact with them in a standard way.</p>

<h3>Using Traits instead of inheritance</h3>

<p>Due to the simple nature of the Dispatchable interface, it lends itself well to PHP 5.next's traits features. As an example, a "REST" trait could be mixed in to a class quite easily to create a REST controller.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
trait RestDispatcher
{
public function dispatch(Request $request, Response $response = null)
{
if (!$request instanceof HttpRequest)

Unknown macro: { throw new DomainException('REST expects HTTP requests'); }

$resource = $this->getResource();

switch (strtoupper($request->getMethod()))

Unknown macro: { case 'GET'}

return $response;
}
}

// In a controller file:
namespace Blog\Controller;

class Entry implements Dispatchable
{
use RestDispatcher;

public function getResource()

Unknown macro: { return new BlogModelEntryResource; }

}
]]></ac:plain-text-body></ac:macro>

<p>While ZF2 will target PHP 5.3, we can also potentially provide ready-made traits for those using PHP 5.next. This will help simplify development for end users.</p>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Dec 22, 2010

    <p>Some quick comments on the subject matter that are not explicitly stated but should be:</p>
    <ol>
    <li>Configuration and Injection of Resources
    <ul>
    <li>I would go with a Service Locator as it would allow the most flexibility in terms of the concrete implementation to fetch what it may or may not need without the need to largely inject what is needed. Since the configuration and injection of these resources should likely be consistent AND expected in terms of development, it would make sense to utilize the Service Locator as it would be a dependency at this point in time thus enforcing it would be available for folks that are adding on in the future with little additional work which would also keep performance slightly faster when looking up several different items.</li>
    </ul>
    </li>
    <li>Request and Response objects must not be completely tied to HTTP requests.
    <ul>
    <li>CLI is a perfect example of this where $_SERVER is for HTTP as $_ENV is mainly for CLI</li>
    <li>The implementations of Request and Response is largely different depending on the implementation throughout the current ZF 1 and should largely be tied to Request objects that can be of any type that implement that interface.</li>
    <li>Some good examples of this are thing such as implementing a Gearman Wrapper - you want to display jobs based off of the CLI and be able to inject them that way. This is also where a Page Controller would help explicitly.</li>
    </ul>
    </li>
    </ol>

    1. Dec 28, 2010

      <ol>
      <li>I'm definitely leaning towards a Service Locator that implements lazy loading. As you note, it would reduce the amount of overhead necessary during application initialization to only what is needed for that particular controller. (Symfony 2 even invokes this same paradigm, passing the service pull the dependencies they need.) locator – which contains the DI container – to controllers so that they may</li>
      <li>There is no intention to tie the Request and Response objects to HTTP. If you look at the interfaces, the most basic versions are not HTTP aware, and HTTP awareness is provided by implementing specific interfaces. The idea is that for controllers you wish to re-use in different contexts, you would program towards the most compatible interface.</li>
      </ol>

      <p>Thanks for your input!</p>

  2. Dec 22, 2010

    <p>I'm not sure if we can discuss all the interfaces without discussing the behavior, having said that...</p>

    <blockquote><p>The basic building blocks of the MVC must include standard Request and Response interfaces.</p></blockquote>

    <p>Is by that meant interfaces that are shared/used across several frameworks (ZF, symfony, etc)?</p>

    <p>What do you understand by both Action Controllers and Page Controllers, what purpose do they suite, and how do they differ?</p>

    <blockquote><p>Application-wide (or commonly used) resources must be configurable</p></blockquote>

    <p>Imho, once done initializing (bootstrapping) we shouldn't allow for further configuration.</p>

    <p>I skimmed over the page quickly, so I'm sorry if I missed something (will look at it more closely later) <ac:emoticon ac:name="wink" /></p>

    1. Dec 28, 2010

      <blockquote><p>Is by that meant interfaces that are shared/used across several frameworks (ZF, symfony, etc)?</p></blockquote>

      <p>Potentially. Fabien Potencier, lead for the Symfony project, discussed this idea with me at ZendCon, and I began work on defining shared interfaces. However, we have not at this point solidified any plans in this direction. </p>

      <p>The point of the wording is that we will explicitly define interfaces for Request and Response objects, and that these will be used across the framework – both in the MVC (where they may be used for both HTTP and CLI requests), as well as in server components (which are a form of MVC, with the presentation format strictly defined) and service clients (HTTP client, REST client, etc.).</p>

      <blockquote><p>"Application-wide (or commonly used) resources must be configurable." Imho, once done initializing (bootstrapping) we shouldn't allow for further configuration.</p></blockquote>

      <p>I wasn't trying to imply differently.</p>

      1. Jan 18, 2011

        <p>With time closing in on finalizing the proposal, has any additional effort been given to coming up with cross library interfaces? I think both communities have some outstanding people working with/for them, so any ability to leverage more resources has the potential to benefit all involved.</p>

        <p>That said, I completely understand that getting two projects coordinated is an incredibly difficult tasks since both will be on different release schedules and may have licensing/ownership issues, etc.</p>

  3. Dec 22, 2010

    <p>On last example I see you are using a request object of Zend\Http component.<br />
    1. Will Zend\Http\Request be similar to Zend_Http_Client of zf1 now also used on mvc ?<br />
    2. If yes, we'll have to fill the request object with current data (get, post & friend)<br />
    or how to create an own request with own data to send an own request to a remote server ?</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    // dispatch + emit
    $request = Zend\Http\Request::createFromSapi();
    $response = $dispatcher->dispatch($request);
    $response->emit();

    // send + emit
    $request = new Zend\Http\Request();
    $request->setUri('http://www.google.de');
    $response = $request->send();
    $response->emit();
    ]]></ac:plain-text-body></ac:macro>

    1. Dec 28, 2010

      <ol>
      <li>Zend\Http\Request would be an HTTP-specific Request implementation – and would be used in both the MVC, as well as Zend\Http\Client.</li>
      <li>The request would not have an emit() method – the client would.</li>
      </ol>

  4. Dec 22, 2010

    <p>At a glance there seems to be something smelly going on in the last code sample. </p>

    <p>There seems to be no relation between the Router and Dispatcher. </p>

    <p>I guess the Router is mutating the Request object somehow, and I don't particularly like that idea. As it means the Request has to go via a Router first, or some some magic has to alter the Request first before being dispatched.</p>

    <p>Either passing the Router to the dispatcher, or the dispatcher to the router, and making the request immutable would be more preferable maybe (requires more thought)</p>

    1. Dec 23, 2010

      <p>As written, we still have to discuss whether the router should manipulate the request object or not. I personally feel better to return the RouteResult object, as I dislike to have the router manipulate the request, but this could be a problem with an event driven dispatcher implementation.</p>

      1. Dec 23, 2010

        <p>Another option would be for the router to decorate the original request with say a DispatchableRequest interface. </p>

        1. Dec 24, 2010

          <p>Mh, decorating... reminds me of christmas trees <ac:emoticon ac:name="wink" /></p>

        2. Dec 28, 2010

          <p>This is the route (pardon the pun!) I'd like to take. The reason being that passing just a request object to a dispatcher simplifies a number of workflows. As an example, I worked on an event-oriented dispatcher at one point, which made it difficult to allow different return values – it was much simpler to simply pass each event the request object.</p>

          <p>This would be fairly simple – DispatchableRequest would simply define two methods:</p>
          <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
          public function setRouteResult(RouteResult $result);
          public function getRouteResult();
          ]]></ac:plain-text-body></ac:macro>

          1. Jan 05, 2011

            <p>I am also think that it is a good idea.</p>

          2. Jan 10, 2011

            <p>After more thought I think the Request object should be immutable. <br />
            I cannot see why you would need to access the RouteResult more than once (just to dispatch it).</p>

            <p>Having the Request immutable allows you to cache the RouteResult per Request by decorating the Router instead.</p>

            <p>Something like this... </p>

            <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
            class Caching extends Decorator implements \Router
            {
            protected $requestActions;

            function __construct(\Router $router)

            Unknown macro: { parent}

            function lookupAction(\Request $request)

            Unknown macro: { if ($this->requestActions->contains($request)) return $this->requestActions[$request]; $action = parent}

            }
            ]]></ac:plain-text-body></ac:macro>

  5. Dec 23, 2010

    <p>"Hierarchical MVC. HMVC's primary strength is that it allows for creation of complex content by executing many different MVC triads."</p>

    <p>This would be just great. </p>

    <p>Exactly my problem ATM. <ac:emoticon ac:name="smile" /></p>

    1. Dec 27, 2010

      <p>HMVCs seem quite compelling but also demanding.</p>

      <p>Quite a good article here I thought:
      <a class="external-link" href="http://techportal.ibuildings.com/2010/02/22/scaling-web-applications-with-hmvc/">http://techportal.ibuildings.com/2010/02/22/scaling-web-applications-with-hmvc/</a></p>

      <p>...and a follow up to it optimising with caching and even (to an extent) asynchronous processing:
      <a class="external-link" href="http://techportal.ibuildings.com/2010/11/16/optimising-hmvc-web-applications-for-performance/">http://techportal.ibuildings.com/2010/11/16/optimising-hmvc-web-applications-for-performance/</a></p>

      1. Dec 28, 2010

        <p>They can be demanding, but a number of developers have created such implementations in ZF – and wished that the structure of the framework was such that it more easily facilitated such endeavors. </p>

        <p>As such, my hope is that the proposed interfaces can accomplish this.</p>

      2. Jan 08, 2011

        <p>I noticed that the PECL extension HttpRequestPool (<a class="external-link" href="http://www.php.net/manual/en/class.httprequestpool.php">http://www.php.net/manual/en/class.httprequestpool.php</a>) is being used in that second article. Perhaps an implementation of this can be included with the new MVC also?</p>

  6. Dec 23, 2010

    <p>Generally speaking the interfaces look quite good. But the HttpResponseHeaders interface is bloated a little bit. For testing the header status wouldn't it be better to do some chaining like:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $httpResponseHeaders->getStatus()>isSuccessful(); $httpResponseHeaders>getStatus()->getCode(); // ... and so on
    ]]></ac:plain-text-body></ac:macro>

    <p>Furthermore there are some inconsistencies like the <em>sent()</em> method. Shouldn't it be named <em>isSent()</em>, <em>wasSent()</em> or something like that?</p>

    1. Dec 28, 2010

      <p>These are great suggestions, and I plan to incorporate them. <ac:emoticon ac:name="smile" /></p>

      <p>I chose "sent()" for its brevity – but since we don't have a "?" operator for testing booleans in PHP, you're right – "isSent()" makes more sense.</p>

  7. Dec 25, 2010

    <p>This is looking good overall. A few notes:</p>

    <p>>> Routing could write matched metadata to the Request object metadata.</p>

    <p>I would like to see the request object as immutable.</p>

    <p>>> Application Configuration should be injected into the child objects.</p>

    <p>Excellent.</p>

    <p>>> Application-wide (or commonly used) resources must be configurable</p>

    <p>It would be helpful if these always needed a key prefix (assuming a similar style to resources.*) so > 1 of each resource could be configured. For instance, it is often necessarily to have two mail objects configured differently. Of course, this can be done today with a bit of massaging; however, a consistent and supported API would be a bit nicer.</p>

    <p>>> Design of any shipped MVC implementations must encourage module re-use, and must support modules as basic MVC infrastructure.</p>

    <p>Are we talking in terms of Symfony "bundles" and Rails Engines or are we talking about the current nomenclature of a module as a directory of controllers? I'm hoping we are talking about the former as I believe the latter can be more cleanly handled with routing and namespaces.</p>

    1. Dec 28, 2010

      <blockquote><p>I would like to see the request object as immutable.</p></blockquote>

      <p>This is a no-go for me – making it immutable makes testing more difficult (need to use different implementations for testing than you do for the application), and also means it cannot be used easily in other contexts (server classes, where the request would be decorated in order to deserialize the request to PHP data structures).</p>

      <blockquote><p>For instance, it is often necessarily<ac:link><ri:page ri:content-title="sic" /></ac:link> to have two mail objects configured differently.</p></blockquote>

      <p>I'd argue for situations like these, you either create a "manager" object, or we ship one in the framework (much as we've done for cache, log, DB, etc.). That makes the story of having multiple related resources simpler, and more easily configurable.</p>

      <blockquote><p>Are we talking in terms of Symfony "bundles" and Rails Engines or are we talking about the current nomenclature of a module as a directory of controllers?</p></blockquote>

      <p>Definitely the former – the idea is self-contained functionality, which would require controllers, models, views, all resources, etc. to enable that module to work.</p>

      <p>(This was the goal of modules in ZF1, and partly realized with Zend_Application, but the idea is to be more explicit with ZF2.)</p>

      1. Dec 29, 2010

        <blockquote><p>making it immutable makes testing more difficult (need to use different implementations for testing than you do for the application)...</p></blockquote>

        <p>I can appreciate this stance; however, I would argue that it would probably be better to test based on the interface which would allow 1..N implementations?</p>

        <blockquote><p>...it cannot be used easily in other contexts (server classes, where the request would be decorated in order to deserialize the request to PHP data structures).</p></blockquote>

        <p>If this truly is the deal breaker, then I would have to agree with your stance.</p>

        <blockquote><p>you either create a "manager" object, or we ship one in the framework</p></blockquote>

        <p>Fair enough <ac:emoticon ac:name="smile" /></p>

        <blockquote><p>Definitely the former - the idea is self-contained functionality, which would require controllers, models, views, all resources, etc. to enable that module to work.</p></blockquote>

        <p>This is good.</p>

  8. Jan 03, 2011

    <p>I would like to see the "Message" interface as an implementation of <a href="http://php.net/manual/en/class.serializable.php">Serializable</a>. Since the message object is at the main tier there is an extreme amount of usage for this object. It would be quite easy to extend this on our own, however, there is quite a bit of usage specifically for it to be an implementation of serializable by default.</p>

    <p>For instance:</p>
    <ul>
    <li>Flash Messaging</li>
    <li>JSON Payloads</li>
    <li>Apple APNS Payloads (meta data could contain implementation for the specific keys)</li>
    <li>Google C2DM Payloads</li>
    <li>Previous request methods</li>
    </ul>

    <p>Also with the "Message" interface, I am unsure about the following methods:</p>
    <blockquote><p>public function setContent($content); <br />
    public function getContent($content);</p></blockquote>

    <p>Is this meant to be an array going to set for content? Or a method to set just a string of body content? Not sure what get is supposed to do while taking a parameter... maybe a typo?</p>

    1. Jan 03, 2011

      <p>re: Message being Serializable – good point, and I think that makes sense.</p>

      <p>re: setContent/getContent(). First, I've now updated the proposal to remove the argument to getContent(); it was indeed a typo. As for what $content should be, I specifically didn't typehint it, to allow flexibility in what the argument may be. This will allow passing a string, an array, an object – it would be up to the individual implementations to determine what valid content might be for a message.</p>

  9. Jan 04, 2011

    <p>Regarding the </p>

    <blockquote>
    <p>The question is fundamentally this, however: how should controllers, models, and views get their dependencies/resources? Should we expect the front controller/dispatcher to inject them, or to pass a service locator to them so that they may pull them themselves?</p></blockquote>

    <p>I believe that passing the service locator or DI container into controllers, and views would make the code in the controller/view easier to understand since the service needed for the execution would be in the same place they are needed.</p>

    <p>Not sure though if this would have an impact on unit testing controllers.</p>

    <p>I really love the idea about Hierarchical MVC. And if I understood correctly it would be possible to create an implementation of ESI quite trivial. A had a look at <a href="http://docs.symfony-reloaded.org/guides/cache/http.html#using-edge-side-includes">Symfony 2 ESI implementation</a> and this is maybe the most important feature I would like to see done well in ZF2 because fetching parts of the page from memory at the reverse proxy level is order of magnitude faster that hitting the application on every request.
    <br class="atl-forced-newline" /></p>

    1. Jan 17, 2011

      <p>I'm definitely interested in investigating ESI for ZF2; I can't tell you how many times the "personalization problem" has bit me, and ESI definitely provides a solid solution for it. Right now, however, that investigation is a bit further down the line in the milestone.</p>

  10. Jan 09, 2011

    <p>I was wondering if the Response object should also be Dispatchable?</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$reponse = new Response();
    $response->dispatch( new Request() );]]></ac:plain-text-body></ac:macro>

    <p>The Dispatcher::dispatch method could then keep dispatching until either Request::isDispatched() (or similar) is true or the maximum number of dispatches allowed is reached.</p>

    <p>The return type of the dispatch method could then be null or another Dispatchable object.</p>

    1. Jan 17, 2011

      <p>While I see the elegance of this approach, I also worry that a semi-recursive structure like this would lead to difficulty in debugging and learning the framework. That said, since we can't type-hint on the return value, we could certainly check for a Dispatchable return and execute it.</p>

  11. Jan 11, 2011

    <p>The HttpResponse interface is currently sending the headers to the client by usage of Sendheaders and Sendcontent. Wouldn't it be better to change the methods to getHeaders and getContent? Because it not always clear where you want send the headers and content. Maybe you would want to log the headers and content? </p>

    <p>Furthermore, the HTTPResponse Interface should also be usable for Zend\Http\Client ( or the ZF2 equivalent )</p>

    <p>The same applies to HTTPRequest & HTTPRequestHeader & HTTPResponseHeader</p>

    <p>Furthermore the great number of isValid, isEmpty, is* is confusing, and should be reduced to a more normal limit</p>

    <p>delete:</p>

    <ul class="alternate">
    <li>isOk the isSuccesfull should be enough for most cases</li>
    <li>isInformational not many people will ever need this.</li>
    <li>isEmpty</li>
    <li>isServerError, isInvalid and isClientError should be combined in isError. People are most interested if it was succesfull or not. If people need more information, they can easily get this from getStatus.</li>
    </ul>

    1. Jan 17, 2011

      <p>HttpResponse has both get(Headers|Content) methods as well as send(Headers|Content) methods. Sending the response is done with emit() (defined in the base Response interface).</p>

      <p>Regarding the various response status testers, another commenter had a good idea: move the status into its own object. This allows us to trim down the HttpResponse interface, while still having a rich interface for testing status.</p>

      <p>I agree that we should have an isError() method – but this shouldn't rule out the more specific tests.</p>

  12. Jan 19, 2011

    <p>I like the suggestion for trait in dispatch. Good new PHP feature!</p>

    <p>I've looked at Kohana and Alloy frameworks in respect of HMVC. I think I get it.</p>

    <p>Would like to suggest the view interface provides for a view-html-header object which is accessible to every (cloned) instance of view for each triad. What I mean is you can mess with the current instance of view and you can also call on it to change the unique state the view-header object. I don't think view has been addressed yet fully.</p>

    <p>I still don't see the justification for a Zend_Application or a front controller - sorry. Better to create a Zend Core that also provides the signal-slot pattern for all classes extending it and then also provide resource grabbing such that based on the config, somehow you just call a resource as needed from anywhere and you get it</p>

    <p>If magic methods are undesirable it's getResource('request'), getResource('log') etc. and all lazy loaded in the core class of course - like a kernel for everything but not a singleton unless it has to be.</p>

    <p>In fact if every class extended a core class there would be no need for any singletons at all. Is that good or bad testing-wise? Perhaps convenience methods for the most often called resources? </p>

    <p>I'm still not sure how modularity would work for that but, perhaps a search list of resources in place of a namespacing would be okay if constants provided a convention for those common resources we all use? I like the idea of everything being a resource to simply call and not existing until referenced.</p>

    <p>Is there any chance of a changing demo showing HMVC capability? Nothing fancy, just flesh on the bones as I seem to be a learn-by-example type and would like to know the ZF2 interpretation re: PAC/HMVC on Wikipedia. Can the view call a new MVC triad for example? I hope so, but not assume the view can access the model.</p>

    <p>Finally the caching. Caching specific parts of the output seems ideal to me - as much or little as you want of the output - so it should be a stage before each MVC call I think and using existing HTTP standards as per: <a class="external-link" href="http://docs.symfony-reloaded.org/guides/cache/http.html#using-edge-side-includes">http://docs.symfony-reloaded.org/guides/cache/http.html#using-edge-side-includes</a></p>

    1. Jan 28, 2011

      <p>Mark, thank you for the very good article <ac:emoticon ac:name="smile" /></p>

      <p>Within this proposal I don't understand the difference of the following methods of HttpResponseHeaders: (setExpires and setMaxAge will set the headers but what does the others ?)</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      setClientTtl($seconds);
      setTtl($seconds);
      // va.
      setExpires($date = null);
      setMaxAge($value);
      ]]></ac:plain-text-body></ac:macro>

      <p>Another question is why does the method "HttpResponseHeaders::isNotModified" need a Request object as parameter?</p>

      <p>By the way it would be helpful to auto-generate cached responses by storing the sent response locally</p>
      <ul class="alternate">
      <li>cache only on GET/HEAD requests</li>
      <li>cache only 200 responses</li>
      <li>generate cache id in base of request<br />
      (URI + Vary-Headers ...)</li>
      <li>generate ETag in base of response (if not present)</li>
      <li>generate Last-Modified by mtime of cached item (if not present)</li>
      <li>create 304 response if:
      <ul class="alternate">
      <li>response was cached before</li>
      <li>ETag of cached response matches requested If-None-Match</li>
      <li>Last-Modified of cached response matches requested If-None-Match</li>
      </ul>
      </li>
      <li>create 412 response if:
      <ul class="alternate">
      <li>ETag of cached response doesn't match requested If-Match</li>
      <li>Last-Modified of cached response doesn't match requested If-Unmodified-Since</li>
      </ul>
      </li>
      </ul>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $dispatcher->setResponseCache(Zend\Cache\StorageFactory('filesystem'));
      $dispatcher->setResponseCacheEnabled(true);
      ]]></ac:plain-text-body></ac:macro>

      <p>Now you can also send a cached raw response if the client doesn't cached this resource before.</p>

      <p>Please correct me if I'm wrong.</p>

  13. Jan 19, 2011

    <p>PS. If you don't want to cache by time-out you cache when it reaches the controller based on DB change and I guess handle that manually.</p>

  14. Feb 05, 2011

    <ac:macro ac:name="note"><ac:rich-text-body><p><strong>Community Review Team Recommendation</strong><br />
    At this stage we accept this proposal, and suggest moving forward to implementing it. The CR-Team recognizes that when problems arise while implementing the interfaces, it may be possible to slightly deviate from this proposal (when discussed with the community).</p></ac:rich-text-body></ac:macro>