View Source

<ac:macro ac:name="toc"><ac:parameter ac:name="type">list</ac:parameter><ac:parameter ac:name="maxLevel">3</ac:parameter></ac:macro>

<ac:macro ac:name="note"><ac:parameter ac:name="title">Participate in the Discussion</ac:parameter><ac:rich-text-body>
<p>Please participate in the discussion! You can either do so in comments to these pages, or by subscribing to the zf-contributors@lists.zend.com &ndash; simply send an email to zf-contributors-subscribe@lists.zend.com!</p></ac:rich-text-body></ac:macro>

<p>Zend Framework has grown substantially and organically throughout the 1.X series of releases.</p>

<p>The result has been a framework that is immensely powerful, broadly appealing, and enormous in scope &ndash; but also one that is cumbersome and difficult to learn, distribute, and extend, and one that is becoming less and less performant. Additionally, since Zend Framework has experienced large adoption, this has led us to re-evaluate a number of design decisions &ndash; but the inability to break backwards compatibility has made it impossible or difficult to create better implementations.</p>

<p><strong><em>The primary thrust of ZF 2.0 is to make a more consistent, well-documented product, improving developer productivity and runtime performance</em></strong>.</p>

<p>The following document serves as a narrative detailing the various goals of Zend Framework 2.0. We will be gradually filling in an implementation plan for each set of goals so that we can measure the success of the effort.</p>

<h2>General Goals</h2>

<ul>
<li>Ease the learning curve</li>
<li>Make extending the framework trivially simple</li>
<li>Improve baseline performance of the framework</li>
<li>Simplify maintenance of the framework for the core team and contributors</li>
<li>Be an exemplar of PHP 5.3 usage</li>
<li>Provide mechanisms for using just the parts of the framework needed</li>
</ul>


<h3>Ease the learning curve</h3>

<p>In late 2009, we did a survey of framework users to determine what they use,<br />
what environments they use, and what their needs are. The top issue, bar none,<br />
was the difficulty of learning the framework. Some of these issues include:</p>

<ul>
<li>Difficulty in the &quot;first hour&quot; with the framework. Many of these questions have been addressed in the revised &quot;Quick Start&quot; shipped with 1.10, but additional issues remain: how to setup vhosts on non-Apache web servers, how to follow the quick start without a dedicated vhost, and problems arising from differences in operating environments.</li>
<li>Uncertainty about the &quot;next steps&quot; following the quick start. Developers are unsure how to go about using different components to create a cohesive application. Again, first steps at alleviating this concern have been taken with 1.10 with the &quot;Learning Zend Framework&quot; section of the manual. However, these are largely disparate tutorials, and don't show full development on a single application.</li>
<li>Inconsistent APIs in the source code itself. One component may use &quot;plugins,&quot; another &quot;helpers,&quot; and yet another &quot;filters.&quot; One component may utilize fluent interfaces, another not. One component may allow <code>MixedCase</code> plugin names, while another only allows <code>Titlecased</code>. One component may use a broker for accessing helpers, while another delegates to them via <code>__call()</code>. One component may allow <code>camelCasedOptions</code>, while another only allows <code>underscore_separated</code>. One component may allow passing an array of options only, while others allow passing an array or a <code>Zend_Config</code> object. The list can go on for quite some time.</li>
<li>Uncertainty about where extension points exist, and how to program for them.</li>
<li>Confusion over whether they can use Zend Framework only as an MVC stack or as individual components.</li>
</ul>


<h4>Goals</h4>

