Let's start with the bootstrap mechanism. The procedural bootstrap code is in the index.php and application/bootstrap.php since Kohana 3.0. In KO3.1 I can only see a very little difference: the main request execution is done at
index.php
and not at application/bootstrap.php but nothing else changed seriously, environment and routing setup is the same as in 3.0.So at the end of
index.php
the main request is initialized, executed and it's response is sent back to the client:echo Request::factory() ->execute() ->send_headers() ->body();
It's also (almost) the same as it was in KO3.0 but the internals are pretty different - and this is the topic of this post :)
If you are familiar with Kohana, you surely know that it implements the HMVC design pattern, but if not, read about it before going on. In a nutshell it's an architectural pattern, where you have a main request coming from the client. The request has a proper MVC triad, and it has sub-requests which do more atomic parts of the job. All of the subrequests have their own MVC triad, and they can have further subrequests, recursively.
The
Request
class in KO3.1 has two strictly HMVC-related static properties: $current
which is the currently executed request and $initial
which is the main request (note that it was $instance
in 3.0).When
Request::factory()
is called with no parameters then a Request
instance is created for the request URI, and if it is the main request then some HTTP-related static properties of the Request
class are also populated (e.g. $client_ip
, $user_agent
etc).In
Request::__construct($uri, $cache)
it is determined if the request is internal or external (based on the $uri
, and a Request_Client
instance is created for the the request. The client can be external (Request_Client_External
) if the URI is a valid URL (it contains the '://' string) or internal otherwise (Request_Client_Internal
). If it is an internal request then routing is also done here: a matching route is searched, and the route parameters are extracted from the URI (or an exception is thrown if no matching route found). The abstract class Request_Client
represents something that can start a request, or that can be a client of a request in other words. Its two subclasses implement the way how the request execution should be done.An external client can execute an external HTTP request three ways:
- using the PECL HTTP extension if it's loaded
- using the cURL extension if it's loaded
- usings streams if both above methods could not be done
An internal client provides the way of execution how it is done by KO3.0
Request
class. It picks the route parameters from it's request, instantiates a Controller
class and calls it's required action method. If you are not familiar with Kohana's routing mechanism then get started with it. Your action method is called in your controller instance, and this is where your job comes. Your controller knows what request instance it belongs to, and what response instance should be populated by it (these objects can be accessed by
$this->request
and $this->response
in the action methods). The methods of the request instance should be called to read the request information and the response instance should be used to build the output. To make it easier to understand here is a hello world-like action method:application/classes/controller/welcome.php:
class Controller_Welcome extends Controller { public function action_index() { $name = Arr::get($this->request->query(), 'name', 'Guest'); $view = View::factory('hello')->set('name', $name); $this->response->body($view); } }
application/view/hello.php:
hello <?php echo $name ?>!We read the array guery parameters (GET parameters) using
$this->request->query()
, pick the 'name'
value from it if this key exists, otherwise we use the default 'Guest'
. We create a new View object that uses our template hello.php
and pass a parameter called 'name'
to it. Then finally we set this View
instance as the body of the response.Note that KO3.1 uses the jQuery-style getter-setter methods for properties and it uses public attributes in less cases. For example
Response::body()
returns the $_body
attribute of the Response
object if it's called with no parameters, otherwise it sets it's value to the passed parameter.So this is how internal and external requests work. The main difference related to KO3.0 is that in 3.1 we have separate classes to execute internal and external requests. In 3.0 we have only one type of request, and if we want to send a HTTP request to an external host, the we create a request and execute the external request using the
Remote
class which is a wrapper around cURL. Now the Remote
class is gone, there are external requests and there is no need to create a new controller/action for executing them. In KO3.1 we also have a response class which separates the request and response data in a more clear way.in this post many terms and many classes occured, so let's clean them up and describe the responsibility of the classes:
Request
: it represents a node in the HMVC requeest hierarchy. It can be the initial or the current request. It can be internal or external, it's decided at request creation and it has an appropriate client instance.Request_Client_External
: the type of client that should execute a request to an external host. It uses PECL HTTP, cURL or streams to execute the request.Request_Client_Internal
: a client that is able to execute an internal request. It gets routing information from the Request
it belongs to, instantiates the required controller and calls it's action method.Controller
: a controller is a class that is instantiated by an internal client. It can read request information using it's $request
property and it should create the response by setting the attributes of it's $response
property. No echo
here. The $request
and $response
properties are injected by the internal client.Response
: the response of a request. The response of subrequests are returned to their parent request and the response of the main request is sent back to the browser. It holds response headers, response body, cookies, etc.
No comments:
Post a Comment