Class loading

1. Introduction

As of version 2.0 the APF natively supports PHP namespaces. Following the namespace concept of prior versions all resources are addressed absolutely and including it's namespaces. This applies both to PHP classes, HTML templates, and configuration files as well as to SQL statement resources. Depending on the type direct and absolute addressing according to the PHP namespace pattern (e.g. (Document-)Controller) or addressing separated by namespace and file (e.g. Templates) applies.

The ClassLoader implementation is based on the PSR-0 PHP standard. The document describes the construction of namespaces of PHP classes that is also applied to templates and configurations within the Adventure PHP Framework.

The following table shows the components of the APF and how they are defined concerning it's location:

Type Namespace Name
PHP classes (controller, tags, ...) \{Vendor name}\{Namespace}\ {Class name}
HTML template \{Vendor name}\{Namespace}\ {Template name}
Configuration files \{Vendor name}\{Namespace}\ {Configuration file name}

The APF implementation of the PSR-0 requires all application components to be addressed in absolute mode. Using this rule, you are able to load PHP classes, templates and configuration files from different base folders according to their vendor name.

For this reason, PHP classes must always be stated fully-qualified (e.g. APF\core\loader\StandardClassLoader) and other components such as templates at least require to have a fully-qualified namespace (e.g. APF\modules\usermanagement\pres\templates).

The following chapters describe the components that are delivered with the APF.

2. RootClassLoader

The RootClassLoader is the central component of the APF that manages the ClassLoader implementations. It's static interface offers the capability to register different ClassLoader with different vendor names and thus load PHP classes, templates and configuration files from different base folders. Due to this mechanism you are enabled to separate framework and application code as well as productive code from test artefacts by vendor name.

A ClassLoader is defined by the following interface:

PHP code
interface ClassLoader { public function load($class); public function getVendorName(); public function getRootPath(); public function getConfigurationRootPath(); }

As you can take from the interface every ClassLoader knows about it's vendor name. The vendor name is the first section of the namespace or the fully-qualified name of a PHP class. This allows to determine which template, configuration file, or PHP class is loaded by which ClassLoader.

Loading classes is done by the respective ClassLoader directly using the vendor name. The standard implementation of the APF - the StandardClassLoader - exclusively loads classes that refer to the vendor name it is registered with. This enables you to easily register multiple ClassLoader instances for different vendors and with different base folders.

Loading templates and configuration files is also done using the registered ClassLoaders, but here they only provide the base path. The base path is then used to calculate the complete file path to load templates (getRootPath()) and configuration files (getConfigurationRootPath()) from different base paths.

The APF class loading mechanism is fully compatible to other frameworks/tools since the RootClassLoader is registered by the spl_autoload_register() function and thus uses the PHP standard.

3. StandardClassLoader

The StandardClassLoader is the standard implementation of the ClassLoader interface. He is responsible for loading classes that are referring to a dedicated vendor name.

To configure the class loader, the constructor takes the desired vendor name as well as the base path for code and configuration. The third argument (base path of configuration files) is optional and is initialized with the value of the code bas path in case it is omitted. Example:

PHP code
use APF\core\loader\RootClassLoader; use APF\core\loader\StandardClassLoader; $classLoader = new StandardClassLoader('ACME', '/acme/src'[, '/acme/conf']); RootClassLoader::addLoader($classLoader);

In case you intend to separate code of different vendors you can configure multiple StandardClassLoaders with different vendor names and base paths.

In contrast, using this mechanism can also be used to combine sources from different vendors into one single base path. This can be done by registering multiple StandardClassLoader with different vendor names but the same base path.

Another scenario is the separation of code and configuration for one vendor. Using the two base paths for classes and templates (getRootPath()) as well as configurations (getConfigurationRootPath()) you are enabled to separate code fragments from configuration.

The base path of the StandardClassLoader registered for the vendor APF registered within the APF/core/bootstrap.php can be manipulated by the bootstrap file of your application. For this reason, please set the $apfClassLoaderRootPath variable according to your environment.

Besides, this mechanism can be used to separate APF code from it's configuration. For this reason, please set the $apfClassLoaderConfigurationRootPath variable according to your environment before inclusion of the APF/core/bootstrap.php file.

Details can be taken from chapter 4.

