Yii 1.1: Understanding Autoloading, Helper Classes and Helper Functions

24 followers

Many Yii users ask how to create helper classes and functions, and though there are numerous approaches spread out among the forum and wiki articles, this Tutorial tries to bring it all together in one place.

Creating helper classes

You might wish to create helper classes for several reasons:

  • to extend and customize existing Yii classes
  • to create classes to implement business objects not covered by Model objects
  • to reuse helpful classes from other projects

Yii handles this particularly efficiently by smart use of Autoloading; when an unknown class is used, Yii automatically loads a file of the same name, but with a .php extension.

One only need create a class file in the components/ directory, define the class itself, and Yii will find it:

<?php
// protected/components/MyClass.php
 
class MyClass {
    ...
}

Then, if the class name MyClass is mentioned (including the use of static references), Yii loads it automatically.

IMPORTANT: Autoloading requires that the class name and the filename match exactly in spelling and case; being off just a little bit will produce an error. If you define class MyClass in the file `Myclass.php, you will receive the error:

PHP ERROR
include(MyClass.php): failed to open stream: No such file or directory

Rename the file to MyClass.php, matching the class name, to fix this error (unless this is an autoload path issue - see final section).

Note: Because Windows filesystems maintain but do not respect case, misspellings like this may still work, but the application will break if ported to Linux/UNIX.

Creating helper functions

Almost all experienced PHP programmers have written small helper or utility functions, ones that are essentially standalone (not related to any particular objects), with string functions being particularly popular.

Though it's possible to create your helpers as static functions inside an autoloaded class:

<?php
// protected/components/Helpers.php       NOT RECOMMENDED!
 
class Helpers {
 
    // does the first string start with the second?
    public static function startsWith($haystack, $needle)
    {
        return strpos($haystack, $needle) === 0;
    }
 
    ...

and then use it with:

...
    if ( Helpers::startsWith(...) )

but this is tedious: it's much more convenient to use regular ordinary functions to do this, and it's easy to achieve in Yii (though it does not involve the autoloader).

1. Create helpers function file

I usually define helpers in protected/components/helpers.php:

<?php
// protected/components/helpers.php
 
function startsWith($needle, $haystack)
{
    ...
}
...

2. "Require" the helper file in the config file

Now that the helpers file is in place, we need a place to include it early in the application so that it's available throughout.

Though Qiang recommends putting it in webroot/index.php, I recommend putting it in the protected/config/main.php file instead, mainly because I usually want to apply the same treatment in protected/config/console.php for console-mode applications. Doing the same change in the same way feels cleaner.

Add the require_once directive near the top of the file so that the config file itself can get the benefit of your helper functions.

<?php
// protected/config/main.php
 
require_once( dirname(__FILE__) . '/../components/helpers.php');
 
...

This done, any part of the application can use the helper functions defined here:

if (startsWith(...))

Note that Qiang has written about this topic as well, though from a somewhat different angle: Qiang's wiki article

Setting/Verifying the Autoload Path

Unlike other frameworks that actually preload your runtime classes — which can be a substantial performance hit — Yii performs autoloading only when the classes are needed.

You define the locations to autoload from in your configuration file in the import section:

// protected/config/main.php
 
return array(
    ...
    'import' => array(
        'application.models.*',
        'application.components.*'
    ),
    ...

Yii notes all the files found in these directories and associates them with classes of the same name. If one of the classes is later needed, the file is included directly, which provides the class to the application.

Some notes about autoloading:

  1. application. is an alias for your protected/ directory
  2. None of these files is actually loaded by PHP unless the contained classes are actually requested by the application; there is very low overhead in setting up autoloading
  3. If you wish to use the same autoload paths in console applications, set the same imports in protected/config/console.php
  4. Autoloading doesn't search subdirectories: if you want to autoload the protected/components/mystuff/ folder, use 'application.components.mystuff.*' in addition to 'application.components.*'

Multiple classes in one file

It's possible to include more than one class inside a file, though this is only useful in some narrow circumstances.

Autoloading is only done when the desired class name matches the filename, but if a file contains a "main" class used by an application, but it in turn defines minor helper classes that the application won't call directly, this is perfectly legitimate.

Since the application only requests the primary class, that file is autoloaded upon use, but everything in a file is loaded into PHP during include time. This brings the support functions along with it even though the application may be unaware of these goings-on.

Manual autoloads

The usual autoload path does not pull from the zii framework hierarchy, which is why full application paths must be used when requesting widgets:

$this->widget('zii.widgets.grid.CGridView', array(
   ...

This tells Yii to find the file via a specific application path, which is typically from within the framework source code directory itself.

But if one wishes to extend the CGridView class, we have to take the step of importing that specific name before we can use it:

<?php
// protected/components/MyGridView.php
 
Yii::import('zii.widgets.grid.CGridView');
 
class MyGridView extends CGridView {
    ...

Curiously, the import() statement does not actually load the file: instead, it sets up an autoload so that the provided filename will be loaded as soon as CGridView is referenced, which it is just two lines later.

This means that any application can now use MyGridView the same as any other class, without an application path:

$this->widget('MyGridView', array(
...

Total 4 comments

#6905 report it
oldblues10 at 2012/02/12 11:00am
Caution in using directory includes

It is better to include the specific path the the class instead of the directory. application.path.directory.* is much slower than including each specific path to the class in the import. I made a post about it here: http://zurmo.org/features/major-performance-improvement

#3244 report it
Steve Friedl at 2011/03/28 10:04am
components is already imported

@abajja - the applications.components.* directory is already imported by the default config file, so you can indeed just create a class file there and Yii will use it.

#3240 report it
elbek at 2011/03/28 06:12am
CI helpers

it is the same with CI helpers.

#3239 report it
abajja at 2011/03/28 06:00am
Some observations
  1. I still do not understand what you mean by the helper classes.
  2. This sentence

One Only Need Create A Class File In The Components/ Directory, Define The Class Itself, And Yii Will Find It:

is inaccurate or at least can lead to ambiguities. Should disambiguate by adding import .../components.

Leave a comment

Please to leave your comment.

Write new article