web-dev-qa-db-ja.com

PHPでmysqlibind_param引数を動的にバインドする方法は?

私はSQLクエリに準備されたバインドされたステートメントを使用することを学びました、そして私はこれまでこれを出しました、それは問題なく動作しますが、複数のパラメータに関してはまったく動的ではありません、またはパラメータが必要ないとき、

public function get_result($sql,$parameter)
    {
        # create a prepared statement
    $stmt = $this->mysqli->prepare($sql);

        # bind parameters for markers
    # but this is not dynamic enough...
        $stmt->bind_param("s", $parameter);

        # execute query 
        $stmt->execute();

    # these lines of code below return one dimentional array, similar to mysqli::fetch_assoc()
        $meta = $stmt->result_metadata(); 

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

        call_user_func_array(array($stmt, 'bind_result'), $parameters); 

        while($stmt->fetch()) 
        { 
            return $parameters;
            //print_r($parameters);      
        }


        # close statement
        $stmt->close();
    }

これが私がオブジェクトクラスと呼ぶ方法です。

$mysqli = new database(DB_Host,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);

パラメータを渡す必要がない場合もありますが、

$sql = "
SELECT *
FROM root_contacts_cfm
";

print_r($output->get_result($sql));

時々私はただ一つのパラメータを必要とします、

$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
ORDER BY cnt_id DESC
";

print_r($output->get_result($sql,'1'));

時々私は複数のパラメータだけを必要とします、

$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
AND root_contacts_cfm.cnt_firstname = ?
ORDER BY cnt_id DESC
";

print_r($output->get_result($sql,'1','Tk'));

したがって、この行は上記の動的タスクに対して十分に動的ではないと思います。

$stmt->bind_param("s", $parameter);

Bind_paramを動的に構築するために、私はこれをオンラインの他の投稿で見つけました。

call_user_func_array(array(&$stmt, 'bind_params'), $array_of_params);

そして、私は php.net からいくつかのコードを変更しようとしましたが、どこにも行きません、

if (strnatcmp(phpversion(),'5.3') >= 0) //Reference is required for PHP 5.3+ 
    { 
        $refs = array(); 
        foreach($arr as $key => $value) 
            $array_of_param[$key] = &$arr[$key]; 

       call_user_func_array(array(&$stmt, 'bind_params'), $array_of_params);

     }

どうして?それを機能させる方法はありますか?

それとももっと良い解決策がありますか?

15
laukok

mysqliの答えが見つかりました:

public function get_result($sql,$types = null,$params = null)
    {
        # create a prepared statement
        $stmt = $this->mysqli->prepare($sql);

        # bind parameters for markers
        # but this is not dynamic enough...
        //$stmt->bind_param("s", $parameter);

        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);
        }

        # execute query 
        $stmt->execute();

        # these lines of code below return one dimentional array, similar to mysqli::fetch_assoc()
        $meta = $stmt->result_metadata(); 

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

        call_user_func_array(array($stmt, 'bind_result'), $parameters); 

        while($stmt->fetch()) 
        { 
            return $parameters;
            //print_r($parameters);      
        }


        # the commented lines below will return values but not arrays
        # bind result variables
        //$stmt->bind_result($id); 

        # fetch value
        //$stmt->fetch(); 

        # return the value
        //return $id; 

        # close statement
        $stmt->close();
    }

その後:

$mysqli = new database(DB_Host,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);

$sql = "
SELECT *
FROM root_contacts_cfm
ORDER BY cnt_id DESC
";
print_r($output->get_result($sql));

$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
ORDER BY cnt_id DESC
";

print_r($output->get_result($sql,'s',array('1')));

$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = ?
AND root_contacts_cfm.cnt_firstname = ?
ORDER BY cnt_id DESC
";

print_r($output->get_result($sql, 'ss',array('1','Tk')));

これに関しては、mysqliは非常に不十分です。 PDOに移行する必要があると思います!

16
laukok

PHP 5.6)を使用すると、 解凍演算子...$var)を使用してこれを簡単に実行し、 get_result() insted bind_result()

public function get_result($sql,$types = null,$params = null) {
    $stmt = $this->mysqli->prepare($sql);
    $stmt->bind_param($types, ...$params);

    if(!$stmt->execute()) return false;
    return $stmt->get_result();

}