Mapping of vendor name or namespace and type (e.g. PHP class) respectively to a dedicated file name is done by the ClassLoader implementation. The standard implementation of the APF acts as follows:

Namespace/fully-qualified name Name File path
ACME\application\pres\controller\DoSomethingController - /acme/src/application/pres/controller/DoSomethingController.php
ACME\application\pres\templates something.html /acme/src/application/pres/templates/something.html
ACME\biz\factory goods.ini /acme/src/config/biz/factory/{CONTEXT}/{ENVIRONMENT}_goods.ini

Please note that mapping of configuration files highly depends on the ConfigurationProvider implementation. Details can be taken from Configuration.

4. Usage

The subsequent chapters describe how you can adapt the ClassLoader mechanism to load PHP classes, templates, and configuration files and how to use it within your application.

4.1. Adaption of the base path

Each ClassLoader is initialized with the base path that is set for loading PHP classes, templates, and configuration files according to the respective vendor. The StandardClassLoader for the vendor APF is initialized within the APF/core/bootstrap.php that is included within your application's bootstrap file. The following code box shows a typical index.php:

PHP code
include('./APF/core/bootstrap.php'); use APF\core\frontcontroller\Frontcontroller; /* @var $fC Frontcontroller */ $fC = Singleton::getInstance(Frontcontroller::class); echo $fC->start('...', '...');

Without further configuration, the framework evaluates the path to the bootstrap.php file and uses it's parent folder - the folder where core, extensions, modules, and tools reside - as the base path for initializing the StandardClassLoader. Vendor name for all APF components is APF.

In case you are using symlinks or your code base delivers multiple projects you can adapt the base path by the $apfClassLoaderRootPath variable right before including the bootstrap.php file. Your index.php changes as follows:

PHP code
$apfClassLoaderRootPath = '/path/to/apf/src'; include('./APF/core/bootstrap.php'); use APF\core\frontcontroller\Frontcontroller; /* @var $fC Frontcontroller */ $fC = Singleton::getInstance(Frontcontroller::class); echo $fC->start('...', '...');

PHP classes, templates, and configuration files for vendor APF are now loaded from /path/to/apf/src.

4.2. Adaption of the configuration base path

Besides the base path for classes and templates each ClassLoader can be applied a base path for configuration files. The StandardClassLoader for vendor APF is also initialized within APF/core/bootstrap.php that in turn is included in the bootstrap file of your application.

Without further configuration, the framework evaluates the path to the bootstrap.php file and uses - as described in chapter 4.1 - it's parent folder - as the base path for initializing the StandardClassLoader. Vendor name for all APF components is APF.

In case you intend to customize the base path for configurations along with the folder for classes and templates you can adapt the base path by the $apfClassLoaderConfigurationRootPath variable right before including the bootstrap.php file. Your index.php changes as follows:

PHP code
$apfClassLoaderRootPath = '/path/to/apf/src'; $apfClassLoaderConfigurationRootPath = '/path/to/apf/config' include('./APF/core/bootstrap.php'); use APF\core\frontcontroller\Frontcontroller; /* @var $fC Frontcontroller */ $fC = Singleton::getInstance(Frontcontroller::class); echo $fC->start('...', '...');

PHP classes and templates for vendor APF are now loaded from /path/to/apf/src, configuration files are expected to be located under /path/to/apf/config by the appropriate ConfigurationProvider implementations.

4.3. Registration of ClassLoaders

In order to use further ClassLoaders they must be registered within the bootstrap file of your application. For this reason, you can use the RootClassLoader::addLoader() method that takes a ClassLoader instance as it's argument. You can thus use the implementation of the APF (StandardClassLoader) or a custom implementation.

The following code block registers the StandardClassLoader using a custom vendor name:

PHP code
include('./APF/core/bootstrap.php'); use APF\core\loader\RootClassLoader; use APF\core\loader\StandardClassLoader; RootClassLoader::addLoader(new StandardClassLoader('ACME', '/acme/src'));

In order to separate configuration from code you may want to use the third argument of StandardClassLoader's constructor to specify a separate base path for configurations files. Your index.php changes as follows:

PHP code
include('./APF/core/bootstrap.php'); use APF\core\loader\RootClassLoader; use APF\core\loader\StandardClassLoader; RootClassLoader::addLoader(new StandardClassLoader('ACME', '/acme/src', '/acme/config'));

