0

Я начал только-только начал учить PDO, но дело не в этом, а в том, что я не знаю где и почему я не правильно выполняю MySQL запрос.
Вот код функции которая выполняется (не судите строго, исправьте пожалуйста если что-то не правильно делаю, я только начал учить PDO):

//Класс aaa114net, только используемые функции показываю (Данные MySQL верные)...
function doTransaction($uname,$token,$transfer){
        try {
            $data = aaa114net::getMysqliLink();
            $DBH = new PDO("mysql:host=".$data['host'].";dbname=".$data['database']."", $data['username'], $data['password']);
            $DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
            $DBH->beginTransaction();
            $DBH->exec('LOCK TABLES aaa114users');
            $ucoins = $DBH->query("SELECT `coins` FROM aaa114users WHERE `username`=$uname");
            $ucoins->setFetchMode(PDO::FETCH_ASSOC);
            $ucoins->fetch();
            $ucoins = (int) $ucoins[0] + $transfer;
            $pcoins = $DBH->query("SELECT `coins` FROM aaa114users WHERE `token`=$token");
            $pcoins->setFetchMode(PDO::FETCH_ASSOC);
            $pcoins->fetch();
            $pcoins = (int) $pcoins[0] - $transfer;
            $DBH->prepare("UPDATE `aaa114users` SET `coins`=$ucoins WHERE `username`=$uname")->execute();
            $DBH->prepare("UPDATE `aaa114users` SET `coins`=$pcoins WHERE `token`=$token")->execute();
            $DBH->commit();
            $DBH->exec('UNLOCK TABLES');
            $DBH = null;
        }
        catch(PDOException $e) {
            echo "An MySQL Error has been occurred. Please check your logs.";
            file_put_contents('PDOErrors.txt', $e->getMessage(), FILE_APPEND);
        }
    }
function cleanString($string,$maxlength) {
        $string = htmlentities($string, ENT_QUOTES, "UTF-8");
        $string = strip_tags($string);
        $string = stripslashes($string);
        $string = htmlspecialchars($string, ENT_QUOTES);
        $string = mb_substr($string, 0, $maxlength, 'UTF-8');
        return $string;
    }

Как я его выполняю:

$transfer = (int) (new aaa114net)->cleanString($_GET['transfer'],64);
$uname = (new aaa114net)->cleanString($_GET['username'],32);
(new aaa114net)->doTransaction($uname,$token,$transfer);

Вот данные для обработки из строки URL: transfer=3&username=username
Cookie с токеном присутствует, токен верный.

Результат выполнения:

An MySQL Error has been occurred. Please check your logs.

Что в логах выдаёт:

SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1

Помогите пожалуйста исправить и объясните пожалуйста что я не так делаю... P.S. PHP 7.2.1, MySQL Сервер не поддерживает InnoDB тип таблиц.
Что есть в MySQL типах таблиц:

MEMORY              Hash based, stored in memory, useful for temporary tables
MRG_MyISAM          Collection of identical MyISAM tables
MyISAM              MyISAM storage engine
BLACKHOLE           /dev/null storage engine (anything you write to it disappears)
CSV                 CSV storage engine
PERFORMANCE_SCHEMA  Performance Schema
ARCHIVE             Archive storage engine
FEDERATED           FederatedX pluggable storage engine
Aria                Crash-safe tables with MyISAM heritage

