Welcome Guest, Not a member yet? Register   Sign In
Object *RECURSION* is a Bug? and the Solution
#1

[eluser]Unknown[/eluser]
First sorry my poor english...

When I try to debug using print_r($this) I have strange output that I think it should be bug. but for sure I posted it here... here is my code:

Code:
<?php

class Bugtest extends Controller {

    public function __construct()
    {
        parent::Controller();
        $this->load->library('Session');
        $this->load->library('Encrypt');
        $this->load->model('Nama_model', 'namod');
   }
  
   public function index() {
      print '<pre>';
      print_r($this);
      print '</pre>';
   }
  
}
?&gt;

Here's my result ( the "...." indicate that i strip some of the output )
Code:
Bugtest Object
(
    [_ci_scaffolding] =>
    [_ci_scaff_table] =>
    [config] => CI_Config Object
        (
...............
    [encrypt] => CI_Encrypt Object
        (
            [CI] => Bugtest Object
*RECURSION*
            [encryption_key] =>
            [_hash_type] => sha1
...............
    [session] => CI_Session Object
        (
            [sess_encrypt_cookie] => 1
            [sess_use_database] => 1
            [sess_table_name] => ci_sessions
            [sess_expiration] => 7200
            [sess_match_ip] => 1
            [sess_match_useragent] => 1
            [sess_cookie_name] => ci_session
            [cookie_prefix] =>
            [cookie_path] => /
            [cookie_domain] =>
            [sess_time_to_update] => 300
            [encryption_key] => *(&DFdshffdsjf;**&98343;[flashdata_key] => flash
            [time_reference] => local
            [gc_probability] => 5
            [userdata] => Array
                (
                    [session_id] => 31a59b74a9cf8d83aa978cbd604c5bea
                    [ip_address] => 127.0.1.1
                    [user_agent] => Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8
                    [last_activity] => 1244967874
                )

            [CI] => Bugtest Object
*RECURSION*
            [now] => 1244968149
        )

    [namod] => Nama_model Object
        (
            [_parent_name] => Nama_model
            [_ci_scaffolding] =>
            [_ci_scaff_table] =>
            [config] => CI_Config Object
...................................
            [encrypt] => CI_Encrypt Object
                (
                    [CI] => Bugtest Object
*RECURSION*
                    [encryption_key] =>
                    [_hash_type] => sha1
...................................
                    [CI] => Bugtest Object
*RECURSION*
                    [now] => 1244968149
                )

            [namod] => Nama_model Object
*RECURSION*
        )
...........

So, is it right or wrong? I'm not expert but I think even if the *RECURSION* happen because reference to the real object. it still need a lot of heavy steps before PHP assume it was recursion.

I suspect 3 files that caused that problem the files are:
- Session.php *
- Encrypt.php **
- Model.php ***

Problem *
---------
For Session.php, the Session class has attribute $CI and in constructor it was assigned using
Code:
$this->CI =& get_intance();
so every child that call constructor also "forking" it self. It cause the recursion.

Solution *
----------
remove $CI attribute, and use only
Code:
$CI =& get_instance()
if you need the super object

Problem **
----------
Same as Session.php, in Encrypt.php it has $CI attribute and assigned
Code:
$this->CI =& get_instance();
inside the constructor. The strange thing was it never been used.

Solution **
-----------
Just remove $CI attribute from the class and remove line
Code:
$this->CI =& get_instance();
in the constructor.

.... CONTINUE TO NEXT POST ....
#2

[eluser]Unknown[/eluser]
Problem ***
-----------
On Model.php, the recursion only appear when you assign another name to the Model. The routine check in failed to determine wheter the new model name is instance of the original Model. Take a look at these code:

Code:
function _assign_libraries($use_reference = TRUE)
    {
        $CI =& get_instance();                
        foreach (array_keys(get_object_vars($CI)) as $key)
        {
            if ( ! isset($this->$key) AND $key != $this->_parent_name)
            {            
                // In some cases using references can cause
                // problems so we'll conditionally use them
                if ($use_reference == TRUE)
                {
                    $this->$key = NULL; // Needed to prevent reference errors with some configurations
                    $this->$key =& $CI->$key;
                }
                else
                {
                    $this->$key = $CI->$key;
                }
            }
        }        
    }

Solution ***
------------
Code:
function _assign_libraries($use_reference = TRUE)
    {
        $CI =& get_instance();            
        
        $original_model = get_class($this);
        foreach (array_keys(get_object_vars($CI)) as $key)
        {
           // if the model assigned using different name it will be no exclude from the
           // list, so it can couse *RECURSION* effect.
           // 1. compare each object with original model
           // 2. if match then unset that

            if ( ! isset($this->$key) AND $key != $this->_parent_name)
            {            
                // In some cases using references can cause
                // problems so we'll conditionally use them
                if ($use_reference == TRUE)
                {
                    $this->$key = NULL; // Needed to prevent reference errors with some configurations
                    $this->$key =& $CI->$key;
                }
                else
                {
                    $this->$key = $CI->$key;
                }
            }

            // lines below prevent object to assign to it self
            // assuming PHP5 ( PHP4 should use is_a() method )
            if ($this->$key instanceof $original_model)
            {
               unset($this->$key);
            }
        
        }
        
    }

Instead of hacking the original file, replace the original wine by putting it on application/libraries.

Suggestion or comments are welcome...

Thanks.
#3

[eluser]macigniter[/eluser]
So let's say I have a huge library with lots of functions. Do you think it would be better in regards to performace to get the CI instance in every function via

Code:
$CI =& get_instance();

instead of assigning the instance to a class variable like

Code:
$this->CI =& get_instance();

and risking the *RECURSION* in lots of other objects?

EDIT: I just did a benchmark with the CI output profiler and couldn't find any difference in loading speed or file size. So I guess it doesn't really matter?
#4

[eluser]Phil Sturgeon[/eluser]
There is no recursion, they are using refferences so the values are only ever stored once. This is not a bug at all, part of normal PHP behavior.

Basically, do not output $this. :-)
#5

[eluser]macigniter[/eluser]
[quote author="Phil Sturgeon" date="1263833586"]Basically, do not output $this. :-)[/quote]

Hehe Smile




Theme © iAndrew 2016 - Forum software by © MyBB