CodeIgniter 1.7 and Objects

0
89
8 min read

Objects confused us, when we started using CodeIgniter. Coming to CodeIgniter through PHP 4, which is a procedural language, and not an object-oriented (OO) language. We duly looked up objects and methods, properties and inheritance, and encapsulation, but our early attempts to write CI code were plagued by the error message “Call to a member function on a non-object”. We saw it so often that we were thinking of having it printed on a T-shirt.

To save the world from a lot of boring T-shirts, this article covers the way in which CI uses objects, and the different ways you can write and use your own objects. Incidentally, we’ve used “variables/properties”, and “methods/functions” interchangeably, as CI and PHP often do. You write “functions” in your controllers, for instance, when an OO purist would call them “methods”. You define class “variables” when the purist would call them “properties”.

Object-oriented programming

We assume that you have basic knowledge of OOP. You may have learned it as an afterthought to “normal” PHP 4. PHP 4 is not an OO language, though some OO functionality has been stacked on to it. PHP 5 is much better, with an underlying engine that was written from the ground up with OO in mind.

You can do most of the basics in PHP 4, and CI manages to do everything it needs internally in either language. The key thing to remember—when an OO program is running, there is always one current object (but only one). Objects may call each other or hand over control to each other, in which case the current object changes, but only one of them can be current at any time. The current object defines the scope, in other words, the variables (properties) and methods (functions) that are available to the program at that moment. So it’s important to know and control the current object.

PHP, being a mixture of functional and OO programming, also offers the possibility where no object is current. You can start off with a functional program, call an object, let it take charge for a while, and then return control to the program. Luckily, CI takes care of this for you.

The CI super-object

CI works by building one super-object—it runs the entire program as one big object, in order to eliminate scoping issues. When you start CI, a complex chain of events occurs. If you set your CI installation to create a log (in /codeigniter/application/config/config.php set $config[‘log_threshold’] = 4; value. This will generate a log file in /www/CI_system/logs/), you’ll see something like this:

1 DEBUG - 2006-10-03 08:56:39 --> Config Class Initialized
2 DEBUG - 2006-10-03 08:56:39 --> No URI present. Default controller
set.
3 DEBUG - 2006-10-03 08:56:39 --> Router Class Initialized
4 DEBUG - 2006-10-03 08:56:39 --> Output Class Initialized
5 DEBUG - 2006-10-03 08:56:39 --> Input Class Initialized
6 DEBUG - 2006-10-03 08:56:39 --> Global POST and COOKIE data
sanitized
7 DEBUG - 2006-10-03 08:56:39 --> URI Class Initialized
8 DEBUG - 2006-10-03 08:56:39 --> Language Class Initialized
9 DEBUG - 2006-10-03 08:56:39 --> Loader Class Initialized
10 DEBUG - 2006-10-03 08:56:39 --> Controller Class Initialized
11 DEBUG - 2006-10-03 08:56:39 --> Helpers loaded: security
12 DEBUG - 2006-10-03 08:56:40 --> Scripts loaded: errors
13 DEBUG - 2006-10-03 08:56:40 --> Scripts loaded: boilerplate
14 DEBUG - 2006-10-03 08:56:40 --> Helpers loaded: url
15 DEBUG - 2006-10-03 08:56:40 --> Database Driver Class Initialized
16 DEBUG - 2006-10-03 08:56:40 --> Model Class Initialized