例:

$mysqli = new database(DB_Host,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);


$sql = "SELECT * FROM root_contacts_cfm WHERE root_contacts_cfm.cnt_id = ?
        AND root_contacts_cfm.cnt_firstname = ?
        ORDER BY cnt_id DESC";

$res = $output->get_result($sql, 'ss',array('1','Tk'));
while($row = res->fetch_assoc()){
   echo $row['fieldName'] .'<br>';
}
7
rray

または多分より良い解決策がありますか?

この答えはあまり役に立ちませんが、mysqliから [〜#〜] pdo [〜#〜] への切り替えを真剣に検討する必要があります。

これの主な理由は、PDOが組み込み関数を使用してmysqliで実行しようとしていることを実行するためです。 手動パラメータバインディング に加えて、 実行メソッド は代わりに引数の配列を取ることができます。

PDOは簡単に拡張でき、準備実行ダンスを実行する代わりに、フェッチエブリシングアンドリターンに便利なメソッドを追加するのは非常に簡単です。

5
Charles

PHP 5.6以降

$stmt->bind_param(str_repeat("s", count($data)), ...$data);

PHP 5.5以下の場合、次のことを期待できます(そして[〜#〜] i [〜#〜]しました)動作する:

call_user_func_array(
    array($stmt, "bind_param"),
    array_merge(array(str_repeat("s", count($data))), $data));

...だが mysqli_stmt::bind_paramはそのパラメータが参照であることを期待しますが、これは値のリストを渡します。

最初に元の配列への参照の配列を作成することで、これを回避できます(ただし、これは醜い回避策です)。

$references_to_data = array();
foreach ($data as &$reference) { $references_to_data[] = &$reference; }
unset($reference);
call_user_func_array(
    array($stmt, "bind_param"),
    array_merge(array(str_repeat("s", count($data))), $references_to_data));
3
Matt Raines

素敵なmysqliクラスを見つけました。動的パラメーターを処理でき、使いやすいです。

https://github.com/ajillion/PHP-MySQLi-Database-Class

クエリを動的に構築する方法をソースコードで参照できます

https://github.com/ajillion/PHP-MySQLi-Database-Class/blob/master/MysqliDb.php

2
Steely Wing

PDOと同様のシステムを適用して解決しました。 SQLプレースホルダーは、二重小数点文字で始まる文字列です。例:

:id, :name, or :last_name

次に、ダブルポイントの直後に指定文字を追加し、ニーモニック変数の前に下線文字を追加することで、プレースホルダー文字列内に直接データ型を指定できます。例:

:i_id (i=integer), :s_name or :s_last_name (s=string)

型文字が追加されていない場合、関数はデータを保持しているphp変数を分析することによってデータの型を判別します。例:

$id = 1 // interpreted as an integer
$name = "John" // interpreted as a string

この関数は、php関数mysqli_stmt_bind_param()をループで実行できる型の配列と値の配列を返します。

$sql = 'SELECT * FROM table WHERE code = :code AND (type = :i_type OR color = ":s_color")';
$data = array(':code' => 1, ':i_type' => 12, ':s_color' => 'blue');

$pattern = '|(:[a-zA-Z0-9_\-]+)|';
if (preg_match_all($pattern, $sql, $matches)) {
    $arr = $matches[1];
    foreach ($arr as $Word) {
        if (strlen($Word) > 2 && $Word[2] == '_') {
            $bindType[] = $Word[1];
        } else {
            switch (gettype($data[$Word])) {
                case 'NULL':
                case 'string':
                    $bindType[] = 's';
                    break;
                case 'boolean':
                case 'integer':
                    $bindType[] = 'i';
                    break;
                case 'double':
                    $bindType[] = 'd';
                    break;
                case 'blob':
                    $bindType[] = 'b';
                    break;
                default:
                    $bindType[] = 's';
                    break;
            }
        }    
        $bindValue[] = $data[$Word];
    }    
    $sql = preg_replace($pattern, '?', $sql);
}

echo $sql.'<br>';
print_r($bindType);
echo '<br>';
print_r($bindValue);
1
Sal Celli