Deep lifecycle

Let's take a deep dive on a typical request lifecycle.

To deeper understand the lifecycle of a request, we'll use the same example as in the Lifecycle section, but this time we'll stop and see with greater detail everything that happens behind the scenes, this will give you a better understanding of the Cherrycake architecture.

If you'd rather prefer to start working with Cherrycake and skip this section about the inner workings of Cherrycake, go straight to the Getting started guide, or keep on learning about the Cherrycake architecture in the Performance section.

When a request is received, the index.php file in your App's public directory is executed. This is the entry point for all requests to your Cherrycake application, and all it does is loading Cherrycake, initialize it and call the Engine::attendWebRequest method. This looks something like this:

namespace CherrycakeApp;
require "vendor/tin-cat/cherrycake-engine/load.php"

$e = new \Cherrycake\Engine;

if ($e->init(__NAMESPACE__, [
    "baseCoreModules" => ["Actions"]
]))
    $e->attendWebRequest();

$e->end();

When the engine is initialized with Engine::init, it loads and initializes the modules specified in baseCoreModules. Since the Actions modules is the one in charge of receiving and handling requests, you should at least specify this module on the list.

The default value for the baseCoreModules key is ["Actions"], so if you only need the Actions module like in our example, you can skip this key on the hash array and it will be included automatically. In the example, this means we can simplify the Engine::init line to just $e->init(__NAMESPACE__)

During its initialization, the Actions module loops through all available modules and asks them to map whatever actions they might need. It does so by using the Engine::callMethodOnAllModules method, which goes through all the available modules and executes the specified static method name, like this:

$e->callMethodOnAllModules("mapActions");

To optimize performance, the method Engine::callMethodOnAllModules caches the information about which methods are available in modules so it doesn't need to search for those methods each time a request is made.

All mapActions methods found in any of the available modules (both core and app modules) are executed, so any module that needs to map an action to respond to requests must do it so on its mapActions static method by calling the Actions::mapAction method. In our Home example module, this would look like this:

public static function mapActions() {
	global $e;
	$e->Actions->mapAction(
		"homePage",
		new \Cherrycake\Action([
			"moduleType" => \Cherrycake\ACTION_MODULE_TYPE_APP,
			"moduleName" => "Home",
			"methodName" => "homePage",
			"request" => new \Cherrycake\Request([
				"pathComponents" => false,
				"parameters" => false
			])
		])
	);
}

Now that we have all the possible actions mapped, the call to Engine::attendWebRequest in index.php asks the Actions module to find and run the action that matches the current request URI. This is how this request to the Actions module looks internally:

$this->Actions->run($_SERVER["REQUEST_URI"]);

Since the browser in our example has requested the root page of our website, the Actions module searches all the mapped actions for one that matches the current "/" request, and finds the action named "homePage".

Notice that this action matches our example request of the home page (/ path) because it specifically has no pathComponents

In the declaration of this Action the moduleName and methodName keys are used to specify which module::method should be called when the action is executed. In our example, Home::homePage.

Cherrycake provides a request-level cache. At this point, if the requested Action has been cached, the result is obtained from cache and the execution ends here.

The Home module will use the Patterns module to retrieve an HTML file and send it back to the browser, this is why this dependency is specified on the dependentCoreModules property of Home, like this:

var $dependentCoreModules = [
    "Patterns"
];

Now Home::homePage uses the method Patterns::out to send the HTML file to the browser, like this:

function homePage() {
    global $e;
    $e->Patterns->out("Home/Home.html");
    return true;
}

In turn, Patterns depends on the Output module, which was loaded and initialized automatically as soon as the chain of dependencies started, when our Home module was loaded.

Since Patterns is actually a parser, it not only loads the HTML file, but also parses it using Patterns:parse and then sends the result as a ResponseTextHtml object to Output::setResponse, like this:

$e->Output->setResponse(new \Cherrycake\ResponseTextHtml([
			"code" => $code,
			"payload" => $this->parse($patternName, $setup)
]));

When the execution is about to end, the Engine calls the end method on all loaded modules. The Output calls Output::sendResponse on its end method, causing the parsed HTML file to be sent to the browser and concluding the request lifecycle.

Last updated