At start up, that is, each time a page request is received over the Internet—CI goes through the same procedure. You can trace the log through the CI files:

  1. The index.php file receives a page request. The URL may indicate which controller is required, if not, CI has a default controller (line 2). The index.php file makes some basic checks and calls the codeigniter.php file (codeignitercodeigniter.php).
    require_once BASEPATH.'codeigniter/CodeIgniter'.EXT;
  2. The codeigniter.php file instantiates the Config, Router, Input, URL, and other such, classes (see lines 1, and 3 to 9). These are called the base classes—you rarely interact directly with them, but they underlie almost everything CI does.
    /*
    * ------------------------------------------------------
    * Instantiate the base classes
    * ------------------------------------------------------
    */
    $CFG =& load_class('Config');
    $URI =& load_class('URI');
    $RTR =& load_class('Router');
    $OUT =& load_class('Output');
  3. The file codeigniter.php tests to see the version of PHP it is running on, and calls Base4 or Base5 (/codeigniter/Base4.php or codeigniter/Base5.php).
    if (floor(phpversion()) < 5)
    {
    load_class('Loader', FALSE);
    require(BASEPATH.'codeigniter/Base4'.EXT);
    }
    else
    {
    require(BASEPATH.'codeigniter/Base5'.EXT);
    }
  4. The above snippet creates an object—one which ensures that a class has only one instance. Each has a public &get_instance() function. Note the &—this is assignment by reference. So, if you assign using &get_instance() method, it assigns to the single running instance of the class. In other words, it points to the same pigeonhole. So, instead of setting up lot of new objects, you start building one super-object, which contains everything related to the framework.
    function &get_instance()
    {
    return CI_Base::get_instance();
    }
  5. A security check,
    /*
    * ------------------------------------------------------
    * Security check
    * ------------------------------------------------------
    *
    * None of the functions in the app controller or the
    * loader class can be called via the URI, nor can
    * controller functions that begin with an underscore
    */
    $class = $RTR->fetch_class();
    $method = $RTR->fetch_method();
    if ( !class_exists($class)
    OR $method == 'controller'
    OR strncmp($method, '_', 1) == 0
    OR in_array(strtolower($method), array_map('strtolower',
    get_class_methods('Controller')))
    )
    {
    show_404("{$class}/{$method}");
    }
  6. The file, codeigniter.php instantiates the controller that was requested, or a default controller (line 10). The new class is called $CI.
    $CI = new $class();
  7. The function specified in the URL (or a default) is then called and life, as we know it, starts to wake up and happen. Depending on what you wrote in your controller, CI will initialize the classes you need, and “include” functional scripts you asked for. So, in the log, the model class is initialized (line 16). The boilerplate script, which is also shown in the log (line 13), is the one we wrote to contain standard chunks of text. It’s a .php file, saved in the folder called scripts. It’s not a class—just a set of functions. If you were writing pure PHP you might use include or require to bring it into the namespace—CI needs to use its own load function to bring it into the super-object.

The concept of namespace or scope is crucial here. When you declare a variable, array, object, and so on, PHP holds the variable name in its memory and assigns a further block of memory to hold its contents. However, problems might arise if you define two variables with the same name. (In a complex site, this is easily done.) For this reason, PHP has several set of rules. Some of them are as listed:

  • Each function has its own namespace or scope, and variables defined within a function are usually local to it. Outside the function, they are meaningless.
  • You can declare global variables, which are held in a special global namespace and are available throughout the program.
  • Objects have their own namespaces—variables exist inside the object as long as the object exists, and can only be referenced by using the object.

So, $variable, global $variable, and $this->variable are three different things.

Remember, $variable and global $variable can’t be used in the same scope. So, inside a function you will have to decide if you want to use $variable or global $variable.

Particularly before OO, this could lead to all sort of confusions—you may have too many variables in your namespace (so that conflicting names overwrite each other). You may also find that some variables are just not accessible from whatever scope you happen to be.

Copying by reference

You may have noticed the function &get_instance() in the previous section. This is to ensure that, as the variables change, the variables of the original class also change. As assignment by reference can be confusing, so here’s a short explanation. We’re all familiar with simple copying in PHP:

$one = 1;
$two = $one;
echo $two;

The previous snippet produces 1, because $two is a copy of $one. However, suppose you reassign $one:

$one = 1;
$two = $one;
$one = 5;
echo $two;

This code still produces $two = 1, because changes made to $one after assigning $two have not been reflected in $two. This was a one-off assignment of the value that happened to be in variable $one at that time, to a new variable $two. Once that is done, the two variables lead separate lives (in just the same way if we alter $two, $one doesn’t change).

In effect, PHP creates two pigeonholes—called $one and $two. A separate value lives in each. You may, on any occasion, make the values equal, but after that each does its own work. PHP also allows copying by reference. If you add just a simple & to line 2 of the snippet as shown:

$one = 1;
$two =& $one;
$one = 5;
echo $two;

The code now echoes 5, the change we made to $one is reflected in $two. Changing the = to =& in the second line means that the assignment is “by reference”. It looks as if there was only one pigeonhole, which has two names ($one<.i> and $two). Whatever happens to the contents of the pigeonhole is reflected in both $one and $two, as if they were just different names for the same variables.

The principle works for objects as well as simple string variables. You can copy or clone an object using the = operator in PHP 4. Or you can clone keyword in PHP, in which case you make a simple one-off new copy, which then leads an independent life. You can also assign one to the other by reference, so the two objects point to each other. Any changes made to one will also happen to the other. Again, think of them as two different names for the same thing.

LEAVE A REPLY

Please enter your comment!
Please enter your name here