【问题标题】:PHP Implode Associative ArrayPHP内爆关联数组
【发布时间】:2011-03-17 15:21:07
【问题描述】:

所以我正在尝试创建一个基于多维数组生成 SQL 查询字符串的函数。

例子:

function createQueryString($arrayToSelect, $table, $conditionalArray) {
$queryStr = "SELECT ".implode(", ", $arrayToSelect)." FROM ".$table." WHERE ";
$queryStr = $queryStr.implode(" AND ",$conditionalArray); /*NEED HELP HERE*/
return $queryStr;
}

$columnsToSelect = array('ID','username');
$table = 'table';
$conditions = array('lastname'=>'doe','zipcode'=>'12345');
echo createQueryString($columnsToSelect, $table, $conditions); /*will result in incorrect SQL syntax*/

如您所见,我需要关于第三行的帮助,因为它目前正在打印

从表 WHERE 中选择 ID、用户名 姓氏和邮政编码

但它应该是打印出来的

从表 WHERE 中选择 ID、用户名 姓氏 = 'doe' AND zipcode = '12345'

【问题讨论】:

  • 我看到您正在尝试创建 SQL 查询生成器。我现在会告诉你,这是一个巨大的痛苦......顺便说一句,如果你的$conditionalArray 是空的(因为 WHERE 子句仍然存在),这将生成无效的 SQL......而且你不适应LIKE、等

标签: php arrays associative-array implode


【解决方案1】:

您必须编写另一个函数来处理$conditionalArray,即处理$key => $value 并处理类型,例如如果它们是字符串,则应用引号。

您只是在处理= 条件吗? LIKE<>呢?

