get_instance() in Codeigniter: Why assign it to a variable?
PhpCodeigniterSingletonReferencePhp Problem Overview
In Codeigniter, get_instance()
is a globally available function that returns the Controller super-object which contains all the currently loaded classes (it returns the Controller class instance). I'll include the current source code:
get_instance()
is defined in Codeigniter.php
// Load the base controller class
require BASEPATH.'core/Controller.php';
function &get_instance()
{
return CI_Controller::get_instance();
}
And CI_Controller
is defined in Controller.php
class CI_Controller {
private static $instance;
/**
* Constructor
*/
public function __construct()
{
self::$instance =& $this;
// Assign all the class objects that were instantiated by the
// bootstrap file (CodeIgniter.php) to local class variables
// so that CI can run as one big super object.
foreach (is_loaded() as $var => $class)
{
$this->$var =& load_class($class);
}
$this->load =& load_class('Loader', 'core');
$this->load->set_base_classes()->ci_autoloader();
log_message('debug', "Controller Class Initialized");
}
public static function &get_instance()
{
return self::$instance;
}
}
Here's how it is recommended to be used in the user guide for creating libraries:
> ###Utilizing CodeIgniter Resources within Your Library
>
> To access CodeIgniter's native resources within your library use the
> get_instance()
function. This function returns the CodeIgniter super
> object.
>
> Normally from within your controller functions you will call any of
> the available CodeIgniter functions using the $this
construct:
> $this->load->helper('url'); $this->load->library('session'); > $this->config->item('base_url');
etc.
>
> $this
, however, only works directly within your controllers, your
> models, or your views. If you would like to use CodeIgniter's classes
> from within your own custom classes you can do so as follows:
>
> First, assign the CodeIgniter object to a variable:
>
> $CI =& get_instance();
>
> Once you've assigned the object to a variable, you'll use that
> variable instead of $this
:
> $CI =& get_instance();
> $CI->load->helper('url'); $CI->load->library('session');
> $CI->config->item('base_url'); etc.
>
> Note: You'll notice that the above get_instance()
function is being
> passed by reference:
>
> $CI =& get_instance();
>
> This is very important. Assigning by reference allows you to use the
> original CodeIgniter object rather than creating a copy of it.
Related posts: https://stackoverflow.com/questions/4740430/explain-ci-get-instance/4740548#4740548 / https://stackoverflow.com/questions/2819435/codeigniter-get-instance
So, here is my actual question:
Why does the user guide recommend assigning get_instance()
to a variable? I'm fairly certain I understand the implications of not assigning by reference, but why is it recommended to assign it to a variable when get_instance()->load->model()
works fine?
I see a lot of user defined or third party classes in CI that assign to a property of the object:
class MY_Class {
private $CI;
function __construct()
{
$this->CI =& get_instance();
}
function my_func()
{
$this->CI->load->view('some_view');
}
function my_other_func()
{
$this->CI->load->model('some_model');
}
}
Poor example, but I see this frequently. Why bother with this method instead of just calling get_instance()
directly? It seems like assigning the entire Controller object to a class variable wouldn't be a great idea, even if it is a reference. Maybe it doesn't matter.
I want to write a wrapper function for get_instance()
so it's easier to type, and I don't have to constantly assign it to a variable.
function CI()
{
return get_instance();
}
Or:
function CI()
{
$CI =& get_instance();
return $CI;
}
Then I could use CI()->class->method()
from anywhere without the hassle of assigning it to a variable, it's very easy to write and understand what it does, and can result in shorter, more elegant code.
- Is there any reason not to take this approach?
- Is there any difference between the two
CI()
functions above? - Why is it recommended to assign
get_instance()
to a variable rather than calling it directly? - What does the
&
infunction &get_instance(){}
mean where it is defined? I know a bit about what references are for and I use them when appropriate, but I've never seen a function defined this way. If I do write a wrapper function, should I use this as well?
Please note that this is not so much a style question, but a technical one. I want to know if there are any issues, performance or otherwise, with using the method I'm suggesting.
EDIT: So far we have:
- Method chaining is not available in php4, so assigning to a variable is a workaround (although this is fairly irrelevant as Codeigniter has dropped php4 support)
- The minor overhead of calling a function more than once to return the object, rather than calling it once and assigning to a variable.
Anything else, or are these the only potential issues?
Php Solutions
Solution 1 - Php
As far as I know, it's a matter of convenience more than anything. Chances are that you will be using the CI super object a lot in your libraries so why not assign it to a variable to make it a little easier to work with?
There are a few other things to consider...
- If you put this method in a helper, that method becomes a dependency for any class you are using it in. This might not be a big deal for you, but if you want to share libraries with anyone else they may not be happy about the dependency, especially since there is already a standard way of handling this in the CI community.
- There is a slight impact on performance because you are calling
get_instance()
every time you use the helper rather than storing its result in a variable. - Since this is a helper method that is supposed to save you time, for anyone who is working mostly in the core MVC files of CI, setting up a helper like this would take longer than just setting it to a variable in the few places you need it.
Solution 2 - Php
> Why is it recommended to assign get_instance() to a variable rather > than calling it directly?
Most probably, it is recommended to maintain backward compatibility with php4, where objects were not passed by reference by default, but were cloned.
> Is there any reason not to take this approach?
Only if you want your application to run on outdated php installations
Solution 3 - Php
Method chaining is not supported in PHP4
and CI
dropped support for PHP4
very recently (from version 2.0.0
). Also it's easy to write $CI
than writing get_instance()
every time.
Solution 4 - Php
It is necessary to assign by reference because the value of CI_Controller::$instance
is subject to change if another instance of the class is created. The constructor re-assigns self::$instance
each time it runs.
In general, this feels like a bad design pattern and is missing the property of a singleton that limits the class to only one instance, http://en.wikipedia.org/wiki/Singleton_pattern.
It seems possible to type, CI_Controller::get_instance()->$className->$method();
which does seem like more typing that your requested CI()->$className->$method
.
Ultimately, it would make sense to require that only one instance of $instance
can be created and then the need for assigning by reference would be eliminated.
Solution 5 - Php
I prefer uses this way, it's simple
class Test
{
//magic method __get, whit this can use $this->load
//instead create a variable ci, then do $this->ci->load, cool :)
public function __get($var)
{
return get_instance()->$var;
}
public function showUrl()
{
$this->load->helper("url");
echo base_url();
}
}
Solution 6 - Php
It could be a combination of several things, including the already mentioned:
- Backwards compatibility
- Convenience
- Style Guide
Preferably, I like the idea of this 'recommendation' as being a part of a style guide. Maybe not the official style guide of CI, but still.
Imagine that all third-party scripts for CI implements this recommendation, any developer would be able to quickly determine how these scripts are designed - although this just being a very small part of the script.
Another thing that IMO is important is the mechanics of method chaining - and doing CI()->class->method()
wouldn't seem intuitive for me, knowing how the rest of CI works.
Solution 7 - Php
Getting the result of get_instance()
by reference just makes no sense since PHP5. Sadly this bad habit seems to be deep-rooted, so let's just deal with it.
For those interested, here's an über fast instance getter:
function CI()
{
static $CI;
isset($CI) || $CI = CI_Controller::get_instance();
return $CI;
}
Note that the static variable wouldn't work if it were assigned by reference.
Also, you are forced not to get by reference the result of this CI()
. Extra sugar :-)
Ah, and obviously you still have the (slight) cost of the function call. You still may want to use a variable instead of calling the function dozens of times.