|Participate in the Discussion|
Please participate in the discussion! You can either do so in comments to these pages, or by subscribing to the firstname.lastname@example.org – simply send an email to email@example.com!
Zend Framework 2.0 will be the first major version following the 1.x series, and as such is the first time BC breaks will be allowed. Our goal is to make breaks only where they make sense:
there will be no renaming of components just for naming consistency, but we will improve the code base to make it more maintainable, easier to extend, or make use of new language features that better accomodate the design,
At this time, we aim to have ZF 2.0 have a minimum requirement of PHP 5.3 (which version of 5.3 is still to be determined). This allows us to make use of a number of new language features that better suit framework development, such as namespaces, __invoke(), closures, goto, Late Static Binding (LSB), etc.
This page serves to summarize various changes we will be making for 2.0, both as new features as well as improvements. Additionally, it links to several pages covering specific components.
Our goal with ZF 2.0 is to improve the overall consistency of the project. With consistent APIs relating to constructors, options, and exceptions, developers can more readily grasp the basics of each component and move on to specifics of usage.
- Unified constructor. All constructors will (optionally) accept an array of options or Zend_Config object as the first argument. This allows for more flexibility to add new arguments to the constructor, have variable numbers of arguments, and allow "named" arguments. Additionally, it's a good technique for allowing Dependency Injection.
As part of this initiative, most components will also have a "setOptions()" method to which the options array will be passed (and which may be used later to reset object state). Typically, this will proxy to other setters.
Many components in ZF 1.X already do this: Zend_Application, Zend_Form, Zend_Validate, Zend_Layout, etc. We are simply extending this paradigm to cover all components.
In order to reduce code duplication, we will likely introduce a zend\Options class, which will work roughly as follows:
- Options. In ZF 1.X, the various components which accept options accept a variety of formats: some expect underscore_separated keys, others expect camelCasedKeys, others expect UPPERCASEDKEYS, and some expect lowercasedkeys. This leads to confusion for many, and also leads to difficulties debugging. Our goal in ZF 2.0 is to standardize option keys to correct this situation.
Currently, we are leaning towards all_lowercase_underscore_keys. These are human-readable, contain only valid PHP variable characters, and make for a simplified option parsing implementation. Additionally, it is trivial to translate such keys to camelCase ("str_replace(' ', '', ucwords(str_replace('_', ' ', $value)))") for purposes of overloading – and this makes it easy to document option key => class property affiliations.
- Exceptions. Each component will have an Exception marker interface, with exceptions defined for discrete exception types thrown by the component. The concrete exceptions will either extend the global Exception class or an SPL Exception, and also implement the component Exception interface. This allows us to throw appropriate SPL exceptions while retaining a common Exception type (via the marker interfaces). As an example:
- Design By Contract. We will be altering portions of the framework to follow the concept of design by contract, and new development will follow this paradigm. At first, this will take the form of refactoring to add interfaces/refactoring interfaces to reflect actual usage.
Interfaces make creating alternate implementations of standard classes easier, while assuring that these implementations will continue to work with the classes that consume them. In many cases currently, we offer only abstract classes, and no equivalent interfaces; in other cases, the consuming classes use methods that are not part of the interface.
In addition to this initiative, we will be eliminating some functionality intended to add flexibility to some of the standard classes. We have found that oftentimes this functionality leads to performance overhead as well as consumes maintenance time that could be used better elsewhere in the framework. As an example, the Dispatcher has a number of methods for adding custom class -> file mappings that are largely unused and which use an unnecessary number of CPU cycles; refactoring this to follow only the standard use cases would ease maintenance, while having a good, simple interface would make creating alternate implementations for custom use cases easier.
- Elimination of most singletons. ZF has often been accused of "singletonitis." While we're not sure if we completely agree, we will note that in most cases, the singletons we have have presented a number of problems and led to difficult test cases. Additionally, in most cases, the singleton is unwarranted. We will be refactoring to eliminate these, including in Zend_Controller_Front. In exceptional cases, we will keep them; these include global operations such as autoloading and database connections.
- Creation of components for general-purpose, cross-functional actions. A number of components duplicate code, and we want to push the duplication areas into discrete components that the original components may then consume. Some of these include:
- Plugins/Helpers/Strategies (seen currently in Zend_Controller_Front, Zend_Controller_Action_Helper, Zend_View (helpers and filters), Zend_Form (validators, filters, and decorators), etc.). Plugin discovery and loading could benefit from a common API.
- Decorators (seen currently in Zend_Form; other areas could benefit from the pattern)
- Factories (seen currently in Zend_Db, Zend_Navigation_Page, Zend_Auth, Zend_Translate, Zend_Cache, etc.)
- Caching (seen currently in Zend_Translate, Zend_Locale, Zend_Queue, Zend_Paginator, Zend_Feed_Reader, Zend_Db_Table, etc.)
- Usage of new language features within plugin architectures. Currently, most plugins rely on the strategy pattern, but the mechanism differs between plugins. PHP 5.3 offers some compelling alternatives that we want to explore: __invoke() and closures. Closures are less compelling as we cannot do simple type-hinting on them. __invoke() offers perhaps the simplest solution, and could become a standard API for plugins.
- Autoload-only. We will move to using autoloading throughout the framework. This solves a number of performance issues, as well as simplifies coding dependencies (particularly exceptions).
- Namespaces. PHP namespaces benefit frameworks and libraries more than any other code bases, and ZF can benefit from it greatly, particularly with components such as Zend_Search_Lucene and Zend_Controller. However, adopting namespaces does not come without a cost: all code will need to be rewritten to define and use namespaces. This will be the single biggest BC break we introduce.
We also plan to introduce a separate namespace for unit testing, and are currently looking at either \test\zend, \zend\test, or \zendtest. This, along with per-component namespaces, will help prevent naming collisions within the test suite.
- goto. Goto is often considered "evil," but is invaluable when creating Finite State Machines (FSM) and parsers; usage of goto will be evaluated on a case-by-case basis. Some examples of components that could potentially benefit from such an implementation include Zend_Search_Lucene (which already implements a FSM), the MVC (more below), Zend_Ical, and Zend_Markup (though not all of these may make use of it).
Our current MVC implementation is increasingly adding overhead to the dispatch cycle, slowing down the request cycle. While you can squeeze additional performance out of it via stripping require_once calls and using autoloading, the fastest requests are still far short of other, slimmer frameworks (CodeIgniter and the various new microframeworks come to mind here).
We are considering several potential approaches to rectify this situation. As mentioned in the previous section, we will be refactoring a number of components to provide strict interfaces. This, along with good documentation of these interfaces, will allow for easier extension and customization of ZF components. This frees us to provide a more conventions-based approach with ZF's MVC layer. The benefit to this is that it makes it possible to streamline the MVC code to just what's necessary for the 80/20 case it was designed for – while still providing flex points (via the interfaces) for customization.
Second, one approach other frameworks (both PHP and otherwise) are using is to provide explicit mapping of URL routes to controllers (a prime example is Horde_Routes, which is in turn based on the Python Routes project, which was in turn inspired by Ruby on Rails). This approach is potentially slightly less RAD friendly (after developing your controllers, you then need to add them to the map), but has the benefit of simplifying the dispatcher (the controller and action are known and do not need to be discovered) while still offering lazy loading. We will be looking into this to either offer as an alternate implementation or as part of the core offering.
Finally, one possibility offered by PHP 5.3 is the idea of having the front controller be a state machine, due to the addition of goto to the language. This would make it possible to define specific application states and easily jump to them, solving several issues that currently occur due to operating within a loop. It's also a common paradigm particularly with event-driven architectures, and should be somewhat familiar to JS and/or GTK developers.
For more information on proposed MVC refactoring, please visit the following pages: