There are no solutions, only tradeoffs.

Solar

Fabien Potencier released Symfony 2.0.0alpha1 last week, along with some benchmarks showing its performance. I am glad to see that Fabien used my benchmarking system and methodology, and am happy to see that he is paying attention to the performance of his framework. I take this as an acceptance on his part that my methodology is legitimate and valid, and that it has value when comparing framework responsiveness.

However, in attempting to reproduce the published Symfony 2 benchmarking results, I found Fabien’s reporting to be inaccurate (or at least incomplete). Read on for a very, very long post detailing my attempt to replicate his results for the “hello world” basic framework overhead comparison, and my conclusions.

For the impatient, here are my conclusions in advance:

Fabien’s benchmark report, as shown at http://symfony-reloaded.org/fast, is inaccurate for the setup he describes. Lithium and Flow3 do not work in Fabien’s benchmark codebase at Github. Also, Symfony 2 is faster than Solar beta 3 by 5%, not 20%, on a “c1.xlarge” instance; to get a relative difference like Fabien describes, one has to use an “m1.large” instance. (It is entirely possible that the process Fabien used for benchmarking is incompletely described, and that the codebase is not fully updated, thus contributing to this disparity in results.)

We should use Siege 2.69, not 2.66, for more accurate benchmarking of baseline responsiveness. If we notice that HTML is slower than PHP, it’s a sign that something is wrong.

Symfony 2 preloads its foundation and application classes, something no other framework does in the benchmarked code. When we treat Solar and Symfony 2 the same way, by preloading the foundation classes for each, we find that Solar is roughly 28% faster than Symfony 2.

Yesterday, I released Solar 1.0.0beta3. You can see the very short change log here. Special thanks to “kalkin” for committing a series of fixes and improvements.

These days I’m concentrating more on documentation. Previously, we had only the ubiquitous quick-start blog demo. This release includes a new chapter on Solar’s dynamic dispatch cycle; i.e., the bootstrap, front controller, page controller, action method, and view. (I’m starting a chapter on the DataMapper-style model system today.)

Incidentally, I’ve been writing the docs in DocBook 5 and building them with xsltproc. You can see the whole build system in the Solar docs repository. I tried using the new PhD build system, but it required more effort and energy than I could spare. PhD is screamingly fast, but it’s not doing everything that xsltproc does just yet, and I barely have enough time to work on my own projects as it is.

FYI: This is a cheerleading post. Some will be annoyed by it or find it “unfair”. Others may find it informative and enlightening. I make no apology for advocating what I think are the good things about Solar, especially when other projects appear to be mimicking those things.

Solar is already doing most of what is described as the future for Zend. Let’s go over this, shall we?

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.

Solar has had a framework-wide unified constructor for years now. As far as I know, Solar is the first PHP framework to demonstrate this design pattern and give it a name. You can have a unified constructor right now with Solar.

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.

Solar has already standardized on its config keys as lowercase-underscore. You can have standard config keys right now with Solar.

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.

Solar provides a unified exception mechanism already. This is not quite the same thing as described in the ZF2.0 document, but if the idea is to have a unified way of throwing and recognizing exceptions, Solar is already doing it.

Design By Contract.

OK, we don’t really do this. Solar tends to use adapters, plugins, class stacks, and duck typing, not interfaces.

Elimination of most singletons.

Solar doesn’t use singletons. We use the registry and, occasionally, unified static-method-collection classes. You want two or more database connections? Mark them for lazy-load in the registry, and set your config file to use the appropriate service locator keys for whatever objects may need them. You can have this right now with Solar.

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)

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).

Solar is already autoload-only. You can be autoload-only too right now with Solar.

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.

Solar already has first-class support for PHP 4/5 style namespaces (i.e., pseudo-namespaces) and supports mixing of multiple “vendor” prefixes in projects (and supports cross-vendor fallbacks across library hierarchies). Solar expects that you will be working within your own pseudo-namespace already, and so is poised to use 5.3 namespaces proper with little effort. You can have namespace-like support right now with Solar.