<ul>
<li><strong>MUST</strong> create two separate quick start tutorials: one that covers using individual components of Zend Framework, and another covering creation of a basic MVC application. In both cases, the tutorials:
<ul>
<li><strong>MUST</strong> explicitly indicate the expectations of developer knowledge. E.g., &quot;developer should be familiar with PHP 5, OOP, and have a basic understanding of autoloading; additional, some knowledge of Apache and configuring virtual hosts is assumed.&quot;</li>
<li><strong>MUST</strong> have reproducible steps that, if followed, will return the same results. Minor variations for common operating system environments <em>should</em> be covered (e.g., differences between linux, mac, and windows environments).</li>
<li><strong>MUST</strong> clearly identify the next steps a developer should take, and link to related references.</li>
</ul>
</li>
<li>Development of a set of cohesive tutorials covering intermediate and advanced Zend Framework topics. To achieve this we:
<ul>
<li><strong>MUST</strong> identify common development problems for Zend Framework, and the most common patterns used to address them.</li>
<li><strong>MUST</strong> write a sample application that illustrates these problems and solves them using the patterns identified.</li>
<li><strong>MUST</strong> have a clear narrative showing step-by-step construction of the application, one problem/pattern at a time.</li>
</ul>
</li>
<li>The reference manual:
<ul>
<li><strong>MUST</strong> clearly state the assumptions and use cases for each component. As an example, the various <code>url()</code> helpers currently allow passing a named route as an option. However, many developers do not understand that if none is provided, then it will use the currently matched route. This leads to a difference between developer expectations and code expectations, which has led to a number of issues in the tracker. Simply documenting these, as well as concise use cases, should alleviate the issues.</li>
<li><strong>MUST</strong> have consistent formatting for each component, for predictability.
<ul>
<li><strong>SHOULD</strong> provide an overview</li>
<li><strong>SHOULD</strong> provide expected arguments and return values, as well as exception conditions</li>
<li><strong>SHOULD</strong> provide one or more usage examples</li>
<li><strong>COULD</strong> provide narrative, but does not need to</li>
</ul>
</li>
</ul>
</li>
<li>Address the various API consistency issues:
<ul>
<li><strong>MUST</strong> standardize option casing</li>
<li><strong>MUST</strong> standardize language and APIs surrounding the various extension points</li>
<li><strong>SHOULD</strong> attempt to standardize APIs for passing options to components</li>
<li><strong>SHOULD</strong> attempt to identify and implement common API patterns throughout the framework</li>
</ul>
</li>
</ul>


<h3>Make extending the framework trivially simple</h3>

<p>Extending Zend Framework is relatively easy already. However, there are a few notable cases where it is difficult, and some of these cases are found in many places in the framework.</p>

<p>These include:</p>

<ul>
<li><em>Singletons</em>. Many singletons have a lot of behavior coded into them, making it difficult, if not impossible, to provide alternate implementations. Examples include <code>Zend_Controller_Front</code>, <code>Zend_Auth</code>, and <code>Zend_Session</code>.</li>
<li><em>Use of abstract classes instead of interfaces</em>. Many ZF components define rich abstract classes with a plethora of behaviors. While these may be extended and alternate implementations provided, doing so often requires accepting functionality you don't need, or needing to overwrite large sets of functionality you don't want. In many cases, the consuming classes only rely on a small subset of behaviors.</li>
<li><em>Hard-coded dependencies</em>. In many cases, components have hard-coded dependencies, making it impossible to alter behavior without extending the class to add your own dependency or to allow injecting the dependency.</li>
<li><em>Failure to identify processes that could benefit from collaborators</em>. As an example, many of <code>Zend_Db</code>'s methods could benefit from user extension &ndash; for instance, to allow checking for and writing to a cache, normalizing values prior to insertion, etc. In order to do this now, users must extend the existing classes, leading to difficult to traverse hierarchies and reducing re-usability. A plugin/filter system could alleviate these issues easily.</li>
</ul>


<h4>Goals</h4>

<ul>
<li><strong>MUST</strong> Eliminate singletons where possible; when not possible, push behavior into collaborating objects.</li>
<li><strong>MUST</strong> identify the essential interfaces for each component and subcomponent, and typehint on these instead of the existing abstract classes.</li>
<li><strong>MUST</strong> remove hard-code dependencies and allow for dependency injection.</li>
<li><strong>MUST</strong> examine each component and address the need for collaborators.</li>
<li><strong>SHOULD</strong> add a section to the documentation for each component, showing in a consistent structure the component's support for extension, plugins, and hooks.</li>
</ul>


<h3>Improve baseline performance of the framework</h3>

