Welcome Guest, Not a member yet? Register   Sign In
Cannot access class variables
#1

[eluser]chuckl[/eluser]
I have been trying to figure this out for days and now believe this might be a bug in CI. I am using CI 2.0 with PHP 5.3.2 on Apache 2.2.14 and MySQL 5.1.48-community. I created the following controller:

Code:
class Test extends CI_Controller
{
  public $data = array();

  function __construct();
  {
    parent::_construct();
  }

  function index()
  {
    $this->data['index'] = 'The data[index] element<br />';    
    echo 'Class index() called.<br />';
    echo $this->data['index'];    
  }
    
  function test1()
  {
    $this->data['test1'] = 'The data[test1] element<br />';    
    echo 'Class test1 called.<br />';
    echo $this->data['index'];    
    echo $this->data['test1'];    
  }
    
  function test2()
  {
    echo 'The data array contains these two entries:<br />';
    echo $this->data['index'];    
    echo $this->data['test1'];    
  }
}

$test = new Test;
echo '---------------- index  ----------------------<br />';
echo $test->index();
echo '---------------- test 1 ----------------------<br />';
echo $test->test1();
echo '---------------- test 2 ----------------------<br />';
echo $test->test2();
/* End of file test.php*/
/* Location: */
Using the URL "../index.php/test" produces the following results:
Code:
---------------- index ----------------------
Class index() called.
The data[index] element
---------------- test 1 ----------------------
Class test1 called.
The data[index] element
The data[test1] element
---------------- test 2 ----------------------
The data array contains these two entries:
The data[index] element
The data[test1] element
Class index() called.
The data[index] element
The URL "../index.php/test/test1" produces the following results:
Code:
---------------- index ----------------------
Class index() called.
The data[index] element
---------------- test 1 ----------------------
Class test1 called.
The data[index] element
The data[test1] element
---------------- test 2 ----------------------
The data array contains these two entries:
The data[index] element
The data[test1] element
Class test1 called.
_________________________________
A PHP Error was encountered

Severity: Notice

Message: Undefined index: index

Filename: controllers/test.php

Line Number: 24
__________________________________
The data[test1] element
The URL "../index.php/test/test2" produces the following results:
Code:
---------------- index ----------------------
Class index() called.
The data[index] element
---------------- test 1 ----------------------
Class test1 called.
The data[index] element
The data[test1] element
---------------- test 2 ----------------------
The data array contains these two entries:
The data[index] element
The data[test1] element
The data array contains these two entries:
_________________________________
A PHP Error was encountered

Severity: Notice

Message: Undefined index: index

Filename: controllers/test.php

Line Number: 31
_________________________________
A PHP Error was encountered

Severity: Notice

Message: Undefined index: test1

Filename: controllers/test.php

Line Number: 32
_________________________________
Could this be caused by the CI instantiation of the controller class? It's almost as though the controller gets freshly instantiated with every method call.
#2

[eluser]chuckl[/eluser]
After more testing I am convinced that while this may be "Works As Coded" it is not the correct implementation. In system/core/CodeIgniter.php around line 267 it appears that CI instantiates a new controller object every time any method is called. The controllers should all be instantiated as singleton objects.

As it currently is coded class level variables will never work because you get a new instance of the class every time. I guess I will have to store all of my class data values on the session stack in the meantime. Not a good thing though.
#3

[eluser]WanWizard[/eluser]
I honestly don't know what you're trying to do or prove.