【讨论】:

    【解决方案2】:

    我建议不要自动创建条件。
    您的案例过于本地化,而可能存在许多其他运算符 - LIKE、IN、BETWEEN、 等。
    一些逻辑,包括几个 AND 和 OR。

    最好的方法是手动。
    我总是这样干这种事

    if (!empty($_GET['rooms']))     $w[]="rooms='".mesc($_GET['rooms'])."'";
    if (!empty($_GET['space']))     $w[]="space='".mesc($_GET['space'])."'";
    if (!empty($_GET['max_price'])) $w[]="price < '".mesc($_GET['max_price'])."'";
    

    如果你仍然想要这个简单的数组,只需使用

    foreach ($conditions as $fieldname => $value)...
    

    然后以您需要的方式组合这些变量。你有 2 个选择:用 field='value' 对创建另一个数组,然后将其内爆,或者只是连接,然后在末尾加上 substr 尾随 AND

    【讨论】:

    • mesc 是什么我在 MySQL/PHP 手册中找不到它
    • @aslum 那是 mysql_real_escape_string
    【解决方案3】:

    您实际上并没有内爆多维数组。 $conditions 是一个关联数组。

    只需在函数 createQueryString() 中使用 foreach 循环。像这样的东西应该可以工作,注意它未经测试。:

    $terms = count($conditionalArray);
    foreach ($conditionalArray as $field => $value)
    {
        $terms--;
        $queryStr .= $field . ' = ' . $value;
        if ($terms)
        {
            $queryStr .= ' AND ';
        }
    }
    

    注意:为防止 SQL 注入,值应转义和/或引用为所用数据库的适当/必要性。不要只是复制和粘贴;想想吧!

    【讨论】:

    • @aidan 实际上,我没有。答案的重点是关注完成任务所需的循环/逻辑。
    • 不是你,只是给计划复制和粘贴代码的任何人的说明(因为它只是要求 SQL 注入)。
    • @aidan 够公平的。非常重要的两点:第一,如果您的目的是帮助未来的新手,您应该在该注释中提供更多解释。例如。通过添加子句:because it's just asking for SQL injection. 二,引用该值是不够的,并且对于一般情况不正确。这些值可以合法地是数字,并且应该这样呈现给数据库。为防止 SQL 注入,值应转义和/或引用作为所用数据库的适当/必要性。
    【解决方案4】:
    function implodeItem(&$item, $key) // Note the &$item
    {
      $item = $key . "=" . $item;
    }
    
    [...]
    
    $conditionals = array(
      "foo" => "bar"
    );
    
    array_walk($conditionals, "implodeItem");
    implode(' AND ', $conditionals);
    

    未经测试,但这样的事情应该可以工作。这样您还可以检查 $item 是否是一个数组,并在这些情况下使用 IN。

    【讨论】:

      【解决方案5】:

      如果不是太性感请见谅!

       $data = array('name'=>'xzy',
                    'zip'=>'3432',
                    'city'=>'NYK',
                    'state'=>'Alaska');
      
      
      $x=preg_replace('/^(.*)$/e', ' "$1=\'". $data["$1"]."\'" ',array_flip($data));
      
      $x=implode(' AND ' , $x);
      

      所以输出会是这样的:

       name='xzy' AND zip='3432' AND city='NYK' AND state='Alaska'
      

      【讨论】:

      • 当两个不同的键具有相同的值时,这将在 array_flip 上失败。
      【解决方案6】:

      我使用了这个的变体:

      function implode_assoc($glue,$sep,$arr)
      {
          if (empty($glue)) {$glue='; ';}
          if (empty($sep)) {$sep=' = ';}
          if (is_array($arr))
          {
              foreach ($arr as $k=>$v)
              {
                  $str .= $k.$sep.$v.$glue;
              }
              return $str;
          } else {
              return false;
          }
      };
      

      这很粗糙,但很有效。

      【讨论】:

      • 这实际上在最后留下了$glue。我目前使用类似的东西,添加$str = substr($str , 0, -strlen($glue));可以按预期解决问题。
      【解决方案7】:

      这是一个工作版本:

      //use: implode_assoc($v,"="," / ")
      //changed: argument order, when passing to function, and in function
      //output: $_FILES array ... name=order_btn.jpg / type=image/jpeg / tmp_name=G:\wamp\tmp\phpBDC9.tmp / error=0 / size=0 / 
      
      function implode_assoc($arr,$glue,$sep){
          $str = '';
          if (empty($glue)) {$glue='; ';}
          if (empty($sep)) {$sep=' = ';}
          if (is_array($arr))
          {
              foreach ($arr as $key=>$value)
              {
                  $str .= $key.$glue.$value.$sep;
              }
              return $str;
          } else {
              return false;
          }
      }
      

      【讨论】:

        【解决方案8】:

        我知道这是针对 pdo mysql 类型的情况。但我所做的是构建 pdo 包装器方法,在这种情况下,我执行有助于构建字符串的函数,因为我们使用键,所以有没有可能的 mysql 注入方法,因为我知道我手动定义/接受的键。

        想象一下这个数据:

                   $data=array(
                    "name"=>$_GET["name"],
                    "email"=>$_GET["email"]
        );
        

        你定义了 utils 方法...

        public static function serialize_type($obj,$mode){
        $d2="";
        if($mode=="insert"){
            $d2.=" (".implode(",",array_keys($obj)).") ";
            $d2.=" VALUES(";
        foreach ($obj as $key=>$item){$d2.=":".$key.",";}
        $d2=rtrim($d2,",").")";}
        
        if($mode=="update"){
            foreach ($obj as $key=>$item){$d2.=$key."=:".$key.",";}    
        }
        return rtrim($d2,",");
        }
        

        然后查询绑定数组构建器(我可以使用直接数组引用,但让我们简化):

          public static function bind_build($array){
             $query_array=$array;
             foreach ($query_array as $key => $value) { $query_array[":".$key] =   $query_array[$key]; unset($query_array[$key]); } //auto prepair array for PDO
        return $query_array;    }
        

        然后你执行...

        $query ="insert into table_x ".self::serialize_type( $data, "insert" );
        $me->statement = @$me->dbh->prepare( $query ); 
        $me->result=$me->statement->execute( self::bind_build($data) );
        

        您也可以通过...轻松进​​行更新

          $query ="update table_x set ".self::serialize_type( $data, "update" )." where id=:id";
            $me->statement = @$me->dbh->prepare( $query ); 
        
            $data["id"]="123"; //add the id 
            $me->result=$me->statement->execute( self::bind_build($data) );
        

        但这里最重要的部分是 serialize_type 函数

        【讨论】:

          【解决方案9】:

          试试这个

          function GeraSQL($funcao, $tabela, $chave, $valor, $campos) {
              $SQL = '';
          
              if ($funcao == 'UPDATE') :
                  //Formata SQL UPDATE
          
                  $SQL  = "UPDATE $tabela SET ";
                  foreach ($campos as $campo => $valor) :
                      $SQL .= "$campo = '$valor', ";
                  endforeach;
                  $SQL  = substr($SQL, 0, -2);
                  $SQL .= " WHERE $chave = '$valor' ";
          
              elseif ($funcao == 'INSERT') :
                  //Formata SQL INSERT
          
                  $SQL  = "INSERT INTO $tabela ";
          
                  $SQL .= "(" . implode(", ", array_keys($campos) ) . ")";
          
                  $SQL .= " VALUES ('" . implode("', '", $campos) . "')";         
          
              endif;
          
              return $SQL;
          }
          
          //Use
          $data = array('NAME' => 'JOHN', 'EMAIL' => 'J@GMAIL.COM');
          GeraSQL('INSERT', 'Customers', 'CustID', 1000, $data);
          

          【讨论】:

          • 您应该描述这与其他答案相比有何不同或更好。就目前而言,“试试这个”是相当没用的。