Jump to content

I just cant understand the concept of referencing


farban6

Recommended Posts

Hello all

 

I am following a tutorial on netplus about prepared statements. And I understand it all with the exception of references, the call user func array etc. I made my own test to try and understand the problem and while I can get it to work, I cant understand how it works and whats going on behind the scenes, when its useful to use it, is this good practise, is there a better alternative?

 

Here is the tutorial link

 

http://net.tutsplus.com/tutorials/php/the-problem-with-phps-prepared-statements/

 

Here is my test

 

<?php

$test = array();

$test[] = &$test2['testkey'];


print_r($test2);

print_r($test);
?>

 

Outputs

 

Array ( [testkey] => ) Array ( [0] => )

 

 

Link to comment
Share on other sites

IMHO, references should be avoided unless there's no other choice, or you have a very good reason to use them.  They are as bad as global variables, in that they allow you to modify variables elsewhere in the program without it being obvious that the modification is happening.

 

Unfortunately I can't see that video as I have no speakers here, so I don't know why he is suggesting references.  Based on that code I don't see why you would need them.

Link to comment
Share on other sites

Use code tags when posting output.

Outputs

Array ( [testkey] => ) Array ( [0] => )

$test2 is some array, and apparently it has some "testkey" key in it. Without a value (null? empty string?). That's all stored in memory somewhere.

$test is a blank array, and you create an element (key 0, because it's the first item and you didn't specify a key yourself). Its value is whatever that "testkey" value is, except instead of making a copy of the value this new key points to the same place in memory.

 

Copy:
$test2[testkey] => ???
$test[0] => ???

Reference:
$test2[testkey] => ???
$test[0] ===========^

So instead of two ???s you have just the one. Make it change and you'll affect both places it's used.

Link to comment
Share on other sites

Interesting stuff, I didn't realize people thought they are as bad as global variables. Through that tutorial I am planning to build a prepared statement which can accept dynamic number of parameters. And one way as shown in the tutorial was to use references. But after your advice, could there be a different alternatives?.

Link to comment
Share on other sites

Passing by reference has its uses -

 function someFunc( &$someVar ) { $someVar .= 'APPENDED'; } 

 

Assigning a variable by reference has no REAL value, except maybe trying to hack together two scripts the wrong way.

Link to comment
Share on other sites

Ok I think I'm seeing now why he uses references.  References are "transitive", in this sense:

 

$a = &$b;
$b = &$c;

 

Now $a, $b and $c are all bound together in the same way.  They reference each other.  If you change any of them, you change all of them.

 

bind_result() "binds" its arguments by making them references.  And because the arguments passed to bind_result with call_user_func_array() are already bound to the $row[$field->name] variables as well, the result is a 3 way reference binding.  $row[$field->name] is bound to $parameters[$i] which is bound to whatever bind_result() binds it to.

 

I could be totally wrong here - still can't watch that video until I get home.  But that seems like a situation where references are the only solution, and where they are appropriate, in that data isn't going to leak in and out of functions (as long as you are careful).

Link to comment
Share on other sites

I use references most when converting recursive functions into linear functions. You know, like displaying a hierarchy of some sort. References mean I can hold a reference to something, modify it, and know that the original was also affected.

It's also great for relationships. You can track hundreds of relationships without needing copies of everything*.

 

I also haven't seen the video, but it sounds like the exact same thing I've done before, and even with bind_param() and bind_result() too.

The problem with those functions is that they require references. This is fine if you call the functions literally. But if you use call_user_func_array() then bind_*() will be referencing the variables that were given by cufa(), which aren't the variables you gave to it (because PHP made copies*). So you use a reference to keep the two tied.

But you probably started with some variables already, so you make a reference between the cufa() variables and the ones you already have, thus ending up with three references.

$foo = "asd"; // first reference
// (actually isn't a reference yet, but it will be)

bind($foo);

