PHP、PDO和SQLSRV在一条INSERT语句上执行多次

PHP、PDO和SQLSRV在一条INSERT语句上执行多次,php,sql-server,pdo,Php,Sql Server,Pdo,我已经在MySQL和Apache服务器上使用PDO和PHP有一段时间了。我最近的任务是将一个企业遗留的web应用程序转换为一个新的设置。旧的设置是一个标准的LinuxWeb堆栈(Apache/PHP/MySQL/Filezilla),新的设置是一个带有IIS/PHP(快速cgi安装)/SQLServer2003/No FTP的WindowsServer2003 除了将MySQL语句转换为使用文件访问信息更新表之外,我几乎所有的工作都在进行中。使用PDO和SQLSRV驱动程序,并在“文件下载”PH

我已经在MySQL和Apache服务器上使用PDO和PHP有一段时间了。我最近的任务是将一个企业遗留的web应用程序转换为一个新的设置。旧的设置是一个标准的LinuxWeb堆栈(Apache/PHP/MySQL/Filezilla),新的设置是一个带有IIS/PHP(快速cgi安装)/SQLServer2003/No FTP的WindowsServer2003

除了将MySQL语句转换为使用文件访问信息更新表之外,我几乎所有的工作都在进行中。使用PDO和SQLSRV驱动程序,并在“文件下载”PHP脚本中执行insert语句,将多条记录插入SQL表

php确实会向SQL server发出多个查询。一次检查一个表中是否存在文件和变量,然后使用访问信息更新另一个表

请参见下面的download.php代码

调试显示$count的打印/回显为1。但是,检查SQL server记录时,始终会显示多个插入的记录。有时,它只是一个额外的行,总共两个,但其他时候,它高达四个额外的语句被插入$在每种情况下,计数仍然显示为1

在调用这个insert语句之前,这个特定的PHP脚本根据SQL数据库验证信息。首先,验证文件访问的身份验证(成功),验证文件存在(成功),然后使用下载信息更新访问表(错误),最后将PDF提供给用户(成功)

当我将INSERT语句手动发布到查询分析器中时,它成功并按预期工作;它每次插入一行。错误似乎与execute()的SQLSRV或PDO实现有关

我已经在stackoverflow、serverfault和万能的Google上搜索了有关这方面的信息。用户需要在一条语句中执行多个查询/插入/execute时返回的结果的唯一类型。我的问题是相反的;我只希望执行一条insert语句,但总是执行多条insert语句

问题是:为什么会发生这种情况,如何防止多重插入发生

按请求更新

访问此文件的代码是来自另一个网页的单个链接。该页面列出了允许用户访问的当前文件,并提供了到download.php脚本的链接,用于验证、更新和实际提供PDF

查看页面有一个链接列表(打印在for循环中),排列如下:

<a href='download.php?f={$item['name']}&t={$type}' target='_blank'>{$item['name']}</a>
我在Firefox、Opera和IE(6-9b)中进行了测试,结果是一样的

更新两个

将整个download.php文件放在此处:

<?php
session_start();
require("cgi-bin/auth.php");

// Don't timeout when downloading large files
@ignore_user_abort();  
@set_time_limit(0);  

//error_reporting(E_ALL); 
//ini_set('display_errors',1);

