Скрипт для сопоставления имен, отправленных через форму с разрывами базы данных MySQL, когда имя содержит апостроф

У меня есть скрипт, который вводит имена в базу данных MySQL, используя mysql_real_escape_string, чтобы апострофы обрабатывались правильно. Проблема, с которой я столкнулся, связана с приведенным ниже сценарием, который проверяет, соответствуют ли имена, введенные с использованием другой формы, именам, уже имеющимся в моей базе данных, и, если имена найдены, обновляет строку.

Когда я пытаюсь ввести имя с апострофом в форму, обрабатываемую этим скриптом, я получаю сообщение об ошибке о том, что имя не найдено, а имя в сообщении об ошибке содержит обратную косую черту перед апострофом, что, очевидно, является проблема.

Итак, вопрос в том, как я могу изменить приведенный ниже скрипт, чтобы он работал с именами с апострофами?

Спасибо,

Ник

$row_count = count($_POST['name']);
if ($row_count > 0) {

    mysql_select_db($database, $connection);
    $name = array();
    $workshop = array(); 
    $not_found = array();

    for($i = 0; $i < $row_count; $i++) {
        // variable sanitation...
        $name[$i] = mysql_real_escape_string(ucwords($_POST['name'][$i]));
        $workshop[$i] = mysql_real_escape_string($_POST['workshop'][$i]);
    }
    $names = "('".implode("','",$name)."')";

    $not_in = Array();

    // lets say all names doesn't exist in `conference`
    foreach($name as $value) {
        // names in array are keys, not values
        $not_in[$value] = true;
    }


    $query = mysql_query("SELECT Name FROM conference WHERE Name IN $names"); 
    while(list($dbname) = @mysql_fetch_row($query)) {
        // delete those name from $not_in who exists
        unset($not_in[$dbname]);
    }

    // names in $not_in array are keys, not values
    $not_in = array_keys($not_in);

    if(empty($not_in)) {
        // its ok, all names have been found. do the magic.
        for($i = 0; $i < $row_count; $i++) {
            $sql = "UPDATE conference SET Workshop = '$workshop[$i]' WHERE Name LIKE '$name[$i]'";
            mysql_query($sql);
            $body .= "Name: " . $name[$i] . "    Workshop: " . $workshop[$i] . "\n\n";
        }

person Nick    schedule 01.11.2011    source источник
comment
Вы случайно не знаете, для чего mysql_real_escape_string?   -  person Your Common Sense    schedule 01.11.2011
comment
Он добавляет обратную косую черту, чтобы апостроф обрабатывался как строка, а не как код.   -  person Nick    schedule 01.11.2011
comment
Итак, почему вы думаете, что это проблема? Кстати, есть ли шанс, что вы сможете сократить свой код, скажем, в 5 раз?   -  person Your Common Sense    schedule 01.11.2011
comment
Я сократил код не в 5 раз, так как не хочу вырезать ничего важного. В исходной форме использование mysql_real_escape_string не добавляет обратную косую черту к записям в моей базе данных (только к сгенерированному электронному письму). Но он добавляет обратную косую черту к сравниваемым значениям. Если я удалю mysql_real_escape_string из приведенного выше кода для значения имени, в сообщении об ошибке не будет обратной косой черты, но имя по-прежнему не будет найдено в базе данных. Итак, я застрял/запутался!   -  person Nick    schedule 01.11.2011
comment
@Nick: Просто прочитав ваш пример кода, я не вижу никаких проблем, почему он не должен искать имена с апострофами. Вы уверены, что данные в правильном формате? Не могли бы вы сделать снимок ваших данных, например, результатов запроса SELECT, чтобы увидеть, как выглядят записи апострофа? Также может помочь, если вы сможете сделать echo "SELECT Name FROM conference WHERE Name IN $names"   -  person Abhay    schedule 01.11.2011
comment
@Абхай Спасибо. echo "SELECT Name FROM conference WHERE Name IN $names" возвращает, например, ('John O\'Shea') запросы SELECT возвращают имя с апострофом в правильном формате, например. Джон О'Ши   -  person Nick    schedule 01.11.2011
comment
В вашем PHP включены magic_quotes? Из-за этого вы можете дважды экранировать имена. (magic_quotes НАДО отключить и вынести обратно и замучить до смерти медленно и мучительно).   -  person Marc B    schedule 01.11.2011
comment
@Marc B Первое, что я сделал, это отключил magic_quotes, добавив файл PHP.ini с magic_quotes_gpc = Off в ту же папку, что и мой PHP-скрипт. До этого я видел три обратных слэша в сообщении об ошибке.   -  person Nick    schedule 01.11.2011
comment
В порядке. покажите нам образец записи в базе данных (вырежьте и вставьте результаты запроса) и покажите нам ТОЧНЫЙ запрос, который генерирует ваш код. Показывать нам молоток и отвертку бесполезно — нам нужно увидеть, что вы пытаетесь построить с их помощью. И поместите их в свой вопрос. Комментарии НЕ являются хорошим местом для расширенных/отформатированных сегментов кода.   -  person Marc B    schedule 01.11.2011


Ответы (1)


Хм! Я думаю, что, возможно, нашел проблему. Проблема может быть не в запросе, а в коде PHP. Ниже я попытаюсь объяснить на вашем примере John O'Shea.

for($i = 0; $i < $row_count; $i++) {
    // variable sanitation...
    $name[$i] = mysql_real_escape_string(ucwords($_POST['name'][$i]));
    $workshop[$i] = mysql_real_escape_string($_POST['workshop'][$i]);
}
$names = "('".implode("','",$name)."')";

$not_in = Array();

// lets say all names doesn't exist in `conference`
foreach($name as $value) {
    // names in array are keys, not values
    $not_in[$value] = true;
}

После приведенного выше кода массив $not_in будет содержать экранированные ключи, поскольку $name уже содержит значения, экранированные с помощью mysql_real_escape_string(). Отсюда, например:

$not_in[Джон] = истина; $not_in[Джон О\'Ши] = true;

$query = mysql_query("SELECT Name FROM conference WHERE Name IN $names"); 
while(list($dbname) = @mysql_fetch_row($query)) {
    // delete those name from $not_in who exists
    unset($not_in[$dbname]);
}

Теперь $dbname в приведенном выше коде содержит неэкранированные значения, полученные из БД, например John O'Shea без обратной косой черты. Поскольку это не то, что содержит $not_in, unset() не будет работать. Это означает, что все значения апострофа остаются в массиве $not_in.

Таким образом, исправление состоит в том, чтобы сохранить неэкранированные значения в $not_in.

Надеюсь, это имеет смысл!

========== РЕДАКТИРОВАТЬ: В ответ на вопрос о том, как сохранить неэкранированные значения в $not_in:

Идея состоит в том, чтобы сделать побег именно там, где это необходимо. Вот изменения, которые вы можете внести в свой код:

Перепишите первый for(), как показано ниже:

for($i = 0; $i < $row_count; $i++) {
    // variable sanitation...
    //$name[$i] = mysql_real_escape_string(ucwords($_POST['name'][$i]));
    $name[$i] = ucwords($_POST['name'][$i]);
    $workshop[$i] = mysql_real_escape_string($_POST['workshop'][$i]);
}
$names = "('" . mysql_real_escape_string(implode("','",$name)) . "')";

И перепишите оператор UPDATE как:

$sql = "UPDATE conference SET Workshop = '$workshop[$i]' WHERE Name LIKE '" . mysql_real_escape_string($name[$i]) . "'";

Кстати, согласно вашему коду, ОБНОВЛЕНИЕ не запустится, если в базе данных есть одно имя, которого нет. Обязательно ли запускать ОБНОВЛЕНИЕ только в том случае, если все $_POST['name'] найдены в базе данных? Если нет, вы можете значительно сократить объем кода.

Я не тестировал вышеуказанные изменения, но думаю, что они должны работать. Дайте мне знать, если у вас возникнут какие-либо проблемы.

========== РЕДАКТИРОВАТЬ 2: Фрагмент кода для обновления существующих записей и создания ошибок для записей, которые не были

Привет, Ник, я думаю, что только написание приведенного ниже кода должно помочь:

$row_count = count($_POST['name']);
if ($row_count > 0) {
    mysql_select_db($database, $connection);
    for ($i = 0; $i < $row_count; $i++) {
        mysql_query("UPDATE conference SET Workshop = '" . mysql_real_escape_string($_POST['workshop'][$i]) . "' WHERE Name LIKE '" . mysql_real_escape_string($_POST['name'][$i]) . "'");
        $affectedRows = mysql_affected_rows();
        if ($affectedRows == 0) {
            echo '<br>Name did not exist - ' . $_POST['name'][$i];
        }
    }
}

Надеюсь это поможет!

person Abhay    schedule 01.11.2011
comment
Спасибо, это имеет смысл, но я не уверен, как сохранить неэкранированные значения в $not_in. - person Nick; 01.11.2011
comment
Привет, @nick, пожалуйста, ознакомься с частью EDIT в моем ответе выше. - person Abhay; 02.11.2011
comment
Большое спасибо, Абхай. Вышеупомянутые изменения, кажется, работают нормально, и я пометил вопрос как ответ. Если у вас есть какие-либо другие идеи относительно того, как скрипт может работать более эффективно, я был бы рад их услышать. По сути, мне просто нужна ошибка, если какое-либо из имен в моей форме не найдено в базе данных. Вы предлагаете, чтобы, если в форму введено несколько имен, и одно из них не найдено, а другие найдены, сценарий мог бы обновить те имена, которые были найдены, и сгенерировать ошибку для имени, которого нет, так что только имена, которые не были найдены, должны быть введены повторно? - person Nick; 02.11.2011
comment
Это здорово, Ник, что изменения работают на тебя. И да, вы меня правильно поняли. Сценарий может обновлять только те имена, которые найдены, и генерировать ошибки для тех, которые не найдены. Я скоро опубликую фрагмент для этого - person Abhay; 02.11.2011
comment
Ник, см. РЕДАКТИРОВАТЬ 2 выше. - person Abhay; 02.11.2011