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!

This is the 3rd part in a series of blogs on the philosophical design choices behind FuelPHP 2.0. The earlier parts were on the biggest changes and on MVC in general.

Context is what gives meaning to otherwise meaningless or ambiguous statements. This holds true not only in language but in programming as well, or at least it should. Those of you who used HMVC may have run into problems in the earlier versions of FuelPHP when Views weren't aware of their context. A statement in the View using Uri::segment() might have a different meaning depending on during which Request it was rendered, instead of the meaning it had when the View was created. This was caught and solved early on, but as explained in the earlier blogs, 2.0 will solve this in a more fundamental way.

What do you mean by 'context'?

The concept of context is similar to Namespaces in PHP: the context (or namespace) gives meaning to particular statements (function/method calls). A call to a model Category may have a different meaning within a webshop and within a weblog. It probably broadly still refers to the same concept, but its context and contents are different. In most cases a category of webshop products (Webshop\\Model\\Category) will have different demands than a category of blogs (Weblog\\Model\\Category). The concept didn't change (they might even share a base model they're based on), but the context changed and asking for the category's items will give wildly different returns.

Another (more basic) example was already given in the introduction. Because of the static interface of FuelPHP 1.x the static calls don't have any context and thus their return was the same, no matter whether it was called from the main Request or the child of a child of a child request. This was solved by adding a concept of an active request that determined the context of such a call. This made Uri::segment() return something different while the main request was active from when the same call was made while a child request was active.

The next iteration of Fuel will solve this differently: as we'll become more object oriented every View, Request, Application, etc. will be aware when it was created and/or to which context it belongs. These internal references will allow you to request information directly from a specific context. To illustrate this: when you make a call like $this->app->forge() from the controller you are telling the Controller's Application to forge an object within it's 'context'. And when you call $this->app->env->forge() you are making the same call but to the greater context of the Application's Environment.
At the same time we'll keep the active request and introduce the active application references to allow static calls that are also context-aware. This is the 'weaker' solution of the 2, as it requires something akin to a global context - something that makes your applications much harder to unit test. (at the same time it will still use the internal references behind the scenes)

Types of context

Now we know what context is we need to define the types that are available during your application's runtime. One might argue that each class is its own context and one would be right about that, so for clarity we'll limit the discussion here to those types of context that have a greater importance within FuelPHP.

Environment

The environment consists of those settings, values and functionalities that are defined by the operating system, by the webserver and by PHP itself. These make up a context that should be considered static during at least each HTTP request (should not be changed after FuelPHP's initialization). Locale and language are examples of this, changing those during runtime might give your output half one locale and the second half the other. The mess of which is obviously something you'd never want.

But also PHP core language concerns should be considered part of the environment. You can't redefine a classname for example, thus if you've loaded a class once with a specific name you can't load a class with the same name (note: the namespace is part of the classname). Functions, classnames, constants and variables are an important part of the context in which you code lives.

Application

Each application has its own configuration and specific concerns for that application. If you need multiple applications to interact with each other (possible as of 2.0) you need these to be completely separate. But it's not just the configuration, you will also have utility classes and objects that need the configuration or keep track of your running application - those should live within it's application context.

Request

The concept of a request in FuelPHP is similar to that of HTTP but not quite. In the 1.x branch each HMVC Request will have its own URI and may have some overwrites but essentially still lives within the same HTTP Request as the main Request does. As of 2.0 this will change and a Request will live within the same Application context as it's parent Request, but it will also have it's own context with completely different input variables.

How does this work

In the 1.x branch there is only a very limited Environment context, it consists mainly of a name that allows you to change some configs based on it. But most of its initializing happens in the Fuel class after loading the application main config. This means it is not only tied to the Application, but it is actually so intertwined with it that it cannot truly be seen as different. And when you want more than one application to exist (in runtime or otherwise) this will quickly get you into trouble, as the fixed truths of one application may differ wildly from those of another.

And as the environment should stay constant between Applications, so should the Application constant remain constant between Requests. As noted before, the 1.x branch makes too little difference between Requests. One possible problem with this is that one might want to use the same types of data retrieval for AJAX as one does from within PHP. The way to solve that is to make a Request from PHP that looks the same as a HTTP Request from JavaScript would look. Something that is hardly possible when the input variables cannot fully be controlled. This also exemplifies why the Application's context should remain constant, otherwise the AJAX Request and the HMVC Request might still lead to wildly different results.

Context 2.0

FuelPHP 2.0 will introduce an Environment object and Application objects that wrap you application. These will maintain the types of contexts discussed here. This is also why Dependency Injection became a central part of 2.0. The Environment and Application objects each have their own object containers that maintain instances within the context to which they belong. But they also have their own forges  (factories) that determines the class to construct based on context.

All Applications one creates within the Environment object will retain a reference to this Environment for context. All Requests created with an Application will be aware of their Application context as well, and indirectly be aware of their Environment via the Application. And the same goes for each and every object instance created.

While 2.0 will still keep track of which Application and Request are currently running, it is no longer the way in which context is determined. The 1.x branch needed to reactivate a Request instance in order to provide the correct context when a View was rendered. In 2.0 the View has object references to its context and doesn't need any environmental context changes to be able to render itself within the proper context. (FuelPHP will still track this and reactivate/deactive for Backwards Compatibility though, but it doesn't need it)

In practice

One of the first places where this has already been put into practice has been Oil. Previously Oil was basicly a separate bit of logic, it used FuelPHP's main parts of course but the application flow of it was completely separate from the logic in a normal request. There where a few downsides to this, for one we'd need to explain the logic of running an Oil task separate from the logic of running a normal HTTP request. Another was that Oil was fully dependant upon 1 application you created, and not really capable of switching between them. The new setup makes Oil a full application within its own right, which can interact with any other application or even package you choose. Because of the new context-awareness a command you give to Oil will be executed within the context of the app upon which you make Oil act, instead of within the context of Oil (another one of the downsides that lead to problems).

The way Oil does this is to instantiate another application by requesting it from the Environment. Then a request is made to that application to execute a request within its context and the result is returned. This will happen in full isolation and non-dependant upon Oil's context:

// Load the application at 'fuel/example' (remember: applications are packages themselves)
$app = $env->loader->load_application('fuel/example');

// To request something from the other app, make a request upon it and get the response
$response = $app->request('migrate/up')->execute()->response();

The above is just an example but it shows how one might migrate a specific application from another (Oil for example). This would be done by tasks which are now specialized controllers instead of plain classes, this will make the same power available through the command line as you have in a normal request and allow you to work with multiple applications. Something that will be possible and reliable because of the improved context awareness.