2

In my view I test if (isset($winner)). However, the view is reprinted in a loop and I cannot clear it for subsequent tests.

How can I instantiate a new View or clear/unset the variables given by it's controller?

I appreciate the Views have a broad scope, but this seems like a bug in Codeigniter.

Controller:

for($i=0; $i < 100; $i++) {
    $test = $this->prizedistributor->isWinner();
    echo $this->load->view("simulateResponse", $test, TRUE);
    unset($test['winner']); // this does not work
}     

View:

<? if (isset($winner)):?>
    WINNER!
<? else: ?>
    LOST!
<? endif; ?>
<?php unset($winner); // this does not work! ?>

The Results:

Say $i==40 is the only winner, the View will report ALL $i's > 40 as winners despite the Controller always defaulting the Boolean as false.

UPDATE

I am aware testing if(empty($test)) allows the View to report correctly. However, my question is how to unset that variable (and clear it from memory). This question largely stems from other similar issues while using HMVC (modular codeigniter). However posting that code here would be too complicated to illustrate the same issue of scope.


Many folks keep questioning irrelevant prizedistributor above. So here is a simpler code sample to illustrate the sampe problem:

CONTROLLER

function testScope() {
    for($i=0; $i < 10; $i++) {
        if($i == 5)$winner = array('winner' => true);
        else $winner = array();
        echo $this->load->view("testScope", $winner, TRUE);
    }        
}  

VIEW

<?= (isset($winner))  ? "WINNER!<br>" : "LOST!<br>"; ?>

OUTPUT

LOST! LOST! LOST! LOST! LOST! WINNER! WINNER! WINNER! WINNER! WINNER!

Possible Answer:

I've looked into codeigniter's system/core/Loader.php and find function _ci_load caches variables around line 800. They're reasoning being:

You can either set variables using the dedicated $this->load_vars() function or via the second parameter of this function. We'll merge the two types and cache them so that views that are embedded within other views can have access to these variables.

This brings up a very good point and something I appreciate about codeigniter. But the question remains, how can we have both? Possibly a 4th optional parameter to skip this catching?

ex. loader->view("testScope", $winner, TRUE, FALSE); ????

E.A.T
  • 848
  • 11
  • 24
  • try giving winner a value and comparing against that. $winner == this iteration number. set to 0 when the condition is met. – Kai Qing Oct 16 '13 at 18:16
  • @KaiQing, that will not work in my case. Winner is definited within the library. I suppose i could pass around $i and another variable, but frankly that does not answer my question. – E.A.T Oct 16 '13 at 18:27
  • @stormdrain, lol. fair enough. However, nothing here even answers how to unset. Unless wesley murch's statement is true is it cannot be. Anyway, i up'd yours and others here for the valid points. – E.A.T Oct 17 '13 at 18:28
  • Wes answered it: "To unset $myvar, you have to do it explicitly: $this->load->view('myview', array('myvar' => null));"; you have to send the `null`'d var to the view otherwise the previous value will remain in the cache. – stormdrain Oct 17 '13 at 18:30
  • i sincerely appreciate all responses here, but to nullify !== unset... http://stackoverflow.com/questions/584960/whats-better-at-freeing-memory-with-php-unset-or-var-null/5441869#5441869. – E.A.T Oct 17 '13 at 18:39
  • @stormdrain, let me give this a few days. If no one can come up with a nice edit to Loader.php or whatever's needed in codeigniter's core, i will accept Wes's answer as " it's not truly possible". – E.A.T Oct 17 '13 at 18:41
  • 1
    @EliATaylor interesting. In the link you posted, the accepted answer says: "If you are doing $whatever = null; then you are rewriting variable's data. You might get memory freed / shrunk faster, but it may steal CPU cycles from the code that truly needs them sooner, resulting in a longer overall execution time.". It looks like nullifying is the better option anyway - if the concern is memory. i.e. `null`ifying a var will get PHP's gc to act on it sooner than if it were to be `unset()`. – stormdrain Oct 17 '13 at 19:41

6 Answers6

3

Using TRUE/FALSE might work better than isset.

Also, the way you are loading the view in the code you posted won't send a variable called $winner to the view unless isWinner() is returning something like array('winner' => 'someval') which may be the case.

TRUE/FALSE might look something like

for($i=0; $i < 100; $i++) {
    $test = $this->prizedistributor->isWinner();//isWinner() returns either array('winner' => '') or array('winner' => 'somevalue')
    echo $this->load->view("test_view", $winner, TRUE);
}   

view

<?php if ($winner):?>
    WINNER!<br>
<?php else: ?>
    LOST!<br>
<?php endif; ?>

example