function bind(&$bar /* second reference */) {
$args = array(... &$bar ...); // third reference
call_user_func_array("mysqli_bind_param", $args);
// mysqli_bind_param gets a fourth reference
}

Here you actually end up with four references: $foo, $bar, $args[?], and whatever variable mysqli_bind_param() got.

 

* PHP is smart about copying versus using references.

Link to comment
Share on other sites

@requinix - Why would you use a reference and not the original variable itself?

Because I might want to make a change to something and have it propagated everywhere. Say I have a lookup array in the form

$lookup = array(
1 => array("title" => "One", "parent" => null, "children" => array()),
2 => array("title" => "Two", "parent" => /* reference to #3 */, "children" => array()),
3 => array("title" => "Three", "parent" => null, "children" => array(2 => /* reference to #2 */))
);

With that I could jump to any item, regardless of where it was in the hierarchy, and go up or down however I pleased. You can do that just fine with copies, but what if I wanted to modify #2's parent's title? Without references I would have to add an "id" item to each array and use

$lookup[$lookup[2]["parent"]["id"]]["title"] = "tres";

With references I could forget that "id" array item and just use

$lookup[2]["parent"]["title"] = "tres";

If I tried that without references then #2's parent's title would be changed, but #3's title wouldn't be.

Admittedly that doesn't happen very often; normally I do it because that's what the data actually represents and I want to make sure that's mirrored in my code. Probably a better example would be modifying some sort of counter or tally, such as how many children a node has (you go up the tree, decrementing each node's counter, until you hit the top).

 

I don't understand the use of having multiple names pointing to one resource. Why not just use one name?

Variable names? Because of function calls. They all use different variables. Maybe I don't get the question. How would you "just use one name"?

Link to comment
Share on other sites

Ignore the second point.

 

I see how that can be helpful, just something I'd never use.

 

If I wanted to modify the data, I'd keep it in a 2d array. If I wanted to display the data, I'd create a recursive function to group and display the data. I don't see a benefit in storing the child of a child of a child in a complex array. If you want to access the data, you're going to need to use a recursive function regardless of how complex or simple you format it.

 

I suppose if you wanted to grab all the children of a specific set, your example would need a few less recursions to do it. Perhaps I've just never run into a challenge that needed such complex and constant interactions of hierarchical data.

 

I'm going to do a little digging, perhaps I can be proven wrong. You mind helping me through if I post a couple code blocks? Just to make sure I'm doing things correctly.

Link to comment
Share on other sites

Another use for references is like this:

 

$big_array = <some huge array of data>;
add_foos($big_array);

# add_foos(): Adds a foo to each array element of $big_array, without generating a second copy of the array
function add_foos(&$big_array) {
  foreach ($big_array as $key => $val) {
    $val['foo'] = 'bar';
    $big_array[$key] = $val;
  }

  # No return value - parameter $big_array is passed by reference and modified
  return;
}

 

If instead you passed $big_array by value and added the foo elements and returned the modified array, PHP will create a second copy of the entire array.  If your array is huge this can double your memory usage.  In this case it makes sense to use a single well documented reference.

 

If you're not modifying the array then you don't need to do this, as php won't copy the array unless you modify it.

 

Another example is php's built in sort() functions - these all use references so they can do the sorting without making a copy of the array.

Link to comment
Share on other sites

I understand passing a variable to a function by reference, that makes perfect sense. You're keeping the copy in it's own scope, and as long as you know the function was designed to pass by reference, changes to the variable after the function call make sense.

 

The part I'm confused with is creating a variable by reference (in the same scope). requinix provided a great example when using hierarchical data, but I haven't thought of a situation where I'd use it over just storing the data in 2D.

Link to comment
Share on other sites

I also can't think of any situation where I'd use a reference within a data structure.  It would drive me crazy debugging, even if it is useful.

 