<p>Baseline performance of Zend Framework applications has been getting worse with almost every release; even when gains are made, new processes added in new releases lead to degradation. While ZF started as a fairly nimble framework, it is now among the slower alternatives available, when using the supplied MVC stack.</p>

<p>The goal of ZF 2.0 is to identify causes of performance issues and implement code changes to mitigate them until ZF is competitive with any other PHP framework of similar capabilities.</p>

<h4>Goals</h4>

<ul>
<li><strong>MUST</strong> improve the baseline performance by 200-300% over the 1.x series, and competitive with the more agile full-stack frameworks currently available, including Solar and Symfony 2.</li>
<li><strong>MUST</strong> provide deployment-time tools for optimizing performance in production</li>
<li><strong>MUST</strong> provide documentation, tutorials, and examples to help developers follow best practices for performance.</li>
</ul>


<h3>Simplify maintenance of the framework</h3>

<p>As of the time of this writing, Zend Framework consists of over 2 million lines of code, including over 14k unit tests. The internal team at Zend consists of a project lead and two engineers, while actual contributors number in the dozens. Contributors come and go, largely based on their interest in specific components and the time they have to devote to the project. High turnover of maintainers and lack of availability from maintainers, both internally and amongst contributors, has led to many orphaned components and/or components with large numbers of issues filed against them.</p>

<p>Additionally, with the growing complexity of many of the components, often due to differing expectations between different end-users and the original designs, we often get internal conflicts within components, where resolving one issue or addressing one feature requests can spawn regressions for existing functionality.</p>

<p>Another barrier has been actually getting contributions <em>into</em> the framework. Currently, we only offer SVN access on a case-by-case basis, which leads to developers posting patches as comments or attachments to the issue tracker. Often, the best way to really evaluate these patches is to apply them and run unit tests &ndash; and this method of obtaining the patches is inefficient. </p>

<p>When it comes to writing patches, many developers are unsure how to do so, or where in the tree to generate the diffs. As a result, often the patches submitted need to be doctored in order to be usable. Subversion does not help here, as <code>svn diff</code> can be done at arbitrary levels, and will not diff from the tree root.</p>

<p>Collaborating on complex components or component development is also often difficult, as there are no good conventions for maintaining separate branches in the ZF repository. As a result, many developers will do feature/component development in their own version control systems, and point developers to these to test. This can lead to overhead if they use a different class prefix during development versus once the component is pushed into the ZF repository, as well as if the original development is done in a different version control system.</p>

<p>Finally, there are a number of components for which 3rd party code offers more comprehensive solutions. Users may turn to these, or compare ZF components with them when evaluating solutions to their application needs.</p>

<h4>Goals</h4>

<ul>
<li><strong>SHOULD</strong> migrate to a distributed version control system
<ul>
<li><strong>SHOULD</strong> be a system many ZF developers are already familiar and/or *comfortable with</li>
<li><strong>SHOULD</strong> have the ability to specify multiple remotes per local repository</li>
<li><strong>SHOULD</strong> create patches from the repository root by default</li>
<li><strong>SHOULD</strong> support some method of verifying committer identity, to allow easy verification of CLA status when merging to the canonical repository</li>
</ul>
</li>
<li><strong>SHOULD</strong> include well-defined interoperability and compatibility with existing third-party libraries and frameworks.
<ul>
<li>This <strong>COULD</strong> be via consuming these third-party solutions</li>
<li>This <strong>SHOULD</strong> include helping define and follow interoperability standards, such as the common naming schema to allow common autoloading capabilities.</li>
</ul>
</li>
</ul>


<h3>Be an exemplar of PHP 5.3 usage</h3>

<p>One goal of Zend Framework was to advance the usage of PHP 5 and establish best practices surrounding the usage of PHP 5. To a large extent, ZF has achieved this goal (along with a handful of other projects).</p>

<p>PHP 5.3 was released mid-2009, and with it a myriad of new features were introduced: closures/lambdas, namespaces, late static binding, goto, and more. Many of these new features were introduced at the insistence of framework developers, as they can assist in making code more maintainable, concise, and readable.</p>

