【问题标题】:php $_POST to get values - not the best wayphp $_POST 获取值 - 不是最好的方法
【发布时间】:2011-07-05 09:26:33
【问题描述】:

编辑:

非常感谢您的回答,您的智慧真让我惊讶:)

我正在尝试对 TuteC 的代码进行一些更改,但不知道如何使其正常工作:

$valor = $_POST['valor'];

$post_vars = array('iphone3g1', 'iphone3g2', 'nome', 'iphone41', 'postal', 'apelido');
foreach($post_vars as $var) {
    $$var = "'" . mysql_real_escape_string($_POST[$var]). "', ";
}

$sql = "INSERT INTO clientes (iphone3g1, iphone3g2, nome, iphone41, postal, apelido, valor) VALUES ($$var '$valor')";
$query= mysql_query($sql);

我知道代码有一点作弊,我需要使用子字符串,这样 $$var 就不会在我需要值的末尾输出“,”,而是尝试插入一个变量是一个值($valor = $_POST['valor'];) 出了什么问题?

对于其他试图帮助我的人,非常感谢,我在 stackoverflow 和你一起学习了很多东西。

我有一个包含多个字段值的表单,当我尝试编写一个读取这些值的 php 文件时,结果很严重:

$codigounico= md5(uniqid(rand()));
$modelo=$_POST['selectName'];
$serial=$_POST['serial'];
$nif=$_POST['nif'];
$iphone3g1=$_POST['iphone3g1'];
$iphone3g2=$_POST['iphone3g2'];
$iphone3g3=$_POST['iphone3g3'];
$iphone3g4=$_POST['iphone3g4'];
$iphone3gs1=$_POST['iphone3gs1'];
$iphone3gs2=$_POST['iphone3gs2'];
$iphone3gs3=$_POST['iphone3gs3'];
$iphone3gs4=$_POST['iphone3gs4'];
$iphone41=$_POST['iphone41'];
$iphone42=$_POST['iphone42'];
$iphone43=$_POST['iphone43'];
$iphone44=$_POST['iphone44'];
$total=$_POST['total'];
$valor=$_POST['valor'];
$nome=$_POST['nome'];
$apelido=$_POST['apelido'];
$postal=$_POST['postal'];
$morada=$_POST['morada'];
$notas=$_POST['notas'];

$sql="INSERT INTO clientes (postal, morada, nome, apelido, name, serial, iphone3g1, iphone3g2, iphone3g3, iphone3g4, total, valor, iphone3gs1, iphone3gs2, iphone3gs3, iphone3gs4, iphone41, iphone42, iphone43, iphone44, nif, codigounico, Notas)VALUES('$postal', '$morada', '$nome', '$apelido', '$modelo', '$serial', '$iphone3g1', '$iphone3g2', '$iphone3g3', '$iphone3g4', '$total', '$valor', '$iphone3gs1', '$iphone3gs2', '$iphone3gs3', '$iphone3gs4', '$iphone41', '$iphone42', '$iphone43', '$iphone44', '$nif', '$codigounico', '$notas')";
$result=mysql_query($sql);

这是一个非常难维护的代码,

我能让我的生活更轻松吗?

【问题讨论】:

  • 确保您阅读过SQL injection,然后再进一步执行此代码,例如在生产中。
  • 我知道 SQL 注入,感谢您的警告,我正在进一步改进这部分代码,这对编码器不太友好。非常感谢您的提示。

标签: php


【解决方案1】:
$set = array();
$keys = array('forename', 'surname', 'email');
foreach($keys as $val) {
  $safe_value = mysqli_escape_string($db, $_POST[$val]);
  array_push($set, "$val='$safe_value'");
}
$set_query = implode(',', $set);

然后让您的 MySQL 查询类似于 UPDATE table SET $set_query WHERE...INSERT INTO table SET $set_query


如果您需要验证、修剪等,请在之前执行上述代码,如下所示:

$_POST["surname"] = trim($_POST["surname"];

【讨论】:

    【解决方案2】:

    TuteC 目标很好,但细节失败。

    这让我想知道,为什么没有人有现成的解决方案,却不得不即时设计。以前没有人遇到过同样的问题吗?
    以及为什么大多数人试图只解决部分问题,只获得变量。

    目标不是获取变量。
    目标是获得查询。所以,给自己一个查询。

    //quite handy way to define an array, saves you from typing zillion quotes
    $fields = explode(" ","postal morada nome apelido name serial iphone3g1 iphone3g2 iphone3g3 iphone3g4 total valor iphone3gs1 iphone3gs2 iphone3gs3 iphone3gs4 iphone41 iphone42 iphone43 iphone44 nif codigounico Notas");
    
    $sql    = "INSERT INTO clientes SET ";
    foreach ($fields as $field) {
      if (isset($_POST[$field])) {
        $sql.= "`$field`='".mysql_real_escape_string($_POST[$field])."', ";
      }
    }
    $sql = substr($set, 0, -2); 
    

    此代码将为您创建一个查询,而不会无聊地多次重复相同的字段名称。

    但这仍然不是您可以做出的所有改进。
    一个非常巧妙的东西叫做函数。

    function dbSet($fields) {
      $set    = '';
      foreach ($fields as $field) {
        if (isset($_POST[$field])) {
          $set.="`$field`='".mysql_real_escape_string($_POST[$field])."', ";
        }
      }
      return substr($set, 0, -2); 
    }
    

    将此函数放入您的代码库中,并包含在您的所有脚本中(您有一个,不是吗?) 然后将其用于插入和更新查询:

    $_POST['codigounico'] = md5(uniqid(rand()));//a little hack to add custom field(s)
    if ($action=="update") {
      $id  = intval($_POST['id']);
      $sql = "UPDATE $table SET ".dbSet($fields)." WHERE id = $id";
    }
    if ($action=="insert") {
      $sql = "INSERT $table SET ".dbSet($fields);
    }
    

    因此,您的代码变得极其简短、可靠,甚至可重复使用。
    要处理另一个表,您唯一需要更改的是 $fields 数组。

    您的数据库似乎没有很好地计划,因为它包含看似重复的字段 (iphone*)。你必须规范你的数据库。

    在我的问题中可以找到与准备好的语句相同的方法:Insert/update helper function using PDO

    【讨论】:

      【解决方案3】:

      首先,我建议你创建一个这样的键值数组:

      $newClient = array(
        'codigounico' => md5(uniqid(rand())),
        'postal'      => $_POST['postal'],
        'modelo'      => $_POST['selectName'],
        ...
      );
      

      在这个数组中,key 是您的 MySQL 表的列名。 在您提供的代码中,并非每个字段都是从 POST 数组中直接复制的(有些是计算出来的,有些 POST 的键与表的列名不相等),因此您应该使用灵活的方法。 您仍应指定所有列和值,但只指定一次,以便代码仍然可维护,并且如果有人向您发送损坏的 POST,您将不会出现任何安全错误。至于我,它看起来更像是配置而不是编码。

      那我推荐你写一个类似这样的函数:

      function buildInsertQuery($tableName, $keyValue) {
        $result = '';
        if (!empty($keyValue)) {
          $delimiter = ', ';
          $columns = '';
          $values = '';
          foreach ($keyValue as $key => $value) {
            $columns .= $key . $delimiter;
            $values .= mysql_real_escape_string($value) . $delimiter;
          }
          $columns = substr($columns, 0, -length($delimiter));
          $values = substr($values, 0, -length($delimiter));
          $result = 'INSERT INTO `' . $tableName . '` (' . $columns . ') VALUES (' . $values . ')';
        }
        return $result;
      }
      

      然后,您只需一个函数调用即可构建查询:

      $query = buildInsertQuery('clientes', $newClient);
      

      【讨论】:

      • OP 询问如何减少他的代码,但您的方法保留了最丑陋的部分(变量赋值)。
      • 请再看一下 Souza 的代码。您将如何处理这样的情况:$_POST['selectName'] 应该作为 'modelo' 列名插入?
      • 另外,OP 并没有问如何减少他的代码,而是如何使其更易于维护。
      • 要么手动重写 $_POST 元素,要么给表单输入一个合适的名称
      • 如果有人手动更改了他的 POST 请求,如果您不过滤设置的列名,数据库查询将失败。这意味着您必须指定列列表。
      【解决方案4】:

      要限制您“导入”的 POST 变量,您可以执行以下操作:

      $post_vars = array('iphone3g1', 'iphone3g2', '...');
      foreach($post_vars as $var) {
          $$var = mysql_real_escape_string($_POST[$var]);
      }
      

      编辑:将addslashes 更改为mysql_real_escape_string(感谢@Czechnology)。

      【讨论】:

      • @czechnology:在这种情况下,您可以更改 TuteC 的陈述 - 但主要思想是值得推荐的。
      • 我推荐使用 mysql(i)_real_escape_string 函数而不是 add_slashes 来插入数据库。
      • @Robert,你说得对,我忽略了第一个数组,所以我把它拿回来并删除了我的第一条评论(在你发布之前)。
      • 有一个更短的方法:$escaped_data = array_map('mysql_real_escape_string', $_POST)。而且,顺便说一句,答案中的代码不起作用,正确的代码是foreach($post_vars as &$var)
      • 使用 escaped_data 你不会只过滤你期望的变量。关于代码不起作用,$$var 也可以解决问题。
      【解决方案5】:

      我看到的问题是同名重复四次。这就是我将其减少到两次的方法(您可以将其减少到一次,并进行更多修改)。

      $sql = 'INSERT INTO clientes (postal, morada, nome, apelido, name, serial, iphone3g1, iphone3g2, iphone3g3, iphone3g4, total, valor, iphone3gs1, iphone3gs2, iphone3gs3, iphone3gs4, iphone41, iphone42, iphone43, iphone44, nif, codigounico, Notas) VALUES(:postal, :morada, :nome, :apelido, :modelo, :serial, :iphone3g1, :iphone3g2, :iphone3g3, :iphone3g4, :total, :valor, :iphone3gs1, :iphone3gs2, :iphone3gs3, :iphone3gs4, :iphone41, :iphone42, :iphone43, :iphone44, :nif, :codigounico, :notas)';
      
      preg_match_all('/:(\w+)/', $sql, $inputKeys);
      $tokens = $inputKeys[0];
      $values = array_map($inputKeys[1], function($k){
          return mysql_real_escape_string($_POST[$k]);
      });
      $sql = str_replace($tokens, $values, $sql);
      $result = mysql_query($sql);
      

      根据您希望如何分离逻辑,反向方法可能更有用,您可以指定键名数组并对其进行迭代以生成 SQL 字符串。

      <?php
      
      $inputKeys = array('postal', 'morada', 'nome', 'apelido', 'name', 'serial', 'iphone3g1', 'iphone3g2', 'iphone3g3', 'iphone3g4', 'total', 'valor', 'iphone3gs1', 'iphone3gs2', 'iphone3gs3', 'iphone3gs4', 'iphone41', 'iphone42', 'iphone43', 'iphone44', 'nif', 'codigounico', 'Notas');
      
      $keyList = '(' . implode(',', $inputKeys) . ')';
      $valueList = 'VALUES (';
      foreach ($inputKeys as $k) {
          $valueList .= mysql_real_escape_string($_POST[$k]);
          $valueList .= ',';
      }
      $valueList = rtrim($valueList, ',');
      $valueList .= ')';
      
      $sql = 'INSERT INTO clientes '.$keyList.' '.$valueList;
      $result = mysql_query($sql);
      

      这种方法将键的出现次数减少到一个,并且可能更自然地用于您的应用程序。

      【讨论】:

      • Erisco,非常感谢您的合作/帮助。我看到你的代码时意识到,keylist 和 valuelist 必须相同,否则它不会在我的客户表中正确写入,对吗?
      • 为了缩短你的代码,我滥用了数据库中的列名与 POST 数据中的键名相同的事实。如果这不是惯例,那么只有我的第一个解决方案才有用。
      • 没有一个理由不使用相同的名字。
      【解决方案6】:

      您可以使用 PHP 中一个相当丑陋的部分,称为变量变量,但它通常被认为是一种糟糕的编码习惯。您可以同时包含您的数据库转义。代码看起来像:

      foreach($_POST as $key => $value){
          $$key = mysql_real_escape_string($value);
      }
      

      变量变量手册部分说它们不适用于像 $_PATH 这样的超全局变量,但我认为它可能适用于这种情况。我现在不在可以测试的地方。

      【讨论】:

        【解决方案7】:

        实际上,您可以通过使您的代码更复杂一些来让您的生活更轻松 - 在插入数据库之前转义输入!

        $sql = 
          "INSERT INTO clientes SET
            "postal = '" . mysql_real_escape_string($_POST['postal']) . "', ".
            "morada = '" . mysql_real_escape_string($_POST['morada']) . "', ".
            ...
        

        【讨论】:

        • 我不是在否决 :),我很感谢你的提示,更安全比更有条理的代码更好
        • @Souza,这不是针对你的。 ** 我不认为更长的代码 == 无组织的代码。如果您使用空格或制表符来对齐我提供的代码中的相应内容,则该代码易于阅读和遵循。下一个追随你的程序员可以很容易地看到哪些变量放在了哪里。与自动循环相比,对此类代码进行小的调整(intvaltrim 等)也更容易。
        • 如何让它更安全、更有条理?
        【解决方案8】:

        PHP: extract

        不过要小心,并确保在使用前清理数据。

        【讨论】:

        • 来自 php 手册:Do not use extract() on untrusted data, like user input (i.e. $_GET, $_FILES, etc.). If you do, for example if you want to run old code that relies on register_globals temporarily, make sure you use one of the non-overwriting extract_type values such as EXTR_SKIP and be aware that you should extract in the same order that's defined in variables_order within the php.ini.
        • =( 我说的是Be careful。不推荐的事情并不意味着不能负责任地完成。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多