MVC Implementation: 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 …

Solar‘s dynamic dispatch cycle is much faster than the one in Zend Framework, and has been for years; see these benchmarking posts:

Models and Master-Slave

One more thing that Zend Framework doesn’t have, that Solar does, is an integrated model system. Zend Framework recently dropped the idea of having their own, and expect to depend on an external system for reasonable model/record/collection support. Solar has an integrated model systemright now with support for single table inheritance, various relationships, unified saving of object graphs, automatic data filtering, automatic form generation, calculated columns, collection methods, automatic data caching and clearing, and much more.

Yet another thing: we also provide support out-of-the-box for master-slave MySQL replication, with no changes needed to the codebase. Change your config file from the single-server connection to the master-slave connection, and you are done. (Well, you might have to wrap some parts in transactions, but after that, it will work on both single-server and master-slave setups.)

You can have robust models and master-slave support right now with Solar.

Modesty

There is one significant thing that Zend has that Solar doesn’t: narrative documentation. Our API docs are second to none, but the tutorials are lacking. We’ve been spending our time making the framework great, not writing about the framework. You can help with that if you like by adding to the wiki.

Conclusion

The point of all this is not to belittle Zend, Zend Framework, its developers, or its contributors. The point is to ask Zend Framework adopters: why wait for ZF 2.0 when you can have all those features and more, right now, by using Solar?

Solar 1.0.0alpha5 has been released, with a few bugfixes and one change. The change notes are below. Also of note, I have created separate "alpha" and "beta" branches, so we can continue bugfix releases on the alpha side while not having to release new beta features.

Solar_Form

[FIX] Form attributes for id, name, and class are now reset properly. Thanks, jelofson and stloyd, for the report and patch.

Solar_Class

[FIX] Do not use $this for exceptions in a static method call. Thanks, Richard Thomas, for the report and patch.

Solar_Sql_Model_Cache

[FIX] Method getCacheKey() now converts the fetch params to array before unsetting values. Was previously unsetting the ‘cache’ value (among others) causing cache results not to be saved in Solar_Sql_Model. Thanks, Anthony Gentile, for the report that led to this fix.

[FIX] Default adapter is now Solar_Cache_Adapter_None, as it is in Solar_Sql_Model.

Solar_Sql_Model_Params_Eager

[CHG] Method joinCond() (and array key ‘join_cond’) now accept arrays for cond => val the same way where() and having() do in fetch params. Thanks, Anthony Gentile, for the report that led to this change.

Solar_Sql_Model_Params_Fetch

[FIX] In method cache(), use $this, not $$this. Was preventing the cache value from being set, so it was always null. Thanks, Anthony Gentile, for the report that led to this fix.

Solar_Sql_Model_Record

[FIX] In method newFilter(), do not add filters for table cols that are not part of the fetch cols. All other filters are still added.

Solar_Sql_Model_Related_HasManyThrough

[FIX] In _modEagerFetchJoin(), cast the base join conditions to array before merging.

The alpha3 version of the Solar Framework for PHP5 has been released; you can get it from the usual locations. Of note, this will be the last alpha release of Solar; I expect to have a beta released within the next 8 weeks or so.

Big news (for me anyway): my employer, OmniTI, is allowing me to spend a minimum of 40% of my paid work time on the Solar Framework for PHP 5 (plus additional time as duty allows). The project remains open-source under New BSD license.

The agreement has been in place for several weeks now, and has allowed me to put a lot more effort into the project lately (as a review the SVN logs will reveal) while keeping a paycheck. At the same time, OmniTI gets to show its support for one of the many the open-source communities it has been able to build its business on. Thanks, Theo and Ciprian, for this generous allowance. I appreciate it, and so does everyone who uses Solar; I think it’s a big win-win all around.