PHP将Apostrophes插入不应该的地方

时间:2010-04-01 18:43:40

标签: php mysql codeigniter

不太清楚这里发生了什么,因为这对我来说似乎不是标准做法。但基本上我有一个基本的数据库东西,让用户提交代码片段。他们可以提交最多5个标签。

现在我还在学习,如果这很明显,请原谅我!

这是完成所有工作的PHP脚本(注意那里可能有一些CodeIgniter特定的函数):

function submitform()
  {
    $this->load->helper(array('form', 'url'));

    $this->load->library('form_validation');
    $this->load->database();

    $this->form_validation->set_error_delimiters('<p style="color:#FF0000;">', '</p>');

      $this->form_validation->set_rules('title', 'Title', 'trim|required|min_length[5]|max_length[255]|xss_clean');
      $this->form_validation->set_rules('summary', 'Summary', 'trim|required|min_length[5]|max_length[255]|xss_clean');
      $this->form_validation->set_rules('bbcode', 'Code', 'required|min_length[5]'); // No XSS clean (or <script> tags etc. are gone)
      $this->form_validation->set_rules('tags', 'Tags', 'trim|xss_clean|required|max_length[254]');

    if ($this->form_validation->run() == FALSE)
    {
        // Do some stuff if it fails
    }
    else
    {  
      // User's input values
      $title   = $this->db->escape(set_value('title'));
      $summary = $this->db->escape(set_value('summary'));
      $code    = $this->db->escape(set_value('bbcode'));
      $tags    = $this->db->escape(set_value('tags'));

      // Stop things like <script> tags working
      $codesanitised    = htmlspecialchars($code);

      // Other values to be entered
      $author = $this->tank_auth->get_user_id();

       $bi1 = "";
       $bi2 = "";

       // This long messy bit basically sees which browsers the code is compatible with.
       if (isset($_POST['IE6'])) {$bi1 .= "IE6, "; $bi2 .= "1, ";} else {$bi1 .= "IE6, "; $bi2 .= "NULL, ";}
       if (isset($_POST['IE7'])) {$bi1 .= "IE7, "; $bi2 .= "1, ";} else {$bi1 .= "IE7, "; $bi2 .= "NULL, ";}
       if (isset($_POST['IE8'])) {$bi1 .= "IE8, "; $bi2 .= "1, ";} else {$bi1 .= "IE8, "; $bi2 .= "NULL, ";}
       if (isset($_POST['FF2'])) {$bi1 .= "FF2, "; $bi2 .= "1, ";} else {$bi1 .= "FF2, "; $bi2 .= "NULL, ";}
       if (isset($_POST['FF3'])) {$bi1 .= "FF3, "; $bi2 .= "1, ";} else {$bi1 .= "FF3, "; $bi2 .= "NULL, ";}
       if (isset($_POST['SA3'])) {$bi1 .= "SA3, "; $bi2 .= "1, ";} else {$bi1 .= "SA3, "; $bi2 .= "NULL, ";}
       if (isset($_POST['SA4'])) {$bi1 .= "SA4, "; $bi2 .= "1, ";} else {$bi1 .= "SA4, "; $bi2 .= "NULL, ";}
       if (isset($_POST['CHR'])) {$bi1 .= "CHR, "; $bi2 .= "1, ";} else {$bi1 .= "CHR, "; $bi2 .= "NULL, ";}
       if (isset($_POST['OPE'])) {$bi1 .= "OPE, "; $bi2 .= "1, ";} else {$bi1 .= "OPE, "; $bi2 .= "NULL, ";}
       if (isset($_POST['OTH'])) {$bi1 .= "OTH, "; $bi2 .= "1, ";} else {$bi1 .= "OTH, "; $bi2 .= "NULL, ";}

       // $b1 is $bi1 without the last two characters (, ) which would cause a query error
       $b1 = substr($bi1, 0, -2);
       $b2 = substr($bi2, 0, -2);

// :::::::::::THIS IS WHERE THE IMPORTANT STUFF IS, STACKOVERFLOW READERS::::::::::

       // Split up all the words in $tags into individual variables - each tag is seperated with a space
      $pieces = explode(" ", $tags);
      // Usage:
      // echo $pieces[0]; // piece1 etc

      $ti1 = "";
      $ti2 = "";

      // Now we'll do similar to what we did with the compatible browsers to generate a bit of a query string
      if ($pieces[0]!=NULL) {$ti1 .= "tag1, "; $ti2 .= "$pieces[0], ";} else {$ti1 .= "tag1, "; $ti2 .= "NULL, ";}
      if ($pieces[1]!=NULL) {$ti1 .= "tag2, "; $ti2 .= "$pieces[1], ";} else {$ti1 .= "tag2, "; $ti2 .= "NULL, ";}
      if ($pieces[2]!=NULL) {$ti1 .= "tag3, "; $ti2 .= "$pieces[2], ";} else {$ti1 .= "tag3, "; $ti2 .= "NULL, ";}
      if ($pieces[3]!=NULL) {$ti1 .= "tag4, "; $ti2 .= "$pieces[3], ";} else {$ti1 .= "tag4, "; $ti2 .= "NULL, ";}
      if ($pieces[4]!=NULL) {$ti1 .= "tag5, "; $ti2 .= "$pieces[4], ";} else {$ti1 .= "tag5, "; $ti2 .= "NULL, ";} 

       $t1 = substr($ti1, 0, -2);
       $t2 = substr($ti2, 0, -2); 

       $sql = "INSERT INTO code (id, title, author, summary, code, date, $t1, $b1) VALUES ('', $title, $author, $summary, $codesanitised, NOW(), $t2, $b2)";
       $this->db->query($sql); 

          $this->load->view('subviews/template/headerview');
          $this->load->view('subviews/template/menuview');
          $this->load->view('subviews/template/sidebar');

          $this->load->view('thanksforsubmission');
          $this->load->view('subviews/template/footerview');
    }
  }