<p>Simply stated, Zend Framework 2.0 <strong>MUST</strong> be an exemplar of PHP 5.3 usage.</p>

<h4>Goals</h4>

<ul>
<li><strong>MUST</strong> use namespaces throughout the framework, to mitigate naming collisions with userland code and third-party code.</li>
<li><strong>MUST</strong> remove all <code>create_function()</code> constructs and replace them with closures</li>
<li><strong>MUST</strong> evaluate the framework to identify areas that could benefit from other new language features.</li>
<li><strong>MUST NOT</strong> use new language features just for the sake of using them. They should be utilized only where they make sense.</li>
<li><strong>SHOULD</strong> be a showcase for improving code through refactoring, which is itself a strength of PHP.</li>
</ul>


<h3>Provide mechanisms for using just the parts of the framework needed</h3>

<p>One criticism often levied against Zend Framework is that it is &quot;bloated.&quot; In most cases, the accusers are critical of the shear breadth of the framework &ndash; they do not want to install the entire code base when they only need one or a handful of components from it.</p>

<p>Additionally, many developers are using individual Zend Framework components within larger applications, sometimes written in other frameworks. ZF should support and encourage such use cases, and make it trivially easy for developers to fetch the components they wish to integrate.</p>

<p>On a related note, when pushing an application to production, often there are huge swaths of the framework that must be pushed that will never, ever be used, which wastes bandwidth, time, and disk space.</p>

<p>We have traditionally shipped the entire framework because it makes sense to have the entire framework available; if you need a component, you don't need to fetch it before using it. However, with the shear breadth of the framework, and the many uses to which developers are putting it, the time has come to enable developers to select individual components.</p>

<h4>Goals</h4>

<ul>
<li><strong>MUST</strong> provide code infrastructure to support packaging
<ul>
<li><strong>MUST</strong> use namespaces and good component design to ensure components are properly encapsulated</li>
<li><strong>MUST</strong> write tools that can automatically determine dependencies for components</li>
<li><strong>SHOULD</strong> write tools for automatically creating packaging information (e.g., files necessary, component name, release version, etc.)</li>
</ul>
</li>
<li><strong>SHOULD</strong> create a PEAR and/or Pyrum channel for distributing packages
<ul>
<li><strong>SHOULD</strong> create a number of meta-packages or bundles for common functionality; e.g., &quot;MVC&quot;, &quot;I18n&quot;, etc.</li>
</ul>
</li>
</ul>


<h2>Development Objectives</h2>

<ul>
<li>Simplify</li>
<li>Programming by Contract</li>
<li>Favor the explicit over the magical</li>
</ul>


<h3>Simplify</h3>

<p>Zend Framework is often accused of being &quot;bloated&quot;. When pressed, those who claim this will point to two factors: overall size of download, and the fact that most ZF components try to cover absolutely every use case possible, while specializing in none. The first factor is a straw-man; with a comprehensive framework such as Zend Framework, one <em>should</em> expect the download size to be fairly large. </p>

<p>The second factor, however, is very much accurate. It is also the source of our large number of issue reports. Developers consistently want a component to cover &quot;just one more&quot; use case, and adding support for it may then lead to inconsistent or undesired behavior in other use cases, leading to a spiral increase in reports.</p>

<p>Additionally, we have maintained a number of &quot;hacks&quot; and structures within the framework that can either be eliminated or replaced with language features available in PHP 5.3.</p>

<h4>Focus on Core Components</h4>

<p>Our goal with 2.0 should be to identify the <em>essential</em> use cases for each component, and what <em>specific</em> problems need to be solved. Components should be designed in such a way as to allow injection of alternate implementations, either through extension or implementation of interfaces. Any additional use cases can be covered either in alternate implementations shipped in the framework, or in custom, userland implementations outside the framework. The focus on maintenance should be on the core functionality of the component (and framework), no more, no less.</p>

<p>Additionally, we should examine components carefully to determine if a need for the component still exists, and whether the component is still under active maintenance. If the answer to either question is &quot;no,&quot; we should consider removing the component.</p>

<h4>Make Code Readable</h4>

