CodeIgniter and Objects

0
225
11 min read

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, I’ve used ‘variables/properties’, and ‘methods/functions’ interchangeably, as CI and PHP often do. You write ‘functions’ in your controllers for instance, when the OO purist would call them ‘methods’. You define class ‘variables’ when the purist would call them ‘properties’.

Object-Oriented Programming

I’m assuming you—like me—have a basic knowledge of OOP, but may have learned it as an afterthought to ‘normal’ PHP 4. PHP 4 is not an OO language, though some OO functionality has been tacked on to it. PHP 5 is much better, with an underlying engine that was written from the ground up with OO in mind.

But 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 is that, when an OO program is running, there is always one current object (but only one). Objects may call each other and hand over control to each other, in which case the current object changes; but only one of them can be current at any one time. The current object defines the ‘scope’—in other words, which variables (properties) and methods (functions) are available to the program at that moment. So it’s important to know, and control, which object is current. Like police officers and London buses, variables and methods belonging to objects that aren’t current just aren’t there for you when you most need them.

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

Working of the CI ‘Super-Object’

CI works by building one ‘super-object’: it runs your whole 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, 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

On startup—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). Index.php makes some basic checks and calls the codeigniter.php file (codeignitercodeigniter.php).
  2.     

  3. The codeigniter.php file instantiates the Config, Router, Input, URL, (etc.) classes (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.
  4.     

  5. codeigniter.php tests to see which version of PHP it is running on, and calls Base4 or Base5 (/codeigniter/Base4(or 5).php). These create a ‘singleton’ 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 to the &get_instance() method, it assigns to the single running instance of the class. In other words, it points you to the same pigeonhole. So, instead of setting up lots of new objects, you are starting to build up one ‘super-object’, which contains everything related to the framework.
  6.     

  7. After a security check, codeigniter.php instantiates the controller that was requested, or a default controller (line 10). The new class is called $CI. 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 then initialize any other classes you need, and ‘include’ functional scripts you asked for. So in the log above, the model class is initialized. (line 16) The ‘boilerplate’ script, on the other hand, which is also shown in the log (line 13), is one I wrote to contain standard chunks of text. It’s a .php file, saved in the scripts folder, but 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, etc., 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 sets of rules. For example:

        

  • Each function has its own namespace or scope, and variables defined within a function are usually ‘local’ to it. Outside the function, these 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 for as long as the object exists, but can only be referenced through the object.

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

Particularly, before OO, this could lead to all sorts of confusion: you may have too many variables in your namespace (so that conflicting names overwrite each other), or you may find that some variables are just not accessible from whatever scope you happen to be in. CI offers a clever way of sorting this out for you.

So, now you’ve started CI, using the URL www.mysite.com/index.php/welcome/ index, which specifies that you want the index function of the welcome controller.

If you want to see what classes and methods are now in the current namespace and available to you, try inserting this ‘inspection’ code in the welcome controller:

    $fred = get_declared_classes();
    foreach($fred as $value)
    {$extensions = get_class_methods($value);
    print "class is $value, methods are: ";
    print_r($extensions);}

When I ran this just now, it listed 270 declared classes. Most are other libraries declared in my installation of PHP. The last 11 came from CI: ten were the CI base classes (config, router, etc.) and last of all came the controller class I had called. Here’s the last 11, with the methods omitted from all but the last two:

    258: class is CI_Benchmark
    259: class is CI_Hooks,
    260: class is CI_Config,
    261: class is CI_Router,
    262: class is CI_Output,
    263: class is CI_Input,
    264: class is CI_URI,
    265: class is CI_Language,
    266: class is CI_Loader,
    267: class is CI_Base,
    268: class is Instance,
    269: class is Controller, methods are: Array ( [0] => Controller [1]    => _ci_initialize [2] => _ci_load_model [3] => _ci_assign_to_models
    [4] => _ci_autoload [5] => _ci_assign_core [6] => _ci_init_scaffolding
    [7] => _ci_init_database [8] => _ci_is_loaded [9] => _ci_scaffolding
    [10] => CI_Base )
    270: class is Welcome, methods are: Array ( [0] => Welcome [1] =>
    index [2] => Controller [3] => _ci_initialize [4] => _ci_load_model
    [5] => _ci_assign_to_models [6] => _ci_autoload [7] => _ci_assign_core
    [8] => _ci_init_scaffolding [9] => _ci_init_database [10] => _ci_is_
    loaded [11] => _ci_scaffolding [12] => CI_Base ).

Notice—in parentheses as it were—that the Welcome class (number 270: the controller I’m using) has all the methods of the Controller class (number 269). This is why you always start off a controller class definition by extending the controller class—you need your controller to inherit these functions. (And similarly, models should always extend the model class.) Welcome has two extra methods: Welcome and index. So far, out of 270 classes, these are the only two functions I wrote!

Notice also that there’s an Instance class. If you inspect the class variables of the ‘Instance’ class, you will find there are a lot of them! Just one class variable of the Instance class, taken almost at random, is the array input:

    ["input"]=> &object(CI_Input)#6 (4) { ["use_xss_clean"]=> bool(false)
    ["ip_address"]=> bool(false) ["user_agent"]=> bool(false) ["allow_get_
    array"]=> bool(false) }

Remember when we loaded the input file and created the original input class? Its class variables were:

    use_xss_clean is bool(false)
    ip_address is bool(false)
    user_agent is bool(false)
    allow_get_array is bool(false)

As you see, they have now all been included within the ‘instance’ class.

All the other CI ‘base’ classes (router, output, etc.) are included in the same way. You are unlikely to need to write code referencing these base classes directly, but CI itself needs them to make your code work.

Copying by Reference

You may have noticed that the CI_Input class is assigned by reference ([“input”]=> &object(CI_Input)). This is to ensure that as its variables change, so will the variables of the original class. As assignment by reference can be confusing, here’s a short explanation. We’re all familiar with simple copying in PHP:

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

produces 1, because $two is a copy of $one. However, if you re-assign $one:

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

This code still produces 1, because changes to $one after $two has been assigned aren’t reflected in $two. This was a one-off assignment of the value that happened to be in variable $one at the time, to a new variable $two, but once it was done, the two variables led separate lives. (In just the same way, if I alter $two, $one doesn’t change.)

In effect, PHP creates two pigeonholes: one called $one, one called $two. A separate value lives in each. You may, on any one occasion, make the values equal, but after that they each do their own thing.

PHP also allows copying ‘by reference’. If you add just a simple & to line 2 of the code:

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

Then the code now echoes 5: the change we made to $one has also happened to $two. Changing the = to =& in the second line means that the assignment is ‘by reference’. Now, it’s as if there was only one pigeonhole, which has two names ($one and $two). Whatever happens to the contents of the pigeonhole happens both to $one and to $two, as if they were just different names for the same thing.

The principle works for objects as well as simple string variables. You can copy or clone an object using the = operator, in which case you make a simple one-off new copy, which then leads an independent life. Or, you can assign one to the other by reference: now the two objects point to each other, so any changes made to the 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