Categories


Archives


Recent Posts


Categories


Magento 2: An Abstract Operating System for Running PHP Applications

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Updated for Magento 2! No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

This is quick, high level, possibly incorrect, overview of how Magento 2 runs applications, and what the kernel of its system architecture looks like. This is the level above MVC/MVVM, and will only be of interest to folks (like me) who need to know how everything works.

From a high level point of view, Magento 2 is an abstract operating system for creating PHP applications, or what I’m calling “Magento System Applications”.

Magento 2’s top level architecture is relatively simple.

First, your web server (apache, nginx) routes PHP requests to a single PHP script/entry point. This PHP script is responsible for

A Magento system application is a PHP object with a launch method. When the MagentoFrameworkAppBootstrap class runs an application, it ultimately calls this launch method. An application’s launch method is responsible for returning a response object. This response object has no explicit interface, but has an implied interface with a single public method of sendResponse.

After calling the Magento System Application object’s launch method, the MagentoFrameworkAppBootstrap object will call the returned response object’s sendResponse method. This method is responsible for echoing any output to the end user.

The MagentoFrameworkAppBootstrap object provides the Magento System Application with a root path, a set of parameters ($_SERVER variables), and an initialized object manager. This object manager is initialized with every module’s etc/di.xml files.

Magento System Application developers have the option of setting an area on the application during launch. Setting an area will ensure requests for configuration trees include files located in

app/etc/[area]/*.xml

If Magento System Application developers want their object manager to include files from the specific area configuration, they’ll need to load that configuration into the object separately (as the MagentoFrameworkAppBootstrap object already loads the default etc/di.xml when it provides the instantiated object manager)

Example: Main Application Entry Point

The first obvious example is Magento’s main index.php entry point.

First, Magento’s .htaccess ensures everything is sent to index.php

############################################
## never rewrite for existing files, directories and links

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l

############################################
## rewrite everything else to index.php

    RewriteRule .* index.php [L]

</IfModule>
############################################

Then, in index.php

try {
    require __DIR__ . '/app/bootstrap.php';
} catch (Exception $e) {
    //...
    exit(1);
}

$bootstrap = MagentoFrameworkAppBootstrap::create(BP, $_SERVER);
/** @var MagentoFrameworkAppHttp $app */
$app = $bootstrap->createApplication('MagentoFrameworkAppHttp');
$bootstrap->run($app);

We see the app/bootstrap.php file required in.

Then the script creates a MagentoFrameworkAppBootstrap object. Then it creates a MagentoFrameworkAppHttp object using the createApplication method, and then runs the Magento System Application object by passing it in to the Bootstrap object’s run method.

In the application object’s launch method, we see the top level code that implements Magento 2’s MVC/MVVM system, including setting the application area, re-configuring the object manager so it includes area specific di.xml, and returning a response object.

#File: vendor/magento/framework/App/Http.php
public function launch()
{
    $areaCode = $this->_areaList->getCodeByFrontName($this->_request->getFrontName());
    $this->_state->setAreaCode($areaCode);
    $this->_objectManager->configure($this->_configLoader->load($areaCode));
    /** @var MagentoFrameworkAppFrontControllerInterface $frontController */
    $frontController = $this->_objectManager->get('MagentoFrameworkAppFrontControllerInterface');
    $result = $frontController->dispatch($this->_request);
    // TODO: Temporary solution until all controllers return ResultInterface (MAGETWO-28359)
    if ($result instanceof ResultInterface) {
        $this->registry->register('use_page_cache_plugin', true, true);
        $result->renderResult($this->_response);
    } elseif ($result instanceof HttpInterface) {
        $this->_response = $result;
    } else {
        throw new InvalidArgumentException('Invalid return type');
    }
    // This event gives possibility to launch something before sending output (allow cookie setting)
    $eventParams = ['request' => $this->_request, 'response' => $this->_response];
    $this->_eventManager->dispatch('controller_front_send_response_before', $eventParams);
    return $this->_response;
}   

Example: Serving Static Asset Files

Another example is how Magento 2 serves static asset files. A static asset file is a CSS, Javascript, etc. file whose URL takes the form.

http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css
http://magento.example.com/pub/static/frontend/Magento/luma/en_US/mage/calendar.css

First, if we look at the pub/static folder’s .htaccess file

#File: pub/static/.htaccess
<IfModule mod_rewrite.c>
    RewriteEngine On

    # Remove signature of the static files that is used to overcome the browser cache
    RewriteRule ^version.+?/(.+)$ $1 [L]

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-l

    RewriteRule .* ../static.php?resource=$0 [L]
</IfModule>    

We see all requests are rewritten to the pub/static.php file. We also see the original path is passed as a resource query string parameter.

If we take a look at pub/static.php

#File: pub/static.php
require __DIR__ . '/../app/bootstrap.php';
$bootstrap = MagentoFrameworkAppBootstrap::create(BP, $_SERVER);
/** @var MagentoFrameworkAppStaticResource $app */
$app = $bootstrap->createApplication('MagentoFrameworkAppStaticResource');
$bootstrap->run($app);

We, again, first see that bootstrap.php gets required in.

Then, the script creates a MagentoFrameworkAppBootstrap object.

Then, the script uses the MagentoFrameworkAppBootstrap object to create a Magento System Application object – this time a MagentoFrameworkAppStaticResource object.

Then, same as before, the MagentoFrameworkAppBootstrap object runs the application object.

If we take a look at this application object’s launch method

#File: vendor/magento/framework/App/StaticResource.php
public function launch()
{
    // disabling profiling when retrieving static resource
    MagentoFrameworkProfiler::reset();
    $appMode = $this->state->getMode();
    if ($appMode == MagentoFrameworkAppState::MODE_PRODUCTION) {
        $this->response->setHttpResponseCode(404);
    } else {
        $path = $this->request->get('resource');
        $params = $this->parsePath($path);
        $this->state->setAreaCode($params['area']);
        $this->objectManager->configure($this->configLoader->load($params['area']));
        $file = $params['file'];
        unset($params['file']);
        $asset = $this->assetRepo->createAsset($file, $params);
        $this->response->setFilePath($asset->getSourceFile());
        $this->publisher->publish($asset);
    }
    return $this->response;
}   

We see a slightly simpler launch. This method still returns a response object, but instead of kicking off a MVC/MVVM front controller, this object uses the passed in resource variable to find the static asset file, and serve it. You can also see the launch method parses the area name (frontend, adminhtml) from that path, before setting it for the application and re-configuring the object manager

$path = $this->request->get('resource');
$params = $this->parsePath($path);
$this->state->setAreaCode($params['area']);
$this->objectManager->configure($this->configLoader->load($params['area']));

The MagentoFrameworkAppStaticResource Magento System Application is what allows us to store our static assets in our module folders, but still have them load from public URLs.

Copyright © Alan Storm 1975 – 2019 All Rights Reserved

Originally Posted: 29th November 2015