PHP, PDO и SQLSRV выполняется несколько раз в одном операторе INSERT - PullRequest
8 голосов
/ 14 февраля 2012

Я уже некоторое время использую PDO и PHP с серверами MySQL и Apache. Недавно мне было поручено преобразовать устаревшее веб-приложение для бизнеса в новую настройку. Старая установка - это стандартный веб-стек Linux (Apache / PHP / MySQL / Filezilla), а новой установкой будет Windows Server 2003 с IIS / PHP (быстрая установка cgi) / SQL Server 2003 / без FTP.

У меня почти все работает, кроме преобразования оператора MySQL для обновления таблицы с информацией о доступе к файлам. Используя PDO с драйвером SQLSRV и выполнив оператор вставки внутри PHP-скрипта 'file download', вставляет несколько записей в таблицу SQL.

download.php выдает несколько запросов к серверу SQL. Один раз, чтобы проверить существование файла и переменных в одной таблице, а затем обновляет другую таблицу с информацией о доступе.

СМОТРИТЕ НИЖЕ ДЛЯ КОДА download.php

Отладка показывает печать / эхо $ count как 1. Однако при проверке записей сервера SQL всегда отображается БОЛЕЕ, чем одна вставленная. Иногда это всего лишь одна дополнительная строка из двух, но в других случаях она достигает четырех вставляемых операторов EXTRA. $ count остается показанным как 1, в каждом случае.

Этот конкретный скрипт PHP проверяет информацию в базе данных SQL перед вызовом этого оператора вставки. Сначала проверка подлинности для доступа к файлу (успешно), проверка существования файла (успешно), затем обновляет таблицу доступа с информацией для загрузки (ОШИБКА) и, наконец, предоставляет PDF для пользователя (успешно).

Когда я выполняю инструкцию INSERT вручную в Query Analyzer, он выполняется успешно и работает как положено; он вставляет один ряд каждый раз. Похоже, ошибка связана с реализацией execute () в SQLSRV или PDO.

Я искал информацию об этом в stackoverflow, serverfault и всемогущем Google. Возвращаются единственные типы результатов, когда пользователям нужно выполнить несколько запросов / вставок в одном операторе / execute. Где моя проблема противоположна; Однако я хочу выполнить только ОДНУ оператор вставки, более одного всегда выполняется.

Вопрос: почему это происходит и как я могу предотвратить множественную вставку?

ОБНОВЛЕНИЕ ПО ЗАПРОСУ

Код, который обращается к этому файлу, представляет собой одну единственную ссылку с другой веб-страницы. На странице перечислены текущие файлы, к которым пользователь имеет доступ, и представлены ссылки на скрипт download.php для проверки, обновления и фактического предоставления PDF.

Страница просмотра имеет список ссылок (напечатанных в цикле for), расположенных следующим образом:

<a href='download.php?f={$item['name']}&t={$type}' target='_blank'>{$item['name']}</a>

Когда пользователь нажимает на эту ссылку, сценарий, приведенный ниже, запускается в дополнение к другому приведенному выше коду для download.php. Он успешно обслуживает файл PDF. Содержимое отправляется download.php в виде заголовка PHP / встроенного PDF:

СМОТРЕТЬ НИЖЕ ДЛЯ КОДА

Просмотр журналов сервера показывает два GET-запроса к файлу download.php:

2012-02-14 17:44:37 W3SVC1785071458 172.17.31.254 GET /download.php f=06304844-1A.pdf&t=av 4090 - 172.17.31.112 Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/535.7+(KHTML,+like+Gecko)+Chrome/16.0.912.77+Safari/535.7 200 0 0
2012-02-14 17:44:37 W3SVC1785071458 172.17.31.254 GET /download.php f=06304844-1A.pdf&t=av 4090 - 172.17.31.112 Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/535.7+(KHTML,+like+Gecko)+Chrome/16.0.912.77+Safari/535.7 200 0 0

Я тестировал в 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();

?>

Ответы [ 3 ]

1 голос
/ 30 марта 2012

Если вы используете 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>

Каждое обновление увеличивает страницу на два.Я попробовал то же самое, используя один запрос INSERT, и то же самое происходит: с .htaccess вставляются две строки;без, вставляется одна строка.

Итак, если вы используете mod_rewrite и это влияет на download.php, то я считаю, что это проблема.

1 голос
/ 02 апреля 2012

Совет: удалите ?> из конца всех ваших PHP-файлов.Иногда вы получаете невидимое место или символ новой строки после него, и при выводе двоичного формата, такого как PDF, он становится частью PDF и делает его поврежденным.(Следовательно, браузер / программа чтения PDF зависают.)

1 голос
/ 14 февраля 2012

Вы комментируете, что скрипт загрузки вызывается дважды и должен завершиться, если правильное значение не найдено. Это может быть не так, потому что вы неправильно используете PDOStatement rowCount() для проверки найденных файлов. Этот метод предназначен для возврата числа затронутых строк для операторов DELETE, UPDATE или INSERT, но не SELECT .

Если последний оператор SQL, выполненный связанным PDOStatement, был оператором SELECT, некоторые базы данных могут возвращать количество строк, возвращаемых этим оператором. Однако такое поведение не гарантируется для всех баз данных и не следует полагаться на переносимые приложения. http://php.net/manual/en/pdostatement.rowcount.php

Они продолжают с:

Для большинства баз данных PDOStatement :: rowCount () не возвращает количество строк, затронутых оператором SELECT. Вместо этого используйте PDO :: query (), чтобы выдать инструкцию SELECT COUNT (*) с теми же предикатами, что и предполагаемый оператор SELECT, а затем используйте PDOStatement :: fetchColumn (), чтобы получить количество строк, которые будут возвращены.

Если вы вызываете скрипт download.php дважды - 1 раз с неправильными значениями файлов (которые проходят проверку вашего регулярного выражения) и 1 раз с правильными значениями, вполне возможно, что вы вставляете дважды, даже если вы загружаете только один раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...