对那里无聊的代码感到抱歉。我意识到我可能在那里有一些不好的做法 - 如果是这样,请指出它们。

这是输出的查询的样子(导致错误,根本不查询):

A Database Error Occurred
Error Number: 1136

Column count doesn't match value count at row 1

INSERT INTO code (id, title, author, summary, code, date, tag1, tag2, tag3, tag4, tag5, IE6, IE7, IE8, FF2, FF3, SA3, SA4, CHR, OPE, OTH) VALUES ('', 'test2', 1, 'test2', 'test2   ', NOW(), 'test2, test2, test2, test2, test2', NULL, NULL, 1, 1, 1, 1, 1, 1, 1, NULL)

你会在NOW(),'test2,test2,test2,test2,test2'之后看到这一点 - 我从未要求它把所有这些都放在撇号中。我有吗?

我能做的就是把这些行中的每一行都这样:

 if ($pieces[0]!=NULL) {$ti1 .= "tag1, "; $ti2 .= "'$pieces[0]', ";} else {$ti1 .= "tag1, "; $ti2 .= "NULL, ";}

单引号约$ pieces [0]等等 - 但我的问题是当用户只输入4个标签,或3或其他任何内容时,这种情况会失败。

对不起,如果这是历史上最糟糕的措辞问题,我试过了,但我的大脑已经变得糊涂了。

感谢您的帮助!

杰克

4 个答案:

答案 0 :(得分:2)

很难确定,但我相信这是相关的一线

$tags = $this->db->escape( set_value( 'tags' ) );

由于我不知道set_value()做什么

,所以很难说

我猜这会将字符串test2 test2 test2 test2 test2转换为'test2 test2 test2 test2 test2' - 这些单引号保留在字符串中,即使在您将其爆炸并将它们全部放在一起之后也是如此。

但是,我不得不说,所有这些代码都真的凌乱。实际上,您遇到的问题是您甚至不必担心的问题(动态构建带有可变列的INSERT)。它向我展示了模式中的一个弱点 - 标签应该与code表的N:M(多对多)关系,而不是列的硬限制。你在这里完成它的方式打破了database normalization的第二范式。

所以,你绝对可以选择快速修复并改变你如何转义这些标记值,但我建议你更新你的模式。

答案 1 :(得分:1)

实际上你通过这样做告诉我做了引号:

 $tags  = $this->db->escape(set_value('tags'));

您的数据库类不知道您提供了多个值(应该如何?)。它将来自set_value('tags')的值视为字符串,并且需要转义字符串。

稍后你会爆炸该字符串:

 $pieces = explode(" ", $tags);

应该给你,例如

$pieces[0] = "'test1";
$pieces[1] = "test2";
$pieces[2] = "test3'";

然后你再次连接各个部分,最后给你:“'test1,test2,test3'”。

对我来说,唯一看起来很奇怪的是当您从字符串中删除最后一个字符时结束单引号。

要解决此问题,您可以执行此操作:不要事先逃离set_value('tags')。转义单个值:

if ($pieces[0]!=NULL) {$ti1 .= "tag1, "; $ti2 .= $this->db->escape($pieces[0]) . ',';}

另一个问题:我对您的数据库设计没有任何见解,但看起来tags应该进入自己的表(然后通过一对多或多对多关系进行关联)。否则,您将仅限于六个标签。但这取决于实际目的。

浏览器也是如此:如果新的浏览器进入市场会怎样?你必须扩展你的表。更好的是拥有一个包含所有浏览器的表格,例如

   id   |  browser
-------------------
   0    |    IE6
   1    |    IE7
   2    |    IE8
   3    |    FF2
etc.

并通过中间表将它们与代码相关联:

 --table code_browser

 code_id | browser_id
 --------------------
    0    |    0
    0    |    1
    0    |    3
    1    |    2
    2    |    1

 etc.

答案 2 :(得分:0)

对这个问题有点困惑但是根据我的理解,我认为你有几个问题

a)对于您的ID列,我建议使用0而不是''(在我看来,它会更具可读性。

b)您定义空字符串$ti1 = "";

然后你加入

if ($pieces[0]!=NULL) {$ti1 .= "tag1, "; $ti2 .= "$pieces[0], ";} else {$ti1 .= "tag1, "; $ti2 .= "NULL, ";}

$ ti1现在等于tag1,

  if ($pieces[1]!=NULL) {$ti1 .= "tag2, "; $ti2 .= "$pieces[1], ";} else {$ti1 .= "tag2, "; $ti2 .= "NULL, ";}

$ ti1现在等于tag1, tag2,

我认为你应该做的是

  if ($pieces[0]!=NULL) {$ti1 .= "'tag1', "; $ti2 .= "$pieces[0], ";} else {$ti1 .= "'tag1', "; $ti2 .= "'NULL, ";}

(注意在双引号内添加单引号,所以不要使用 $ ti1现在等于tag1, tag2,你将得到$ ti1现在等于'tag1', 'tag2',

c)我不完全确定这些标签应该代表什么,但我会创建一个带有列ID和 ONE 标签的连接表。这意味着每个id在连接表中最多匹配5行。这将允许您构建更好的查询,以便对这些标记进行计数等。

答案 3 :(得分:0)

正在给出一个变量,所以它被视为这样。

您可以使用if语句直接构建$ sql查询。

   $sql = "INSERT INTO code (id, title, author, summary, code, date";
        if ($pieces[0]!=NULL) {$sql .= ", tag1"; }
        if ($pieces[1]!=NULL) {$sql .= ", tag2"; }
    ...