0

MYSQLi 呼び出しで使用される変数の数を動的に変更する方法を探していました。5 年前に PHP.net で非常に役立つ投稿を見つけました ( http://php.net/manual/en/mysqli-stmt.bind-param.php#100879 )。しかし、私は物事に少し夢中になり、彼の仕事の私の適応がまだ安全/効率的/エラーでいっぱいかどうかを知るのに助けが必要です。

アイデアは次の 5 点です。

  1. 必要な数の変数を動的に簡単に使用できるようにする。
  2. 古い MYSQL と同じくらい簡単にクエリを作成できるようにするため (MYSQLi プリペアド ステートメントの最新性とセキュリティを利用しながら)。
  3. 手動でクラスを追加する必要をなくし、代わりに PHP に処理させます。
  4. 戻り値を期待するクエリ (SELECT および EXPLAIN) とそうでないクエリ (INSERT、DELETE、および UPDATE) を自動的に区別するため。
  5. 1 つの変数を変更することにより、個々の行またはページ全体を簡単にデバッグする方法を提供します。

これらはすべて、次のようなもので達成されることを願っています。

doMYSQL('INSERT INTO table(id, name) VALUES ($id,$name)');

必要に応じて、以下の関数では、クエリ (古い MYSQL のようにインライン変数を使用) が一重引用符で囲まれていることに注意してください。変数は、その値ではなく、実際の変数名として解析されます。値は、MYSQLi の準備済みステートメントを準備する段階で 1 回だけ行われます (したがって、私が知る限り、差し止め攻撃に対する同じセキュリティがあるはずです)。

さて、公式ノート。これを改善する方法について、またはどこかに明らかなエラーがある場合は、フィードバックをお待ちしております。最後のメモの下にあるすべてのコード (「その他のコード」) は、PHP.net の投稿からのものであり、そのほとんどが理解できないため、それに関するコメントも役に立ちます。もしこの関数が基準をクリアできれば、私の人生は確実に楽になるでしょう。

明確にするために、これは私が試みたすべてのテストで機能したので、何か問題があると考える理由はありません. 私は、危険信号があるかどうかを知るのに十分な経験がないことを知っているだけです。したがって、私は皆さんに敬意を表し、機能の安全性を検証するための支援を求めます.

<?php
/*
doMYSQL($sql, $debug_local [optional]);
$sql = Statement to execute;
$debug_local = 'print' to show query on page but not run, 'both' to show it and run, leave blank for normal execution.
(You can add a $debug variable at the top of the page to control all doMYSQL functions at once, though local ones take precedence.
*/

function doMYSQL($sql, $debug_local = 'none')
{
  $mysqli = new mysqli("localhost", "username", "password", "database");
  $print = $sql; // Save unaltered copy in case 'print' is enabled later

  // Get debug settings (priority is user-set $debug_local, then global $debug, then default to 'none')
  global $debug;
  if (($debug == 'print' OR $debug == 'both') AND $debug_local == 'none'){$debug_local = $debug;}

  // Create list of variables in the query
  preg_match_all('/\$\w+/',$sql,$matches);

  // For each variable found, find its value and add its kind and value to $params 
  $params = array();
  foreach ($matches[0] AS $match)
  {
      $match = substr($match,1); // Get rid of the now-unneccessary '$'' on the variable name
      global $$match; // Get the global value for that variable
      $kind = gettype($$match);  // Get the kind for that variable

        // Convert PHP kind to mysqli kind for bind_result
        if ($kind == "integer"){$kind = 'i';} 
        if ($kind == "double"){$kind = 'd';}
        if ($kind == "string"){$kind = 's';}

      $params[0] .= $kind; // Adds to ongoing list of types in $param[0]
      $params[] = $$match; // Adds to ongoing list of values in $params[1+]
      $sql = str_replace("$"."$match", '?', $sql); // Switch variable with '?' in the query
      $print = str_replace("$"."$match", $$match."[$kind]", $print); // Switch variable with '?' in the query      
  }

  // If debug is print or both, print
  if ($debug_local == "print" OR $debug_local == "both")
  {
    echo "MYSQLi Debug: $print<br>"; 
  }


  // If debug is not 'print', run it
  if ($debug_local != 'print')
  {
    // Get first word; if a select/explain, set $close to false; otherwise set to 'true.'  If irregular query, error message.
    $temp = explode(' ',trim($sql),2);
    $firstword = strtolower($temp[0]);
    if ($firstword == 'select' OR $firstword == 'explain'){$close=false;}
    else if ($firstword == 'update' OR $firstword == 'delete' OR $firstword == 'insert'){$close=true;}
    else {echo "Invalid first word on query $query!<br>";}


    // Start misc code found on the PHP link
    $stmt = $mysqli->prepare($sql) or die ("Failed to prepared the statement!");

    call_user_func_array(array($stmt, 'bind_param'), refValues($params));

    $stmt->execute();

     if($close){
         $result = $mysqli->affected_rows;
     } else {
         $meta = $stmt->result_metadata();

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

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

      while ( $stmt->fetch() ) {  
         $x = array();  
         foreach( $row as $key => $val ) {  
            $x[$key] = $val;  
         }  
         $results[] = $x;  
      }

      $result = $results;
     }

     $stmt->close();
     $mysqli->close();

     return  $result;   
   } 
}

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

Examples (generic):

doMYSQL('SELECT * FROM table WHERE id = $id');
doMYSQL('SELECT * FROM table');
doMYSQL('INSERT INTO table(id, name) VALUES ($id,$name)');


Examples (with data):
$user = 1;
$location = 'California';

$result = doMYSQL('SELECT * FROM watchlists_locations WHERE user = $user AND location = $location');
print_r($result);

doMYSQL('INSERT INTO watchlists_locations(user, location) VALUES ($user,"1000")');
?>
4

1 に答える 1