Использую бесплатный веб хостинг...

  • cleanString() - Сохранил себе. Полный арсенал! Пригодится (нет) – Manitikyl Aug 09 '18 at 18:54
  • Please check your logs. - ну так покажите нам что там такое, и постараемся помочь. – Manitikyl Aug 09 '18 at 18:56
  • @Manitikyl, ниже написано, что выдаёт в логах – dimankiev Aug 09 '18 at 18:58
  • Вашу проблему мой комментарий не решит (а может и решит), но при исключении выводите стектрейс, чтобы было понятно в какой строчке ошибка. $DBH->prepare("UPDATE aaa114users SET coins=$ucoins WHERE username=$uname")->execute(); нужно заменить на $DBH->prepare("UPDATE aaa114users SET coins=? WHERE username=?")->execute([$ucoins, $uname]); И менять нужно везде, где приходят внешние данные в запрос – ArchDemon Aug 09 '18 at 18:59
  • @ArchDemon, спасибо, сейчас попробую – dimankiev Aug 09 '18 at 19:00
  • Ну другой разговор. Syntax error все понятно, username=$uname - не нравится ему такая передача параметров, нужна такая: username='$uname' – Manitikyl Aug 09 '18 at 19:02
  • prepare-bind-execute - Решит ваши проблемы. И тогда можно смело отказаться от вашей cleanString() – Manitikyl Aug 09 '18 at 19:03
  • @ArchDemon, ваш комментарий ничего не решил, но спасибо за совет. Всё та же ошибка – dimankiev Aug 09 '18 at 19:07
  • Хоть бы сказали в какой строчке. Мы тут не экстрасексы – ArchDemon Aug 09 '18 at 19:11
  • @ArchDemon, простите, сейчас скажу – dimankiev Aug 09 '18 at 19:14
  • #1 transfer.php(23): aaa114net->doTransaction('Taptrue', '398122dc22ae427...', 1) #2 network.php(46): include('класс') #3 {main} (Все что выдало) – dimankiev Aug 09 '18 at 19:19
  • У меня нету ваших файлов. Я не знаю что находится в строке 23 transfer.php и строке 46 network.php – ArchDemon Aug 09 '18 at 19:28
  • @ArchDemon, я рядом с номером строки написал что в строке содержалось. – dimankiev Aug 09 '18 at 19:35
  • @ArchDemon, в 23 ->doTransaction($uname,$token,$transfer); всё что с PDO, а в 46 вызов самого класса с функцией doTransaction (); – dimankiev Aug 09 '18 at 19:37
  • @Manitikyl, bind не обязательно использовать, можно в execute передавать параметры - так проще. Причем в большинстве случаев bind каждой переменной по-отдельности и ненужен вовсе. – Александр Белинский Aug 10 '18 at 07:10

1 Answers1

0
  1. параметры соединения устанавливайте сразу (можно передать четвертым параметром массив опций в конструктор PDO).
  2. не склеивайте строки сами - используйте связывание переменных (иначе в чем смысл переходить на PDO вообще?).
  3. Если Вам нужно только одно значение из строки - так и используйте fetchColumn. И на всякий случай лучше закрывать курсор при выборке (если Вы не выберете все данные, то следующий запрос может не исполниться и выдать ошибку).
  4. Вообще лучше бы вместо функций типа cleanString завести функции, которые будут выполнять последовательно функции: подготовка запроса, исполнение с параметрами, выборка данных. Причем удобно иметь хотя бы три функции: для выбора ровно одного значения, ровно одной строки из базы и для выборки всего массива данных.

Вот так будет работать (скорее всего, если нет других ошибок в коде):

$DBH = new PDO("mysql:host=".$data['host'].";dbname=".$data['database']."", $data['username'], $data['password'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$DBH->beginTransaction();
$DBH->exec('LOCK TABLES aaa114users');
$stmt = $DBH->prepare("SELECT `coins` FROM aaa114users WHERE `username`=?");
$stmt->execute([$uname]);
$ucoins = $stmt->fetchColumn();
$stmt->closeCursor();
$ucoins = (int) $ucoins + $transfer;
$stmt = $DBH->prepare("SELECT `coins` FROM aaa114users WHERE `token`=?");
$stmt->execute([$token]);
$pcoins = $stmt->fetchColumn();
$stmt->closeCursor();
$pcoins = (int) $pcoins - $transfer;
$DBH->prepare("UPDATE `aaa114users` SET `coins` = ? WHERE `username` = ?")->execute([$ucoins, $uname]);
$DBH->prepare("UPDATE `aaa114users` SET `coins` = ? WHERE `token` = ?")->execute([$pcoins, $token]);
$DBH->commit();
$DBH->exec('UNLOCK TABLES');
$DBH = null;

Подробнее почитать можно про: prepare, execute.

Почему так следует делать можете прочитать тут: 1, 2.

Полезная инфа по PDO.