Love Fuel?    Donate

Thoughts, ideas, random notes, ramblings...

Anything about PHP in general, and FuelPHP in particular. Sometimes serious, sometimes with a big wink. But always with a message. Do you have an opinion about an article? Don't forget to comment!

A question that has been asked many times about 2.0 is "why the big changes", in this blog I will attempt to explain the choices made and place them in context. But let me also begin by saying that if there weren't any big changes in it, it wouldn't be a 2.0 version but just a 1.x version that got bumped beyond its station.
First to ease some of the fears: we will make a static interface available for 2.0 classes and a Legacy add-on that will almost completely mimic the 1.x usage. And for those classes that already work primarily with objects after forging them: little will change but for the way you forge them.

A bit of history

Fuel began when Dan broke with CodeIgniter because it had become stale in his, mine and many other minds. It's codebase had been written originally for PHP4 and as they never took a leap and broke with the old base it was basicly still a PHP4 framework at the time (and even though they got the community involved to expand it, the architecture remains unchanged).

With the CodeIgniter experience and knowledge he started Fuel and was joined by Phil, myself, Harro and Frank. All of us came with backgrounds in an array of frameworks such as Ruby on Rails and Kohana, but mainly CodeIgniter. We had the desire to create a more modern framework, for PHP 5.3 taking on board some of the lessons learned by other frameworks.

I feel that with 1.0 and especially 1.1 this was a goal we reached. For me the framework allowed my applications to grow beyond what would have been possible in CI.

But as we improved we also took some baggage with us that stayed in one way or another. We got rid of the Singleton-like core class concept and implemented everything as static Multitons. Which is an improvement over CI, but still has the downside of a global scope. And this became at the same time our biggest boon (easy static syntax and instances available when required) and our biggest limitation (global scope).
The global scope gave as various problems when we started to think about where we wanted to take the framework next. But most of all it was very hard to unit test some of the most essential core classes. And the lack of unit tests has given us problems with each release that should have been easily preventable.

The next step

Some of the plans for 2.0 already entailed:

  • wrapper objects for applications (allowing multiple active applications in runtime)
  • wrapper object for the environment setup
  • input overwrites for each HMVC request
  • replacing the multitons with a Dependency Injection Container (DiC) behind the scenes

As you may conclude this means some new layers that give more context to your application than currently available. Right now each application is at the same time its environment and really doesn't differ that much from the controller context. To get that to a level that allowed for nesting multiple application objects that live within an environment object, I had to eliminate pretty much all the global scope available. Which in the end came down to eliminating all static methods as all those broke context and could take an application context from global while working inside another.

When the essential core classes became ignorant of global so had everything else. Where 1.x had had essentially no inversion of control, 2.0 is suddenly using a DiC and setters all over the place. Where the global state had been binding things together in 1.x, the 2.0 code is held together by a beautiful object graph.

What does this mean?

The most important improvement is that it will be possible to test every class in full isolation, thus making unit testing much easier. Having the core fully unit tested will also be a requirement for the first 2.0 release.

Most of the core classes you use regularly won't really change. Views will still be Views, Controllers are still the same thing and most of the current libraries will be ported over with little functional changes (except for those mentioned on the roadmap). But as you may have understood by now the big change is that the Core packages of 2.0 will no longer contain static classes. That doesn't mean that the syntax is gone for everyone though, those of you who don't mind a global state but do want the new and unit tested codebase will still be able to make the old calls on the new codebase using static calls. And of course nothing has to change either in your own classes, if they were static or if they aren't fit for usage with a DiC: autoloading will still work as before and no code needs to be changed on that account.

But what will the new non-static syntax look like? Some of that is not yet clear, and we are still thinking about improving it. But one thing is for sure: any core class object always needs to come from somewhere, either as an Environment, Application or Request property or from the Application DiC. Classes like the Controller and ViewModels (renamed to Presenter in 2.0) will have references to the Application object that created them.

Here's an example:

// 1.x / 2.0-legacy-enabled
$var = Input::post('var');
// 2.0
$input = $this->request->input;
$var = $input->post('var');

And for other things the difference is smaller:

// 1.x / 2.0-legacy-enabled
$val = Validation::forge();
$view = View::forge();
// 2.0
$val = $this->app->forge('Validation');
$view = $this->app->forge('View');

I know this looks like a step backwards to some of you, which is why we will keep the static interface alive. This might look somewhat like the CodeIgniter loader but is completely different. It acts just like the existing forge() methods in each class, pumping out instances to be used however you like.

We realize many of you fell in love with the simplicity of the mainly static auto-loaded class names. Where you can start working with a class by just talking directly to it without a loader, but there is also a significant downside to that. When it comes to unit testing there are two strategies called called "Stubbing" and "Mocking", which involve faking a class to see what one class is passing to another. This is near-impossible to do if class A is talking to a static class B, as there is no way for us to "inject" a fake class B.
It also helps massively with HMVC, as Input::get() is global, but we want to have different input available so that HMVC requests emulate HTTP requests perfectly. Doing this allows developers to build up API-driven systems, then use HMVC to call that API, cutting out a HTTP request. But with the static usage the global query string of a controller could interfere with the HMVC request, causing all sorts of problems.

The static interface will continue to exist via a Static package and the Legacy add-on, but the purely OO usage is now the bread-and-butter of Fuel.

The bottom line

We are moving forward to become a better designed, unit tested and in general more flexible framework. For that it is necessary to break with some of the baggage that limits us in what is possible. At the same time we do not want to leave our 1.x users in the cold, which is why development of the Legacy package is a big and important part of the 2.0 development. But we did separate these to make both a clean break and at the same time be able to provide backwards compatibility.

A short note about the speed and memory usage resulting: at this point baseline 2.0 compared to 1.1-dev showed to be about the same speed (~7% slower on a Linux server, ~25% faster on Windows laptop) as wel as memory usage (<2% more on Linux, ~7% less on Windows). These are too early for any final conclusion on it, but are indicative of what is possible.
[late update march 13th: 2.0 is now about 7% faster and uses 8% less memory on Linux after some more optimizing]

- Jelmer