<p>One of the &quot;rules&quot; of Zend Framework 1.X development was that all code should have vendor and component prefixes. In fact, a 1:1 relationship between the classname and the filename has been enforced. This is a good practice overall, but with the use of &quot;vendor prefixes&quot;, it has led to some verbose and difficult to read code.</p>

<p>For instance, to refer to any given class name, you must use the full classname. While this is not problematic for top-level classes such as <code>Zend_Loader</code> and <code>Zend_Registry</code>, once you get much deeper, the classes become unwieldy, and the prefixes tend to distract from the actual function of the given class: e.g., <code>Zend_Controller_Action_HelperBroker</code> seems like overkill for a class that is simply a &quot;Broker&quot;. </p>

<p>Additionally, this leads to over-long lines. If I need to reference more than one classname in a given line, I'm almost guaranteed to exceed the recommended line length and/or split the line into multiple lines. This leads to reduced comprehension for those trying to read and understand the code. Class names, method names, and variable names should be brief. Namespaces in PHP 5.3 can help make this a reality for classnames.</p>

<p>Finally, there is inconsistency in the framework with how code is formatted. While we have a published Coding Standard, we do not always follow it. Additionally, some practices are considered &quot;optimal&quot;, but not &quot;required&quot; &ndash; practices such as alignment of operators, keeping variable names meaningful yet short, consistent indentation, etc. For some detailed examples and discussions of why, read <a href="http://www.perforce.com/perforce/papers/prettycode.html">the Seven Pillars of Pretty Code</a>. These details need to be made required, and enforced via peer code reviews or pre- and/or post- commit hooks utilizing a utility such as <code>PHP_CodeSniffer</code>.</p>

<h4>Identify Common APIs</h4>

<p>Within the framework, we have a variety of common practices that have slight variations. As examples:</p>

<ul>
<li>Both action controllers and views can make use of &quot;helpers&quot; &ndash; but the mechanisms of how these work differ (action controllers use a &quot;broker&quot;; view objects call on helpers via method overloading).</li>
<li>View objects allow post-filtering of rendered contents &ndash; but the filters themselves do not follow the same interface as those found within the Filter component.</li>
</ul>


<p>Having differences between APIs makes learning the framework more difficult. We must identify those areas that are functionally similar, and refactor them to use the same common basis. This will ensure consistency across the framework, which will make learning and mastering the framework easier.</p>

<h4>Goals</h4>
<ul>
<li><strong>MUST</strong> remove obsolete, deprecated, or unmaintained components</li>
<li><strong>MUST</strong> rely on PHPUnit features to automate and simplify test suite maintenance
<ul>
<li>
<ul>
<li>data providers</li>
<li>@group annotations</li>
<li>setExpectedException()</li>
<li><code>TestAsset</code> namespaces</li>
<li>etc.</li>
</ul>
</li>
</ul>
</li>
<li><strong>MUST</strong> make code more consistent
<ul>
<li>Consistency in naming conventions
<ul>
<li>Classes are nouns, methods are verbs</li>
<li>Interfaces are nound or adverbs</li>
<li>Abstract classes are predictably named</li>
<li>Interfaces for any component that could have multiple strategies</li>
</ul>
</li>
<li><strong>MUST</strong> establish well-defined guidelines regarding code formatting to enforce readable code
<ul>
<li><strong>MUST</strong> migrate code to use namespaces</li>
<li><strong>MUST</strong> utilize a code sniffer, and define rules that correspond to our coding standards</li>
</ul>
</li>
<li><strong>MUST</strong> define and enforce consistent APIs; as examples:
<ul>
<li>Plugins</li>
<li>Adapters</li>
<li>Strategies</li>
<li>Helpers</li>
<li>Leverage language features when possible to assist (__invoke(), closures, etc)</li>
</ul>
</li>
</ul>
</li>
</ul>


<h3>Programming by Contract</h3>

<p><a href="http://en.wikipedia.org/wiki/Design_by_contract">Programming by Contract</a> asks<br />
developers and architects to create solid interfaces and answer the following<br />
questions during development:</p>

<ul>
<li>What does it expect?</li>
<li>What does it guarantee?</li>
<li>What does it maintain?</li>
</ul>


