#php #ajax #mysqli #prepared-statement
#php #ajax #mysqli #подготовленная инструкция
Вопрос:
Мой вопрос в том, насколько эффективны подготовленные инструкции PHP Mysqli? Из того, что я понял из базового чтения, подготовленные инструкции 1) помогают в обеспечении безопасности с использованием связанных входных данных 2) ускоряют и «сокращают» данные, отправляемые на сервер, путем некоторой «предварительной упаковки» или «подготовки» sql-запроса до некоторой степени, и как только данные доступны, он просто присоединяетданные в подготовленную инструкцию и выполняет ее. Это также помогает при «повторном» использовании одного и того же оператора при повторной вставке одних и тех же данных (разных значений), поскольку оператор готовится только один раз.
Теперь я создаю веб-сайт с несколькими функциями, и все (или большинство) из них используют jQuery и AJAX для получения пользовательского ввода, выполнения некоторых проверок (либо в JS / JQ, либо в PHP), отправки данных в файл PHP PHP_AJAX_Handler.php указанный в URL-адресе AJAX. Файл PHP подготавливает SQL statemtns для вставки данных в базу данных, а затем возвращает сообщения об успешном завершении / сбое JSON. Например, большинство моих функций / функций запрограммированы следующим образом; ниже приведен один файл, который я использую для 1) проверки существующей пары континент-страна и 2) вставки новой пары континент-страна.
HTML:
<input type='text' id='continent'>
<input type='text' id='country'>
<button id='btn1'></button>
<p id='p1'></p>
<p id='p2'></p>
<p id='p3'></p>
jQuery:
$("#btn1")click(function(){
var Cntt = $("#continent").val();
var Ctry = $("#country").val();
$.post("PHP_AJAX_Handler.php",{CN:cntt,CT:ctry},function(DAT)
{ var RET_j = json.PARSE(dat);
if(RET_j.PASS=='FAIL')
{ $('#p1').html(RET_j.PASS);
$('#p2').html(RET_j.MSG1);
}
if(RET_j.PASS=='OKAY')
{ $('#p1').html(RET_j.PASS);
$('#p3').html(RET_j.MSG2);
} }
);
});
PHP_AJAX_Handler.php
<?PHP
session_start();
if( (isset($_POST['CT'])) amp;amp; (isset($_POST['CN'])))
{ require_once ("golin_2.php");
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$ER = "";
$CONN = mysqli_connect($SERVER, $USER, $PASS, $DBNAME);
If($CONN == FALSE)
{ $ER = $ER . "Err: Conn Could not connect to Databse ".mysqli_connect_errno().' '.mysqli_connect_error();
}
else
{ $SQL_1 = "SELECT * FROM sailors.continental_regions WHERE CONTINENT = ? AND COUNTRY = ?";
if(!($STMT_1 = mysqli_stmt_init($CONN)))
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!mysqli_stmt_prepare($STMT_1, $SQL_1)) ///FIRST SET of prepared statement lines
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!mysqli_stmt_bind_param($STMT_1,"ss",$CN, $CT))
{ $ER = $ER . "Err: Stmt Prepare Failed";
}
else
{ if(!(mysqli_stmt_execute($STMT_1)))
{ $ER = $ER . "Err: Stmt_1 Execute Failed";
}
else
{ $RES_1 = mysqli_stmt_get_result($STMT_1);
$NUMROWS_1 = mysqli_num_rows($RES_1);
if($NUMROWS_1>0)
{ $ER = $ER . "Err: duplicate '$CN' '$CT' pair";
}
if($NUMROWS_1==0)
{ $SQL_2 = "INSERT INTO DB.continental_regions (CONTINENT,COUNTRY) values (?, ?)";
if(!($STMT_2=(mysqli_stmt_init($CONN))))
{ $ER = $ER . "Err: Init2 failed";
}
else
{ if(!mysqli_stmt_prepare($STMT_2, $SQL_2)) ///SECOND SET of prepared statement lines
{ $ER = $ER . "Err: Prep2 failed".mysqli_error($CONN);
}
else
{ if(!mysqli_stmt_bind_param($STMT_2,"ss",$CN, $CT))
{ $ER = $ER . "Err: Bind2 failed";
}
else
{
if(!(mysqli_stmt_execute($STMT_2)))
{ $ER = $ER . "Err: Exec failed";
}
else
{ $arr['PASS'] = 'OK';
}
}
}
}
}
}
}
}
}
mysqli_free_result($RES_1);
mysqli_stmt_close($STMT_1);
mysqli_stmt_close($STMT_2);
mysqli_close($CONN);
}
if($ER!=="")
{ $arr['MSG'] = $ER;
$arr['PASS'] = 'FAIL';
}
if($arr['PASS']=="OK")
{ $arr['MSG2'] = "Insert Success";
}
echo json_encode($arr);
}
else
{ header("location: ../Error_Fail.php");
}
?>
Как вы можете видеть, PHP-файл оказывается довольно длинным. Существует один набор инструкций prepare для проверки, существует ли пара CC уже в таблице, затем другой для вставки пары CC.
Из того, что я вижу, для каждого запроса AJAX на добавление новой пары значений операторы mysqli подготавливаются заново. Затем снова для следующего запроса и так далее. Я полагаю, что это создает много накладных расходов и данных на сервер только для обеспечения безопасности. Верно ли это для других людей, разрабатывающих веб-приложения с AJAX-POST-PHP? мне кажется неизбежным, что для каждой подготовки значения могут быть вставлены только один раз? Как обойти подготовку этого оператора один раз и выполнять повторные выполнения только там, где доступны данные? Кажется, я не могу разобраться в факторе «эффективности» подготовленных операторов..
Спасибо.. был бы признателен за совет от некоторых опытных программистов..
Ответ №1:
Вы сказали:
Как вы можете видеть, PHP-файл оказывается довольно длинным.
Это правда, но это не ошибка подготовленных инструкций. Вы, должно быть, изучали разработку PHP из плохо написанного руководства. Этот код не обязательно должен быть таким длинным. На самом деле, его можно сильно сократить.
Просто исправление существующего кода сделало его намного более читаемым. Я использовал mysqli в стиле ООП и удалил все эти if
инструкции. Вместо этого вы должны включить сообщение об ошибках.
<?php
session_start();
if (isset($_POST['CT'],$_POST['CN'])) {
require_once "golin_2.php";
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$ER = "";
$arr = [
'PASS' => "OK",
'MSG2' => "Insert Success",
]; // successful state should be the default outcome
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$CONN = new mysqli($SERVER, $USER, $PASS, $DBNAME);
$CONN->set_charset('utf8mb4'); // always set the charset
// To check existance of data in database we use COUNT(*)
$stmt = $CONN->prepare("SELECT COUNT(*) FROM sailors.continental_regions WHERE CONTINENT = ? AND COUNTRY = ?");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
$NUMROWS = $stmt->get_result()->fetch_row()[0];
if ($NUMROWS) {
$ER .= "Err: duplicate '$CN' '$CT' pair";
} else {
$stmt = $CONN->prepare("INSERT INTO DB.continental_regions (CONTINENT,COUNTRY) values (?, ?)");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
}
if ($ER) {
$arr = [
'PASS' => "FAIL",
'MSG' => $ER,
];
}
echo json_encode($arr);
} else {
header("location: ../Error_Fail.php");
}
Если у вас есть составной УНИКАЛЬНЫЙ ключ для этих двух столбцов в вашей таблице, вы можете удалить инструкцию select. Кроме того, вам следует очистить подготовку ответа. Состояние успешного выполнения должно быть по умолчанию, и его следует заменить сообщением об ошибке, только если что-то пошло не так.
В этом примере я удалил одну инструкцию SQL. Теперь все стало намного проще.
<?php
define('DUPLICATE_KEY', 1062);
session_start();
if (isset($_POST['CT'],$_POST['CN'])) {
require_once "golin_2.php";
$CN = $_POST['CN'];
$CT = $_POST['CT'];
$arr = [
'PASS' => "OK",
'MSG2' => "Insert Success",
]; // successful state should be the default outcome
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$CONN = new mysqli($SERVER, $USER, $PASS, $DBNAME);
$CONN->set_charset('utf8mb4'); // always set the charset
try {
$stmt = $CONN->prepare("INSERT INTO continental_regions (CONTINENT,COUNTRY) values (?, ?)");
$stmt->bind_param("ss", $CN, $CT);
$stmt->execute();
} catch (mysqli_sql_exception $e) {
if ($e->getCode() !== DUPLICATE_KEY) {
// if it failed for any other reason than duplicate key rethrow the exception
throw $e;
}
// if SQL failed due to duplicate entry then set the error message
$arr = [
'PASS' => "FAIL",
'MSG' => "Err: duplicate '$CN' '$CT' pair",
];
}
echo json_encode($arr);
} else {
header("location: ../Error_Fail.php");
}
Что касается производительности.
В этом примере нет проблем с производительностью, и подготовленные инструкции не улучшают и не ухудшают производительность. Я предполагаю, что вы пытаетесь сравнить производительность со статическими SQL-запросами, но в вашем простом примере не должно быть никакой разницы. Подготовленные инструкции могут ускорить ваш код по сравнению со статическими запросами, когда вам нужно выполнять один и тот же SQL много раз.
Если вы обнаружите, что каждый раз писать 3 строки кода слишком много, вы можете создать функцию-оболочку, которая сократит ее для вас до одного вызова функции. Справедливости ради, вам следует избегать использования mysqli самостоятельно. Либо переключитесь на PDO, либо используйте какую-нибудь библиотеку абстракций вокруг mysqli.