Another example of memory saving with references in php's "standard library" is mysql_fetch_array(), which creates the integer and named indexes as references to the same data.  So even though the data appears twice when you dump the contents, it is only stored once.

Link to comment
Share on other sites

The code i have simply copied from the tutorial on the link I provided earlier on in the thread.

 

http://net.tutsplus.com/tutorials/php/the-problem-with-phps-prepared-statements/

 

    function read()  
    {  
       $parameters = array();  
       $results = array();  
      
       $mysql = new mysqli('localhost', 'root', '', 'fry') or die('There was a problem connecting to the database');  
       $stmt = $mysql->prepare('SELECT * FROM usertest') or die('Problem preparing query');  
       $stmt->execute();  
      
       $meta = $stmt->result_metadata();  
      
       while ( $field = $meta->fetch_field() ) {  
      
         $parameters[] = &$field->name;  
       }  
	print_r($parameters);
       call_user_func_array(array($stmt, 'bind_result'), $parameters);
       while ( $stmt->fetch() ) {  
          $x = array();  
          foreach( $row as $key => $val ) {  
             $x[$key] = $val;  
          }  
          $results[] = $x;  
       }  
      
  
       return $results;  
      
    }  
      
    $results = read();  
    ?>  
    <!DOCTYPE html>  
      
    <html lang="en">  
    <head>  
       <meta charset="utf-8">  
       <title>untitled</title>  
    </head>  
    <body>  
    <?php foreach ($results as $row) : ?>  
      
       <p> <?php echo $row['user_password']; ?> </p>  
    <?php endforeach; ?>  
    </body>  
    </html>  

 

I have learnt a fair bit from all the advice and debate on this thread. The only think that i just cant understand is this code

 

 while ( $field = $meta->fetch_field() ) {  
  
     $parameters[] = &$row[$field->name];  
   }  
  
   call_user_func_array(array($stmt, 'bind_result'), $parameters);  

 

If i simply replaced the $parameters values with this

 

  while ( $field = $meta->fetch_field() ) {  
      
         $parameters[] = &$field->name;  
       }  

 

surely this makes the parameters passed by reference yet this outputs

 

Warning: Parameter 1 to mysqli_stmt::bind_result() expected to be a reference, value given in C:\xampp\htdocs\xampp\testing\index.php on line 19

 

so how is this being passed by value?

 

and how is row getting values assigned to the key?

 

 

Link to comment
Share on other sites

I also don't understand why this happens.  Can you do a var_dump() of $parameters before it's passed to bind_result()?  If it says they're not references then presumably they're not references, and perhaps php wasn't able to make them references.

Link to comment
Share on other sites

  $meta = $stmt->result_metadata(); 

while ( $field = $meta->fetch_field() ) {  
      
         $parameters[] = &$field->name;  
       }  
   
   
	var_dump($parameters);

 

var dump outputs the last fetched values

 

array(3) { [0]=> string(7) "user_id" [1]=> string(9) "user_name" [2]=> string(13) "user_password" }
Warning: Parameter 1 to mysqli_stmt::bind_result() expected to be a reference, value given in C:\xampp\htdocs\xampp\testing\index.php on line 21

 

No references just values, maybe its because $field is set as a method? I am shooting in the dark a bit here

Link to comment
Share on other sites

Is it because $field->name is deallocated after the reference is created?  Therefore reverting $parameters[] to a normal variable with refcount 0.

 

Sample script to demonstrate:

 

<?php
$foo[0] = 1;
$bar[0] = &$foo[0];
var_dump($bar);
unset($foo);
var_dump($bar);
?>

 

Once $foo is deallocated, $bar[0] is no longer a reference, even though it was previously made to be a reference.  Definitely not what I would expect, and another reason to avoid references unless you really need them (and you do need them here it seems).

 

The mistake though was referencing something that doesn't really belong to you - $field->name isn't really yours.  If you only reference variables you created and you know the history and future use of, then all should be fine.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.