22

I have a function that does this:

function registerUser($firstName, $lastName, $address, $postcode, $email, $password)
{
    $params = array($firstName, $lastName, $address, $postcode, $email, $password);
    $result = $this->db->bind("INSERT INTO Users VALUES (?, ?, ?, ?, ?, ?)", 'ssssss', $params);
}

Which sends off to my database class, which does this:

public function bind($query, $type, $params)
{
    $this->query = $query;
    $stmt = $this->mysqli->prepare($this->query);
    $stmt->bind_param($type, $param);
    $stmt->execute;
}

The problem is this doesn't work.

What I was hoping to do, was to take the $params list and have it list them after the $type, so that the query would resemble:

$stmt->bind_param('ssssss', $firstName, $lastName, $address, $postcode, $email, $password);

But obviously I'm going about it the wrong way.

is there a way to make the array...transform as it were, into a list to be printed out at the bind_param query stage?

Black
  • 18,150
  • 39
  • 158
  • 271
David G
  • 6,803
  • 4
  • 28
  • 51
  • 1
    Your query is apparently wrong. Do before the bind_param one `var_dump($this->mysqli->error);` to know what's going wrong. – bwoebi Apr 26 '13 at 12:33

7 Answers7

28

call_user_func_array "Call a callback with an array of parameters"

call_user_func_array(array($stmt, "bind_param"), array_merge(array($type), $params));

should do the job

UPDATE: you have also to change your params array:

$params = array(&$firstName, &$lastName, &$address, &$postcode, &$email, &$password);

as mysqli_stmt::bind_param expects the second and the following parameters by reference.


EDIT: Your query seems to be wrong. Maybe you have less fields than you have variables there. Do:

"INSERT INTO Users (field1, field2, field3, field4, field5, field6) VALUES (?, ?, ?, ?, ?, ?)"

where you replace the name of the fields by the correct names

Black
  • 18,150
  • 39
  • 158
  • 271
bwoebi
  • 23,637
  • 5
  • 58
  • 79
  • You were right - there was a missing field. I have fixed this However upon using call_user_func_array as above, I get the error "Warning: array_merge() [function.array-merge]: Argument #1 is not an array Warning: call_user_func_array() expects parameter 2 to be array, null given" – David G Apr 26 '13 at 12:39
  • @DavidG Use then `array_merge(array($type), $params)`. But when your `$params` is not set, I cannot do anything as it should be right. – bwoebi Apr 26 '13 at 12:41
  • Thank you for your help :) It almost works. I get an error: Warning: Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given So I will try to work out what that is about – David G Apr 26 '13 at 12:54
  • @DavidG humm, seems as if you'd need to do some `eval()` here... all parameters except the first are by reference (and I don't know why...) – bwoebi Apr 26 '13 at 12:59
  • @DavidG: see [this question](http://stackoverflow.com/questions/16120822/mysqli-bind-param-expected-to-be-a-reference-value-given) to learn about your error message. And please don't just put `@`s before your statements, it will just suppress error messages, not repair the causes of them. – Marcel Korpel Apr 26 '13 at 13:06
  • @MarcelKorpel http://3v4l.org/tn4qJ It also doesn't work, I hadn't checked... The parameters have to be passed by ref to the $params array... – bwoebi Apr 26 '13 at 13:08
14

As of PHP 5.6 you can utilize argument unpacking as an alternative to call_user_func_array, and is often 3 to 4 times faster.

<?php
function foo ($a, $b) {
     return $a + $b;
}

$func = 'foo';
$values = array(1, 2);
call_user_func_array($func, $values);
//returns 3

$func(...$values);
//returns 3
?>

Taken from here.

So your code should look something like this:

public function bind($query, $type, $params)
        {

            $this->query = $query;
            $stmt = $this->mysqli->prepare($this->query);
            $stmt->bind_param($type, ...$params);
            $stmt->execute;
        }
Thegerdfather
  • 398
  • 4
  • 13
2

The easiest way would be apparently switching from mysqli to PDO

It will let you to do it the way you want, and even without any additional functions:

function registerUser($firstName, $lastName, $address, $postcode, $email, $password)
{
    $sql  = "INSERT INTO Users VALUES (NULL, ?, ?, ?, ?, ?, ?)";
    $stmt = $this->db->prepare($sql);
    $stmt->execute(func_get_args());
}
Community
  • 1
  • 1
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • @Your Common Sense do you consider PDO better over mysqli? I also use mysqli mostly, but don't know much about PDO. – Aris Apr 26 '13 at 12:46
  • @Aris as you can see, PDO require dramatically less code. These 2 lines is *all* you need. No extra bind function required. PDO can do it out of box – Your Common Sense Apr 26 '13 at 12:48
  • sounds great. I will try to use in the future. And I realize it's a native php functionality? – Aris Apr 26 '13 at 12:51
  • @Aris yes, that's the point. And also PDO has error reporting too, so, you don't need to check for the errors manually. and there are many other things liker getting a scalar right out of query and many more things makes your code better and *readable* – Your Common Sense Apr 26 '13 at 12:54
2

You get your error "Call to a member function bind_param() on a non-object" most likely, because your $this->mysqli->prepare encounters some kind of error. (see http://php.net/manual/de/mysqli.prepare.php - it returns FALSE on error, which seems to be the case here)

After you have resolved that problem, try this instead of your call of $stmt->bind_param:

call_user_func_array(array($stmt, 'bind_param'), array_merge($type, $params));
phry
  • 35,762
  • 5
  • 67
  • 81
0

It is important to note that mysqli_stmt_bind_param() requires parameters to be passed by reference, so the parameters for call_user_func_array() must be a reference. An example taken from class context:

function execute( string $query, string $type, array $params )
{
    if ( !$stmt = $this->mysqli->prepare( $query ) ) 
        throw new \Exception( 'Prepare failed: ' . $query . PHP_EOL . $this->mysqli->error );

    // call stmt->bind_param() with variables to bind passed as a reference 
    call_user_func_array( 
        array( $stmt, 'bind_param' ), 
        array_merge( 
            array( $type ), 
            array_map( function( &$item ) { return $item; }, $params ) 
        ) 
    );

    if ( !$stmt->execute() ) 
        throw new \Exception( 'Execute failed: ' . PHP_EOL . $stmt->error );

}

}
Danijel
  • 12,408
  • 5
  • 38
  • 54
0

Today, I did some research myself in order to create a shorter method of using the prepare statement. The answer @bwoebi is very helpful but not working for an unknown amount of parameters so this is an addition to his answer.

For instance:

public function bind($query, $type, &...$params)
    {

        $this->query = $query;
        $stmt = $this->mysqli->prepare($this->query);
        call_user_func_array(array($stmt, "bind_param"), array_merge([$type], $params));
        $stmt->execute();
    }

using this thread: PHP: variable-length argument list by reference?

I managed to accomplish to pass on an unknown amount of parameters to the bind function inside the class. i then call upon the bind param function using call_user_func_array(...) with an array merge of the $type variable... (must be placed inside array for the merge)

Now i can call upon this function with $email and $password now being references:

$myClass->bind($query, "ss", $email, $password);
Mart
  • 475
  • 4
  • 21
0

Not beautiful, but thats what I use.

function _BindParam(&$stmt, &$values){
    $types="";
    foreach($values as $val){
      if(is_string($val)){$types.="s";continue;}
      if(is_int($val)){$types.="i";continue;}
      if(is_float($val) || is_double($val)){$types.="d";continue;}
      throw new Exception('Invalid value type');
    }
   $stmt->bind_param($types,...$values);
}
SalkinD
  • 753
  • 9
  • 23