for($i=0; $i < 100; $i++) {
    if($i == 50){
        $winner = array('winner' => 123);
    }else{
        $winner = array('winner' => '');
    }
    echo $this->load->view("test_view", $winner, TRUE);
}     

prints

...
LOST!
LOST!
LOST!
LOST!
LOST!
LOST!
WINNER!
LOST!
LOST!
LOST!
LOST!
LOST!
LOST!
...

It might help to get a better answer if you include what's going on in isWinner() or at least mention what it's returning.

stormdrain
  • 7,915
  • 4
  • 37
  • 76
  • 'winner' is indeed defined (or undefined) by the prizedistributor. Also, yes testing as (!empty($winner)) does allow the view to test correctly. However, again this does not answer my question which extends from several scope issues i've run into using Codeigniter. – E.A.T Oct 16 '13 at 20:56
  • your last suggestion to unset in the Controller does not work either. Plus in my actual code $test is a local variable to a separate function which has no scope to the actual echo statement. – E.A.T Oct 16 '13 at 21:01
3

In Codeigniter 3 you can use method

$this->load->clear_vars();

See it in documentation

2

OK so you seem to know CI caches variables that are passed to the view. The variable names depend on the keys of the data you pass to $this->load->view().

// $myvar will be cached as "true"
$this->load->view('myview', array('myvar' => true));

// $myvar will still be there
$this->load->view('myview');

// $myvar will still be there, because myvar has not changed
$this->load->view('myview', false);
$this->load->view('myview', array());

To unset $myvar, you have to do it explicitly:

$this->load->view('myview', array('myvar' => null));

Your code:

$test = $this->prizedistributor->isWinner();
echo $this->load->view("simulateResponse", $test, TRUE);

If $test is just false or null, it won't unset or change the variables already cached. It does nothing actually. You can use something more like this:

$test = $this->prizedistributor->isWinner();
echo $this->load->view("simulateResponse", array('winner' => $test), TRUE);

Have isWinner() return true/false instead of an array.

Unfortunately I think you can't truly unset the variable, but you can set it to null/false.

isset($var) will return false if $var === null.

Wesley Murch
  • 101,186
  • 37
  • 194
  • 228
  • it would be impossbile to nullify variables like this. Again, much of my insistence on unset has to do with memory. I imagine in most cases that would be negligable savings. In mine, i believe it is not. – E.A.T Oct 17 '13 at 18:32
  • I'm not totally sure what you mean, but instead of guessing you should just check the CI profiler. I'm nearly certain that there will be no bottleneck here either way you do it, and the difference will be quite insignificant if it exists at all. – Wesley Murch Oct 17 '13 at 22:19
2

You could create a helper function that load the view and destroy the parameters after that.

if (!function_exists('load_cleaned_view'))
{
    function load_cleaned_view($view, $data = NULL, $return_as_string = FALSE)
    {
        $CI =& get_instance();
        $content = $CI->load->view($view, $data, $return_as_string);

        if(!empty($data))
        {
            foreach ($data as $key => $value) $data[$key] = NULL;
            $CI->load->view($view, $data, TRUE);
        }
        return $content;
    }
}

To use with views that can handle empty variables and not with every view call because of performance issue.

clear_vars(); is interesting but it unsets all the variables and not the ones in the current view.

crocteamgg
  • 65
  • 2
  • 7
1

Have you tried getting the data directly from the view?

Controller

for($i=0; $i < 100; $i++) {
    $this->load->view("simulateResponse");
}    

VIEW

<? if($this->prizedistributor->isWinner()): ?>
    WINNER!
<? else: ?>
    LOST!
<? endif; ?>

If you're looking for a MVC solution:

Controller

<?php
    for($i=0; $i < 100; $i++)
        $data['winner_array'][$i] = $this->prizedistributor->isWinner();
    $this->load->view("simulateResponse", $data);
?>

VIEW

<? foreach($winner_array as $win): ?>
    <? if($win): ?>
        WINNER!
    <? else: ?>
        LOST!
<? endif; endforeach; ?>
Josue Alexander Ibarra
  • 8,269
  • 3
  • 30
  • 37
0

First, two things that your question is unclear on:

  1. How does $this->prizeddistributor->isWinner() work
  2. Is $winner set to $test at some point?

Now, with that out of the way... Your controller does not know what happens "in the view". You are simply echoing text and the controller continues on.

With that in mind... I'm assuming that $winner is set by the value from $test, that would mean your isWinner() function is generating the wrong value. According to your question, it should be giving you null for all values of $i except 40.

I think the main point is that your view scope does not affect the controller scope as you may hope. And that you may need to dive more into how isWinner() is working.

dispake
  • 3,259
  • 2
  • 19
  • 22
  • to your questions: 1. it does many things including possibly setting the key "winner" with data. 2. yes. – E.A.T Oct 16 '13 at 21:03