function getfile() {
    require('cgi-bin/connect_db_pdf.php');
    //Verify information 
    if (!isset($_GET) || !isset($_GET['f']) || !isset($_GET['t'])) {
        echo "Nothing to do!";
        exit(0);

    }

    //Update variables
    $vuname = strtolower(trim($_SESSION['uname']));
    $file = trim($_GET['f']); //Filename we're looking for
    $type = trim($_GET['t']);//Filetype

    if (!preg_match('/^[a-zA-Z0-9_\-\.]{1,60}$/', $file) || !preg_match('/^av|ds|cr|dp$/', $type)) {
        echo "Non conforming values";
        exit(0);
    }

    try {

        $sQuery = "SELECT * FROM pdf_info WHERE PDF_name=:file AND PDF_type=:type";

        $statm = $conn->prepare($sQuery);
        $statm->execute(array(':file'=>$file,':type'=>$type));
        $result = $statm->fetch();
        $count = $statm->rowCount();
        $sQuery = null;
        $statm = null;

        if ($count == 1 ){ //File was found in the database so let them download it. Update the time as well

            $sQuery = "INSERT INTO access (PDF_name,PDF_type, PDF_time, PDF_access) VALUES (:file, :type, GetDate(), :vuname)";

            $statm = $conn->prepare($sQuery);
            $statm->execute(array( ':vuname'=>$vuname, ':file'=>$file, ':type'=>$type));
            $count = $statm->rowCount();
            $sQuery = null;
            $statm = null;

            $sQuery = "UPDATE pdf_info SET last_view=GetDate(),viewed_uname=:vuname WHERE PDF_name=:file AND PDF_type=:type";

            $statm = $conn->prepare($sQuery);
            $statm->execute(array( ':vuname'=>$vuname, ':file'=>$file, ':type'=>$type));
            $sQuery = null;
            $statm = null;

            //$result is from FIRST SELECT query outside this 'if' scope.
            $file_loc = $result['floc'];
            $file_name = $result['filename'];

            $fileh = fopen($file_loc,'rb');//Send content to browser as inline PDF
            header("Content-Type: application/pdf"); 
            header("Pragma: no-cache");  
            header("Cache-Control: must-revalidate, post-check=0, pre-check=0");  
            header("Content-Length: " . filesize($file_loc));  
            header("Accept-Ranges: bytes");
            header("Content-Disposition: inline; filename={$file_name}");  

            while (!feof($fileh)) { 
                echo(@fgets($fileh, 8192)); 
            } 

            fclose ($fileh); 
            exit(0);

            } else { //We did not find a file in the database. Redirect the user to the view page.
                header("Location: view.php");
            }

            }   catch(PDOException $err) {//PDO SQL error. 
            //echo $err;
            header('Location: error.php');
            exit(0);
         }
}

getfile();

?>

您评论说下载脚本被调用了两次,如果找不到正确的值,应该退出。可能不是这样,因为您误用了PDO语句的
rowCount()
来测试找到的文件。此方法用于返回DELETE、UPDATE或INSERT语句的受影响行数,但不选择

如果关联的PDO语句执行的最后一条SQL语句是SELECT语句,则某些数据库可能会返回该语句返回的行数但是,并非所有数据库都能保证此行为,便携式应用程序不应依赖此行为。

他们继续:

对于大多数数据库,PDOStatement::rowCount()不返回受SELECT语句影响的行数。相反,请使用PDO::query()发出一个SELECT COUNT(*)语句,该语句的谓词与预期的SELECT语句相同,然后使用PDO语句::FETCHTCOLUMN()检索将返回的行数


如果您调用download.php脚本两次——一次使用不正确的文件值(恰好通过了regex测试),一次使用正确的值,则很可能插入了两次,即使您只下载了一次。

如果使用Apache的
mod_rewrite
页面实际上加载了两次。例如,在索引页上使用以下代码:

<?php

  session_start();

  if (!isset($_SESSION['id']))
  {
    $_SESSION['id'] = 1;
  }

  else
  {
    $_SESSION['id'] += 1;
  }

  echo $_SESSION['id'];

?>

在不使用重定向的情况下,每次刷新时输出增量为1。现在添加具有以下内容的.htaccess文件:

<IfModule mod_rewrite.c>
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^(.*)$ index.php
</IfModule>

重写cond%{REQUEST_FILENAME}-F
重写规则^(.*)$index.php
每次刷新都会使页面增加两次。我用一个
INSERT
查询尝试了同样的方法,但同样的情况发生了:使用
.htaccess
,插入两行;如果没有,则插入一行


因此,如果您使用的是
mod_rewrite
,它会影响
download.php
,那么我相信这就是问题所在。

提示:从所有php文件的末尾删除
?>
。有时,你会在它后面得到一个不可见的空格或换行符,当输出一个二进制格式(如PDF)时,它会成为PDF的一部分并使其损坏。(因此浏览器/PDF阅读器挂起。)

请发布更多代码。如果您得到了多个插入,则您的脚本可能被多次调用,或者是因为逻辑错误(例如,当post尚未提交时),或者是因为错误的重写规则。在脚本运行时,请查看服务器的访问日志,以查看您是否实际命中了几次。如果您只执行一个简单的测试文件,其中只包含与插入相关的代码,会发生什么情况?@Michael我在重新启动IIS后检查了服务器日志。该脚本实际上调用了download.php两次。如果
$\u GET
变量未设置或不是预期值,则脚本退出。然后,逻辑转到检索文件信息和下载。为什么这会导致下载脚本或我的函数多次执行?@PenguinCoder请发布插入代码之前出现的代码,该部分检查
$\u GET
并退出。我更改了

<IfModule mod_rewrite.c>
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^(.*)$ index.php
</IfModule>