<p>(bullet points copied verbatim from the <a href="http://en.wikipedia.org/wiki/Design_by_contract">wikipedia article</a>)</p>

<p>Basically, any time an object uses collaborators, we should be defining<br />
interfaces to allow developers to inject their own implementations. These<br />
interfaces must be well-designed to ensure that we accurately capture the<br />
requirements of collaborators.</p>

<p>As an example, in the 1.X series of <code>Zend_Controller</code>, we have abstract <code>Request</code> and <code>Response</code> objects. These have grown over time to include additional functionality useful to developers, but the core functionality used within the ZF components themselves is quite minimal. Ideally, we would create lean interfaces capturing the behavior necessary for <code>Request</code> and <code>Response</code> objects to work in the ZF MVC &ndash; thus making it easier for developers to slip-stream in their own implementations. Any functionality beyond what is defined in the interface would be relegated to individual implementations, and likely through collaborator objects to ensure that alternate implementations don't break additional functionality.</p>

<h4>Goals</h4>

<ul>
<li><strong>SHOULD</strong> adhere to <a href="http://en.wikipedia.org/wiki/Solid_(object-oriented_design)">SOLID principles</a>.</li>
<li>All components <strong>MUST</strong> allow for dependency injection.</li>
<li>When collaborators are defined for a component, collaborators <strong>SHOULD</strong> follow a defined interface; if an interface does not exist, one <strong>SHOULD</strong> be defined.</li>
<li><strong>COULD</strong> implement a Dependency Injection container or utilize one from a third party source (such as Symfony Components).</li>
</ul>


<h3>Favoring the Explicit</h3>

<p>One obstacle new ZF developers face is identifying the source of functionality within the framework. One prime example is view helpers. As an illustration, consider the following example code from a view script:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
<?php echo $this->headStyle(); ?>
]]></ac:plain-text-body></ac:macro>

<p>Developers using an IDE can determine quickly that <code>$this</code> refers to the view object, but, unfortunately, most IDEs cannot determine the source of the function <code>headStyle()</code> &ndash; because it does not exist in the view object. The new developer may then turn to the API documentation &ndash; and fail to locate the method. If they're familiar with the concept of overloading, they may look at the <code>__call()</code> method finally, and discover that it proxies to view helper objects, but then they need to determine how view helpers are loaded and executed. All-in-all, it adds a ton of overhead when trying to learn the framework.</p>

<p>Additionally, &quot;magic&quot; methods such as <code>_<em>call()</em></code><em>, </em><code><em>get()</em></code><em>, and </em><code>_set()</code> have performance implications; they take roughly 6x longer to execute than simply calling a standard method. By favoring explicit calls, we can likely radically improve the baseline performance of the framework.</p>

<p>In the above example of view helpers, how might we change the design to be more explicit? One potential solution would be to introduce a &quot;helper broker&quot;, and use it to access helpers. Some use cases might look like this:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$view->broker()->attach('greeting', new MessageHelper());

// Proxy execution through the broker
echo $view->broker()->execute('message', 'world');

// Execution of an interface method on a helper pulled from the broker
echo $view->broker('message')->execute('world');

// Execution of an arbitrary method on a helper pulled from the broker
echo $view->broker('message')->greeting('world');

// Retrieving a helper from the broker, and acting on it
$helper = $view->broker('message');
echo $helper->execute('world');
echo $helper->greeting('world');
echo $helper('world'); // using __invoke()
]]></ac:plain-text-body></ac:macro>

<p>In all such cases the user can easily traverse the API and also get assistance from the IDE; all calls (except the last example, using invocation) use explicit methods, with clearly-defined return values. </p>

<p>This explicit approach is slightly more verbose than using the magic methods, but should ensure that code is simpler to understand, more consistent to use, and more easily maintained.</p>

<h4>Goals</h4>
<ul>
<li>Evaluate all uses of <code>__magic_method()</code> calls, and determine if they may be refactored to remove them.</li>
<li>Where we determine magic methods are necessary, their usage should be well-documented</li>
</ul>