Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number Помогите с INSERT
в общем не могу понять в чем дело, функция работает с другими данными,отключается после добавления данных об access и status:
Вот изначальный код:
if ($_POST) {
$data = $_POST;
$data['password'] = md5($_POST['password']);
$data['acсess'] = 'user';
$data['status'] = 'activ';
print_r($data);
if (DBselect('*', 'users', 'email', $data['email'],false) or DBselect('*', 'users', 'tel', $data['tel'],false)) {
echo 'Не знаю от куда Вы его взяли, но такой клиент уже есть в базе, другой раз повезет';
}
else {
DBinsert('users',$data);
}
}
Вот сама функция добавления:
function DBinsert($_table, array $_data_insert): bool
{
global $pdo;
//$_data
$_data_key = array_keys($_data_insert); //из массива достает все ключи
$_data_key_str = implode(',', $_data_key);
$_data_value_str = ':' . implode(',:', $_data_key);
$data = $_data_insert;
//print_r($data);
//echo "INSERT INTO {$_table} ({$_data_key_str}) VALUES ({$_data_value_str})";
$sql = $pdo->prepare("INSERT INTO {$_table} ({$_data_key_str}) VALUES ({$_data_value_str})");
//print_r($data);
if (!$sql) {
echo "\nPDO::errorInfo():\n";
print_r($pdo->errorInfo());
}
echo "INSERT INTO {$_table} ({$_data_key_str}) VALUES ({$_data_value_str})";
$sql->execute($data);
echo 'Запись добавлена, давай следующую =)';
return true;
}
Обычно при выводе через echo данных понятно в чем проблема (обычно запятая), но тут что то все вроде проверил, или глаз уже замылился или что то делаю не правильно, просвятите плизз.
Вывод print_r ($data):
Array ( [name] => Гриша [tel] => +8555555555 [region] => Казань [email] => [email protected] [password] => a9d89a1b2e6845b7e2a7d6fc37ce5f11 [koment] => ghje\ [partner_id] => 0 [acсess] => user [status] => activ )
Вывод echo "INSERT INTO {$_table} ({$_data_key_str}) VALUES ({$_data_value_str})":
INSERT INTO users (name,tel,region,email,password,koment,partner_id,acсess,status) VALUES (:name,:tel,:region,:email,:password,:koment,:partner_id,:acсess,:status)
Полное описание ошибки:
Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number: parameter was not defined in D:\Project\zzz.lk.ru\lk\include\db.php:146 Stack trace: #0 D:\Project\zzz.lk.ru\lk\include\db.php(146): PDOStatement->execute(Array) #1 D:\Project\zzz.lk.ru\lk\modul\addclient.php(56): DBinsert('users', Array) #2 D:\Project\zzz.lk.ru\lk\home.php(65): include('D:\Project\zzz....') #3 {main} thrown in D:\Project\zzz.lk.ru\lk\include\db.php on line 146
Ответы (1 шт):
Одна из причин такой ошибки - недопустимые символы в имени плейсхолдера. Например русская буква вместо англиийской. Чтобы проверить, надо скопировать имя плейсхолдера и вывести через urlencode:
echo urlencode("acсess status");
и сразу будет видно где не латинские буквы.
Но главная проблема этого кода конечно не в банальной опечатке, а в том что он, как это часто бывает с подобными функциями, открыт нараспашку для всех SQL инъекций, поскольку напрямую вставляет содержимое РНР переменных в SQL запрос. Что делать категорически не рекомендуется, если только это содержимое не прописано явно в РНР.
Такое стремление "автоматизировать" составление запросов практически всегда приводит к инъекциям. Это тот случай, когда не надо лениться, а надо явно прописать все имена полей, которые доступны для изменения. И проще всего это сделать без всякой функции, а просто написав запрос руками. Это совсем не так трудно, как кажется, осоебно если использовать позиционные плейсхолдеры, а не именованные - с которыми писанины, действительно, получается выше крыши.
Но если так прям совсем невмоготу писать вручную, то хотя бы проверить, что в ключах массива у нас действительно нужные имена полей, а не трехэтажная SQL инъекция:
function DBinsert($pdo, $table, array $data, array $allowed)
{
$keys = array_keys($data);
if (array_diff($keys, $allowed)) {
throw new ErrorException('Incorrect field name');
}
$keys_str = '`'.implode('`,`', $keys).'`';
$values_str = str_repeat('?,', count($keys) - 1) . '?';
$sql = $pdo->prepare("INSERT INTO `$table` ($keys_str) VALUES ($values_str)");
$sql->execute(array_values($data));
}
Соответственно, перед исползованием прописать все имена полей в массив и передать в функцию
$data = $_POST;
$data['password'] = password_hash($_POST['password'],PASSWORD_DEFAULT);
$data['acсess'] = 'user';
$data['status'] = 'activ';
$allowed = ['name', 'password','email', 'access', 'status'];
DBinsert($pdo, 'users', $data, $allowed);
как видно, это совсем нетрудно, но зато можно быть спокойным за безопасность и такой код не стыдно выкладывать на всеобщее обозрение.