Using Kohana inside Yii

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.

Getting ready

  1. Create a fresh application using yiic webapp.
  2. Download the Kohana framework 3.1.5 archive from the following URL:

    http://dev.kohanaframework.org/projects/kohana3/files

    In this recipe, we have used Version 3.1.5.

  3. Extract the system and modules directories to protected/vendors/Kohana.

How to do it...

Carry out the following steps:

  1. First, we will need the actual code that performs the image resizing and displays an image. Create 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());
      }
    }
  2. Try to run image/index and you will get the following error:
    How to do it...
  3. This means that Yii cannot find Kohana classes. In order to help it, we will need a custom autoloader. So, create 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;
        }
    }
  4. In order to use it, we need to modify 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();
  5. Now run image/index again and you should see a screen similar to the one shown in the following screenshot instead of an error:
    How to do it...

    That means Kohana classes were loaded successfully.

    Note

    Note that the Kohana class loader provided was not optimized in terms of performance and is not intended for intensive production use.

How it works...

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"));

There's more...

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:

  • If an already processed image exists, serve it
  • If there is no image yet, read the source image, resize it, and write it as the processed one

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.

Further reading

In order to learn more about Yii autoloading and Kohana, refer to the following URLs:

See also

  • The Customizing the Yii autoloader recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.74.54