Sometimes to write a custom autoloader, you need to dig into another framework's source code. An example of this is the Kohana framework. In this recipe, we will handle image resizing by using one of the Kohana classes.
yiic webapp
.http://dev.kohanaframework.org/projects/kohana3/files
In this recipe, we have used Version 3.1.5.
system
and modules
directories to protected/vendors/Kohana
.Carry out the following steps:
protected/controllers/ImageController.php
as follows:<?php class ImageController extends CController { public function actionIndex() { $image = new Image_GD(Yii::getPathOfAlias("system")."/yii-powered.png"); $image->resize(800, 150); Yii::app()->request->sendFile("image.png", $image->render()); } }
image/index
and you will get the following error:protected/components/EKohanaAutoloader.php
as follows:<?php class EKohanaAutoloader { /** * @var list of paths to search for classes. * Add full paths to modules here. */ static $paths = array(); /** * Class autoload loader. * * @static * @param string $className * @return boolean */ static function loadClass($className) { if(!defined("SYSPATH")) define("SYSPATH", Yii::getPathOfAlias("application.vendors.Kohana.system")); if(empty(self::$paths)) self::$paths = array(Yii::getPathOfAlias("application.vendors.Kohana.system")); $path = 'classes/'.str_replace('_', '/', strtolower($className)).'.php'; foreach (self::$paths as $dir) { if (is_file($dir."/".$path)) require $dir."/".$path; } return false; } }
index.php
. Replace the following line:Yii::createWebApplication($config)->run();
with the following:
$app = Yii::createWebApplication($config); // adding custom Kohana autoloader Yii::import("application.components.EKohanaAutoloader", true); EKohanaAutoloader::$paths = array(Yii::getPathOfAlias("application.vendors.Kohana.modules.image")); Yii::registerAutoloader(array('EKohanaAutoloader','loadClass'), true); $app->run();
image/index
again and you should see a screen similar to the one shown in the following screenshot instead of an error:That means Kohana classes were loaded successfully.
Kohana 3 relies on autoloading and has a very special naming convention. As a result, calling its classes directly is too much work and creating an autoloader is the only reasonable way to implement it if we are not modifying Kohana classes.
We will take a look at the Kohana autoloader, which is present at the following location:
protected/vendors/Kohana/system/classes/kohana/core.php
The method name is auto_load
.
public static function auto_load($class) { try { // Transform the class name into a path $file = str_replace('_', '/', strtolower($class)); if ($path = Kohana::find_file('classes', $file)) { // Load the class file require $path; // Class has been found return TRUE; } // Class is not in the filesystem return FALSE; } catch (Exception $e) { Kohana_Exception::handler($e); die; } }
From this part, we can say that it uses a class to form a relative path, which is then used to find a file inside of the classes
directory:
$file = str_replace('_', '/', strtolower($class));
Now let's go deeper inside find_file
:
public static function find_file($dir, $file, $ext = NULL, $array = FALSE) { if ($ext === NULL) { // Use the default extension $ext = EXT; } elseif ($ext) { // Prefix the extension with a period $ext = ".{$ext}"; } else { // Use no extension $ext = ''; } // Create a partial path of the filename $path = $dir.DIRECTORY_SEPARATOR.$file.$ext; if (Kohana::$caching === TRUE AND isset(Kohana::$_files[$path.($array ? '_array' : '_path')])) { // This path has been cached return Kohana::$_files[$path.($array ? '_array' : '_path')]; } if (Kohana::$profiling === TRUE AND class_exists('Profiler', FALSE)) { // Start a new benchmark $benchmark = Profiler::start('Kohana', __FUNCTION__); } if ($array OR $dir === 'config' OR $dir === 'i18n' OR $dir === 'messages') { // Include paths must be searched in reverse $paths = array_reverse(Kohana::$_paths); // Array of files that have been found $found = array(); foreach ($paths as $dir) { if (is_file($dir.$path)) { // This path has a file, add it to the list $found[] = $dir.$path; } } } else { // The file has not been found yet $found = FALSE; foreach (Kohana::$_paths as $dir) { if (is_file($dir.$path)) { // A path has been found $found = $dir.$path; // Stop searching break; } } } if (Kohana::$caching === TRUE) { // Add the path to the cache Kohana::$_files[$path.($array ? '_array' : '_path')] = $found; // Files have been changed Kohana::$_files_changed = TRUE; } if (isset($benchmark)) { // Stop the benchmark Profiler::stop($benchmark); } return $found; }
As we know that our file extension is always .php
and the directory is always classes
, and as we don't care about the caching or profiling right now, the useful part is as follows:
$path = $dir.DIRECTORY_SEPARATOR.$file.$ext; foreach (Kohana::$_paths as $dir) { if (is_file($dir.$path)) { // A path has been found $found = $dir.$path; // Stop searching break; } }
We are pretty close! The only thing left is Kohana::$_paths
:
/** * @var array Include paths that are used to find files */ protected static $_paths = array(APPPATH, SYSPATH);
We don't care about the application, so we can omit the APPPATH
part. Moreover, SYSPATH
is a path to the system
directory. As most of the Kohana classes are there, it is reasonable to make this a default.
When the autoloader class is ready, we use Yii::registerAutoloader
in index.php
to register it. It is important to register the autoloader after the default one built in Yii, so we pass true
as the second parameter value of Yii::registerAutoloader
. Our image
class is not in the core and is located in the image
module, so we set paths to the image
module path in the following way:
EKohanaAutoloader::$paths = array(Yii::getPathOfAlias("application.vendors.Kohana.modules.image"));
As image resizing is a common task, it is better from both reusability and performance perspectives to separate this task from the rest of the application and create a separate PHP script that will handle the image resizing. For example, it will allow using the following code:
<img src="/image.php?src=avatar.png&size=s" />
This means, take avatar.png
as the source image and resize it to 100 x 100 pixels. Possible steps the image.php
script will take are as follows:
In order to achieve better performance, you can configure the web server to serve existing images directly, avoid serving with a PHP script, and redirecting the non-existing ones to the processing script.
In order to learn more about Yii autoloading and Kohana, refer to the following URLs:
3.145.74.54