-2

FYI. ended up going with PDO solution as this was simpler.

I'm trying to add a single method to handle all queries to the database. I want the queries to use parameter binding. How do I handle a variable amount of function parameters in mysqli_stmt_bind_param()?

This post here led me to understand the pros of parameter binding.

Here is my example code..where I am currently stuck at is marked.

INPUT PARAMETERS

  $query = "INSERT INTO b0 VALUES (?, ?, ?)"
  $par_arr = {'bookmark', 'http://www.bookmark.com', 'tag'}

PROTOTYPE CODE

  protected static function query($query, $par_arr)
    {
    if($statement=mysqli_prepare(one::$db, $query) 
      {
      mysqli_stmt_bind_param($statement, "s", ...variable amount of parameters...);<----how should this be handled?
      ...
Community
  • 1
  • 1
  • 1
    possible duplicate of [PHP syntax for variable number of parameters to be passed to function pointer](http://stackoverflow.com/questions/6491614/php-syntax-for-variable-number-of-parameters-to-be-passed-to-function-pointer) – Felix Kling Sep 11 '11 at 20:41
  • 1
    Also have a look at the [documentation](http://php.net/manual/en/mysqli-stmt.bind-param.php). – Felix Kling Sep 11 '11 at 20:44
  • So? What's the difference? A function is a function and parameters are parameters. You should try it first... I can ensure you that a solution involves this function. – Felix Kling Sep 11 '11 at 20:55
  • 2
    You can use `call_user_func_array` to call basically any function, including `mysqli_stmt_bind_param`, and by simply varying the number of elements in the array you pass to `call_user_func_array`, you'll pass more arguments to the underlying function. – John Flatness Sep 11 '11 at 21:27
  • meaningful names...there is an idea...sorry i thought that call_user_func_array was for user functions + that is what the documentation sais...why not call_func_array and why not update php.net?... –  Sep 11 '11 at 21:57

3 Answers3

2

Update 2: If you experience any further problems with this code, then you should probably follow this advice and use PDO instead.

This is how you should be using call_user_func_array [docs]:

protected static function query($query, $types, $values) {
    if($statement = mysqli_prepare(one::$db, $query) {
        $parameters = array_merge(array($statement, $types), $values);    
        call_user_func_array('mysqli_stmt_bind_param', $parameters);
        // ...
    }
}

where $types is a string indicating the type of each value, as described in the mysqli_stmt_bind_param documentation (call_user_func_array is even mentioned there).


Update: It seems it is not that easy after all, and you have to create references to the values first:

 foreach($values as $k => $v) {
     $values[$k] = &$v;
 }

 $parameters = array_merge(array($statement, $types), $values);
 call_user_func_array('mysqli_stmt_bind_param', $parameters);
 // ...

call_user_func_array is for user defined functions per php.net

No it's not. The first parameter is of type callback, and the documentation says (emphasis mine):

A PHP function is passed by its name as a string. Any built-in or user-defined function can be used, except language constructs such as: array(), echo(), empty(), eval(), exit(), isset(), list(), print() or unset().

Next remark:

is just used to simplify syntax for passing arrays to user defined functions

Have you had a look at the examples? Each element of the array you pass to call_user_func_array will be passed as argument to the function you specify. Arrays are the only way to have a collection of values of variable size.

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Note that PHP 5.3 especially is very insistent on the contents of `$values` being references. – John Flatness Sep 11 '11 at 21:48
  • @John: I updated my answer. Though I don't understand why I have to explicitly create references, but I don't know how `call_user_func_array` works internally. – Felix Kling Sep 11 '11 at 22:02
  • http://stackoverflow.com/questions/2045875/pass-by-reference-problem-with-php-5-3-1 has a few good examples of the issue, including one with `bind_param`. Basically, `array($someVar, $otherVar, ...)`, as a caller of this `query` function might have for the `$values` argument, is going to result in an array of *values*, and `call_user_func_array` will pass them by value, even if the function being called expects references. – John Flatness Sep 11 '11 at 22:03
  • The updated answer is *mostly* right, except that you *don't* want to reference-ize `$statement` or `$types`, since those args are declared to be passed by value. This causes the opposite problem as before: you'll get warnings/errors about call-time pass by reference being deprecated when you give `call_user_func_array` a reference for a parameter that should be passed by value. – John Flatness Sep 11 '11 at 22:06
  • @John: Thank you for the link and for the correction. I was infected with Referenceitis ;) – Felix Kling Sep 11 '11 at 22:07
0

I have added the complete code to create a single method for select prepared statement and insert prepared statement, Please follow the instruction and read all the comments. create database with the name 'test' and add the following query to create "users" table in the

CREATE TABLE IF NOT EXISTS `users` (
  `users_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(100) NOT NULL,
  `last_name` varchar(100) NOT NULL,
  PRIMARY KEY (`users_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;


INSERT INTO `users` (`users_id`, `first_name`, `last_name`) VALUES
(1, 'daniel', 'martin'),
(2, 'daniel', 'martin');



<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
session_start();

 class mysqli_access extends mysqli{    

        private $ip1;
        private $dbconn;
        private $hostname = HST;    // hostname
        private $username = USR;    // username
        private $password = PWD;    // password
        private $dbname   = DBN;    // datbase name 


        function mysqli_access()
        {


                $ip= $_SERVER['REMOTE_ADDR'];
                $ip1="ip_".str_replace('.', "", $ip);

                if(!is_resource($_SESSION[$ip1]))
                {
                        $this->dbconn = new mysqli($this->hostname,$this->username,$this->password,$this->dbname);
                        $_SESSION[$ip1] = $this->dbconn;
                        $dbconn = $this->dbconn;
                        if( $this->connect_error ) {
                                $this->Display_error('', $this->connect_errno, $this->connect_error, __FUNCTION__);
                        }
                }
                else {

                        $this->dbconn = $_SESSION[$ip1];        // success
                }

                return $this->dbconn;
        }


        function SelectPrepared($sql,$types,$params,$rows = '') 
        {
                $results = array();
                if ($stmt = $this->dbconn->prepare($sql)) {

                                if($types&&$params)
                                {
                                        $bind_names[] = $types;
                                        for ($i=0; $i<count($params);$i++) 
                                        {
                                                $bind_name = 'bind' . $i;
                                                $$bind_name = $params[$i];
                                                $bind_names[] = &$$bind_name;
                                        }
                                        $return = call_user_func_array(array($stmt,'bind_param'),$bind_names);
                                }
                                $stmt->execute(); /* execute query */
                                $meta = $stmt->result_metadata();
                                while ($field = $meta->fetch_field()) { 
                                        $var = $field->name; 
                                        $$var = null; 
                                        $fields_arr[$var] = &$$var;
                                }

                                call_user_func_array(array($stmt,'bind_result'),$fields_arr);
                                if($rows == 1){

                                        while ($stmt->fetch()) {
                                                $results = array();
                                                foreach($fields_arr as $k => $v)
                                                        $results[$k] = $v;
                                        }

                                }else{
                                        $i = 0;
                                        while ($stmt->fetch()) {
                                                $results[$i] = array();
                                                foreach($fields_arr as $k => $v)
                                                        $results[$i][$k] = $v;
                                                $i++;
                                        }

                                }

                                return $results;

                }
        }


        public function InsertPrepared($tblName,$arrFieldNameValue,$replace_flag=0){
            $TableName = $tblName;
                if($replace_flag==0)  
                {
                        $sqlFirst ="INSERT INTO " . $TableName . "(";
                }
                if($replace_flag==1)
                {
                        $sqlFirst ="INSERT IGNORE INTO " . $TableName . "(";                        
                }
                if($replace_flag==2)
                {
                        $sqlFirst ="REPLACE INTO " . $TableName . "(";                      
                }

                $sqlSecond =" values(";
                $params = array();
                $types = '';
                while(list($key,$value) = each($arrFieldNameValue))
                {                   
                        $sqlFirst   = $sqlFirst . $key . ",";
                        $sqlSecond  = $sqlSecond . '?' . ",";
                        $params[]   = $value;
                        $types      = $types . $this->GetValType($value);
                }               

                $sqlFirst   = substr($sqlFirst,0,strlen($sqlFirst)-1) . ") ";
                $sqlSecond  = substr($sqlSecond,0,strlen($sqlSecond)-1) .")";
                $sql        = $sqlFirst . $sqlSecond;

                if ($stmt = $this->dbconn->prepare($sql)) {

                    if($types&&$params)
                    {
                            $bind_names[] = $types;
                            for ($i=0; $i<count($params);$i++) 
                            {
                                    $bind_name      = 'bind' . $i;
                                    $$bind_name     = $params[$i];
                                    $bind_names[]   = &$$bind_name;
                            }
                            $return = call_user_func_array(array($stmt,'bind_param'),$bind_names);
                    }
                    $stmt->execute(); /* execute query */

                }                       
                return mysqli_insert_id($this->dbconn); 
        }

        private function GetValType($Item)
        {

             switch (gettype($Item)) {
                case 'NULL':
                case 'string':
                    return 's';
                    break;

                case 'integer':
                    return 'i';
                    break;

                case 'blob':
                    return 'b';
                    break;

                case 'double':
                    return 'd';
                    break;
            }
            return 's';

        }
 }






class Model_NAME extends mysqli_access
{   
        function Model_NAME() { 
                $this->tablename = TABLENAME;
                $this->mysqli_access();
        }

        ##---------------------------- Custom function start from here -----------------#

        ## fetch settings values
        function getUserRow($id,$key) {
            $sql ="SELECT first_name,last_name FROM ".$this->tablename." WHERE first_name=? and users_id  = ?";
            $param = "si";
            $array_of_params[] = addslashes($key);
            $array_of_params[] = addslashes($id);
            $result= $this->SelectPrepared($sql,$param,$array_of_params,1);
            //last parameter 1 use if want fetch single row , other wise function will return multi dimensional array
            return $result;
        }


        ## fetch settings values
        function getUserRows($last_name) {
            $sql ="SELECT first_name,last_name FROM ".$this->tablename." WHERE last_name= ?";
            $param = "s";
            $array_of_params[] = addslashes($last_name);
            $result= $this->SelectPrepared($sql,$param,$array_of_params);
            //last parameter 1 use if want fetch single row , other wise function will return multi dimensional array
            return $result;
        }

        function addValue($Array) {
            return $this->InsertPrepared( $this->tablename , $Array);
        }
}       


// configuration
define('HST','localhost');
define('USR','root');
define('PWD','techmodi');
define('DBN','test');
define('TABLENAME','users');

$obj =  new Model_NAME();
$arr = array();
$arr['first_name'] = addslashes("daniel");
$arr['last_name'] = addslashes("martin");
$obj->addValue($arr); // for insert records

// after inserting get the records
$singleRow = $obj->getUserRow(1,'daniel'); // for select single records
$multiRow =$obj->getUserRows('martin'); // for select records
echo '<pre>';
echo '<br/>-------- Single Records -----------------<br/>';
print_r($singleRow);
echo '<br/>-------- Multiple Records-----------------<br/>';
print_r($multiRow);
?>
sandipshirsale
  • 791
  • 7
  • 11
0

Because i find prepared statements boring, I am processing placeholders manually, and experience not a single problem of yours

private function prepareQuery($args)
{
    $raw = $query = array_shift($args);
    preg_match_all('~(\?[a-z?])~',$query,$m,PREG_OFFSET_CAPTURE);
    $pholders = $m[1];
    $count = 0;
    foreach ($pholders as $i => $p)
    {
        if ($p[0] != '??')
        {
             $count++;
        }
    }
    if ( $count != count($args) )
    {
        throw new E_DB_MySQL_parser("Number of args (".count($args).") doesn't match number of placeholders ($count) in [$raw]");
    }
    $shift  = 0;
    $qmarks = 0;
    foreach ($pholders as $i => $p)
    {
        $pholder = $p[0];
        $offset  = $p[1] + $shift;
        if ($pholder != '??')
        {
            $value   = $args[$i-$qmarks];
        }
        switch ($pholder)
        {
            case '?n':
                $value = $this->escapeIdent($value);
                break;
            case '?s':
                $value = $this->escapeString($value);
                break;
            case '?i':
                $value = $this->escapeInt($value);
                break;
            case '?a':
                $value = $this->createIN($value);
                break;
            case '?u':
                $value = $this->createSET($value);
                break;
            case '??':
                $value = '?';
                $qmarks++;
                break;
            default:
                throw new E_DB_MySQL_parser("Unknown placeholder type ($pholder) in [$raw]");
        }
        $query = substr_replace($query,$value,$offset,2);
        $shift+= strlen($value) - strlen($pholder);
    }
    $this->lastquery = $query;
    return $query;
}

and thus an insert query can be called as simple as

$db->run("INSERT INTO table SET ?u",$data);
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • still you can make a candy out of it. see my own answer here: http://stackoverflow.com/questions/3773406/insert-update-helper-function-using-pdo it's not exactly your case but just to get an idea – Your Common Sense Sep 11 '11 at 22:53