CI will load and instantate your class. It will then call the method indicated by the URI (we'll use 'test2' as an example here). The output of that is buffered.

You then create a second instance of your class, and call all three methods in succession. Which works perfectly and outputs what you should expect. The errors you see are from CI's call to the method. When CI calls test2(), $this->data['index'] and $this->data['test1'] don't have a value, and PHP complains about it.

Note: these are two DIFFERENT class instances, they are different objects, and therefore don't share the same properties!
#4

[eluser]chuckl[/eluser]
This is what I am trying to prove...

Remove the instantiation and calls at the bottom of the controller class. Now try to call any of the functions within the class. Other than the default index() function calls to the other functions will receive an error because...wait for it...each function call creates a NEW object where the class variable is a different variable because the controller is a different object.

Think of it this way:
Code:
url /index.php/test       creates controller object test(instance 1)
url /index.php/test/test1 creates controller object test(instance 2)
url /index.php/test/test2 creates controller object test(instance 3)
Three objects with three distinct sets of variables. That is the problem, it is basic OO creation and handling. Controllers should be instantiated as singleton objects so you only have one controller object instantiated and therefore one set of variables and methods.

I really can't believe that no one has come across this before. I guess no one uses class level variables across multiple functions. I've worked around this by storing the data in the session stack but that is really not the best solution. The correct solution is to create controllers as singleton objects.
#5

[eluser]InsiteFX[/eluser]
And what do you think happens to your variables when you reintiate your class?

InsiteFX
#6

[eluser]WanWizard[/eluser]
Maybe you should learn about web applications, and PHP in particular.

PHP is stateless. Every time you request a URL, the PHP interpreter starts, loads your code, and runs it. When it finishes, it cleans everything up. When you request a second URL, the process starts again.

If you're expecting that when you request /index.php/test/test1 the result of the /index.php/test request are still present, think again...
#7

[eluser]chuckl[/eluser]
OK, let me try to explain it this way...basic, fundamental OO:

You can create objects by simply:
Code:
class myController extends CI_Controller
{
  $data = '';

  function myFunction($input){
    $this->data = $input;
    return $this->data;
  }

  function anotherFunction() {
    return $this->data;
  }
}

// In your view you call the function
echo $myController->myFunction('--Test 1--');

// Controller is created in CodeIgniter.php like this:
$myController = new myController;

// The controller returns and the view displays
// --Test 1--

// In your view you call the controller function
echo $myController->myFunction('--Test 2--');

// Controller is created in CodeIgniter.php like this:
$myController = new myController;

// The controller returns and the view displays
// --Test 2--

// In your view you call the controller function
echo $myController->anotherFunction();

// Controller is created in CodeIgniter.php like this:
$myController = new myController;

// The controller returns and the view displays an empty string
Please note that each object instantiation, while the same name is not the same object but a NEW object. The $data variable is a NEW variable each time.

This is what CI does whenever a controller function/method is called, it creates a NEW controller object. What should it should be doing is creating a single controller object and preventing multiple instances from being created. This is called a singleton pattern and is created using code similar to the following (in fact the CI_Controller object is created this way which means that every class that extends CI_Controller and has a parent::__construct(); call is calling the ONE and ONLY CI_Controller object):
Code:
function &getInstance;(){
  static $instance;
  if(!instance){
    $instance = new controllerClass();
  }
  return $instance;
}
Under this scenario, there would be only one instance of each application controller and therefore class variables would be the same instance and could be modified or read by all methods throughout the life of the controller (Think class global variables.) Here is what would then happen:
Code:
// In your view you call the function
echo $myController->myFunction('--Test 1--');

// CI either creates a new instance if it does not exist or returns
// existing instance

// The controller returns and the view displays
// --Test 1--

// In your view you call the controller function
echo $myController->myFunction('--Test 2--');

// CI returns existing instance

// The controller returns and the view displays
// --Test 2--

// In your view you call the controller function
echo $myController->anotherFunction();

// CI returns existing instance

// The controller returns and the view displays
// --Test 2--
The current implementation results in a new object created every time a controller method is called. This is not only expensive to do it also precludes you from having class level variables. Under the current implementation the only possible way to have data persist and available for different methods within a controller is to save it off as a session variable which is not the best use of session space.

I hope you can now begin to understand the issue better.
#8

[eluser]jgetner[/eluser]
i think you should take another look at the load_class function in the core... it acts as a registry and a sinlgeton and does not always make a new instance, if the instance has already been called it just simply recalls the instance... unless you call another class then it must reload initiate the class bla bla but on your example the reason your class is not saving the data is your code is wrong you should look into static variables to save with out destroying it on ever method call.
#9

[eluser]chuckl[/eluser]
If that is the case then please explain this real world example, based on an application I am currently developing.
Code:
class Browse extends CI_Controller
{
  private $data = array();

  function __construct()
  {
    parent::__construct();
  }

  function index()
  {
    // set up some view data
    $this->data['title'] = 'Site Title';
    $this->data['content'] = 'browser/list';
    ...more code
    $this->load->view('includes/template', $this->data);
  }

  function processAjax()
  {
    // Parse data received from AJAX call
    // and save it for later use.
    $this->data['params'] = $this->input->post('item');
  }
  
  function function saveItem()
  {
    $this->load->model('items','items');
    $this->items->save($data['params'];

    $this->load->view('includes/template', $this->data);
  }
}
The processAjax() method is called by a jQuery function passing data via the POST array.

This controller fails in two places.

1. While the processAjax method receives the data off of the POST array and stores in the $data['params'] element, it is only present during the execution of that method. When the method saveItem() is called it is a NEW instance and $data['params'] does not exist.

2. When the view is loaded in the saveItem() method, errors occur because there are unknown variables (e.g. $title, $params) because the call to saveItem() is working on a new and different object instance than when either the index() or processAjax() methods were called; the $data variable is new and an empty array.

The only way (currently) to solve this is to not use the $data variable but store all of the variables that are going to be used by multiple functions within the class in session variables (which are stored in either cookies or the database or both.) If you have more than a few variables that will quickly become expensive processing.
#10

[eluser]jgetner[/eluser]
this could be your problem
Code:
$this->items->save($data['params'];
should be
Code:
$this->items->save($this->data['params']);




Theme © iAndrew 2016 - Forum software by © MyBB