Configuration files of vendor ACME are now loaded from /acme/config.

In case the shipped implementation does not meet your requirements you can easily create a custom ClassLoader implementation. The following example contains an implementation that loads classes containing vendor, namespace, and class name like ACME_Name_Space_ClassName:

PHP code
namespace ACME\loader; use APF\core\loader\ClassLoader; class ExplicitClassNameClassLoader implements ClassLoader { private $vendorName; private $rootPath; public function __construct($vendorName, $rootPath) { $this->vendorName = $vendorName; $this->rootPath = $rootPath; } public function load($class) { // loads classes that are including vendor, namespace, and name within // the class name directly, e.g. ACME_Foo_Bar_ClassName. if (strpos($class, $this->vendorName . '_') !== false) { $file = $this->rootPath . '/' . str_replace( $this->vendorName . '_', '', str_replace('_', '/', $class) ); include($file); } } public function getVendorName() { return $this->vendorName; } public function getRootPath() { return $this->rootPath; } public function getConfigurationRootPath() { // Assumes that classes/templates and configuration files // reside within the same folder! May be different within // your implementations. return $this->rootPath; } }

Usage is as follows:

PHP code
include('./APF/core/bootstrap.php'); use APF\core\loader\RootClassLoader; use ACME\loader\ExplicitClassNameClassLoader; RootClassLoader::addLoader(new ExplicitClassNameClassLoader('ACME', '/acme/src'));

4.4. Loading application components

Loading classes is directly done by the RootClassLoader. In case you want to place application components such as font files within the folder structure of your application and include them easily, you can use the capabilities of the RootClassLoader.

The subsequent code sample loads a font file that resides in parallel to the ImageHeadlineGenerator class and that is intended to create image-based headlines:

PHP code
namespace ACME\components\pres; use APF\core\loader\RootClassLoader; class ImageHeadlineGenerator { private $fontName; private $fontSize; public function __construct($fontName, $fontSize) { $this->fontName = $fontName; $this->fontSize = $fontSize; } public function generateHeadline($text) { $img = imagecreate($this->getWidth($text), $this->getHeight($text)); // The RootClassLoader is able to determine the respective class loader for the // current namespace. The class loader instance then delivers the root path. $loader = RootClassLoader::getLoaderByNamespace(__NAMESPACE__); $rootPath = $loader->getRootPath(); $currentNamespace = RootClassLoader::getNamespace(__NAMESPACE__); // Assuming that the fonts reside side-by-side with this class, // this is the path of the font to load: $font = $rootPath . '/' . str_replace('\\', '/', $currentNamespace) . '/' . $this->fontName . '.ttf'; imagettftext($img, $this->fontSize, 0, 10, 10, 0, $font, $text); imagepng($img); } private function getWidth($text) { ... } private function getHeight($text) { ... } }

The code sample uses getLoaderByNamespace() to retrieve the ClassLoader responsible for the current vendor. The ClassLoader then returns it's current base path that is used to load PHP classes, templates and configuration files.

Moreover, the RootClassLoader is used to extract the base namespace without vendor name from the fully-qualified namespace. Both parts - base path and namespace path - together then form the file path of the font file.

4.5. Usage of the RootClassLoader

Besides the methods listed in chapter 4.3. the RootClassLoader provides further methods that can be used to create file paths or load application components.

The following list includes the provided methods as well as their use cases:

Method Use case
getLoaderByVendor() Returns the ClassLoader instance that was registered for the applied vendor.
getLoaderByNamespace() Returns the ClassLoader instance that refers to the applied namespace.
getLoaderByClass() Returns the ClassLoader instance that refers to the applied fully-qualified PHP class name.
getClassName() Returns the class name (e.g. RootClassLoader) of a fully-qualified class (e.g. APF\core\loader\RootClassLoader).
getNamespace() Returns the namespace (e.g. APF\core\loader) of a fully-qualified class (e.g. APF\core\loader\RootClassLoader).
getNamespaceWithoutVendor() Works like getNamespace(), but ommits the leading vendor name (e.g. core\loader).
getVendor() Returns the vendor name (e.g. APF) of a fully-qualified class (e.g. APF\core\loader\RootClassLoader).

Comments

Do you want to add a comment to the article above, or do you want to post additional hints? So please click here. Comments already posted can be found below.
There are no comments belonging to this article.