Вы result
переменная (помимо того, что перезаписана) не то, что вы думаете.Это PDOStatment
Попробуйте вместо этого
$stmt = $this->con->prepare("select * from ".$table_name." where username = :user_email");
$stmt->execute([
':user_email' => $user_email,
]);
if (false !== ($row = $stmt->fetchColumn()))
{
return $row;
}
Однако это вернет только первый столбец первой строки.Вместо этого вы, вероятно, захотите:
return $stmt->fetchAll(PDO::FETCH_ASSOC);
Я изменил $result
на $stmt
, потому что это не результат, это объект заявления.
Исходные проблемы
В исходном коде (см. Ниже) вы перезаписываете его с возвращением логического значения execute
.
//old code (don't use this)
$result = $result->execute([
':user_email' => $user_email,
]);
//$result = TRUE|FALSE
if ($result->fetchColumn() !== false)
{
return $result;
}
И затем вы пытаетесь вызвать метод логического типа, который хорошоне будет работать.Но проблемы глубже, чем это.Предположим, что вы не перезаписываете его.
//old code (don't use this)
$result->execute([
':user_email' => $user_email,
]);
//$result = PDOStatment object.
if ($result->fetchColumn() !== false)
{
return $result;
}
Теперь результат по-прежнему ваше PDOStatement, что хорошо, но, как я уже сказал, вы не сохраняете извлеченные данные.На этот раз вы возвращаете объект PDOStatement.Это не то, что вы хотите.
Затем, как я уже говорил ранее, если вы сохраните его и вернете.Это все еще, вероятно, не то, что вы после.Потому что fetchColumn()
обращается только к одной строке за раз и только к одному столбцу.
Но у меня нет возможности узнать, что вы хотите.Может быть, это то, что вы хотите?В этом случае ваш запрос меньше идеального.Может быть, вы просто хотите посмотреть, существует ли пользователь с данным адресом электронной почты?В этом случае я бы использовал этот запрос.
$result = $this->con->prepare("SELECT id FROM ".$table_name." WHERE username = :user_email");
$result->execute([
':user_email' => $user_email,
]);
//there is no need to check it (see below)
return $result->fetchColumn();
PDOStatement :: fetchColumn () возвращает один столбец из следующей строки набора результатов или FALSE, если строк больше нет.
Я также могу сказать по вашим материалам, что ваша установка БД, вероятно, неверна, то есть если вам действительно нужна динамическая таблица $table
.Причина, по которой я могу это сказать, заключается в том, что вы не должны дублировать какие-либо пользовательские данные (или любые другие данные, это называется нормализацией), а наличие динамической таблицы означает, что электронная почта может существовать отдельно в двух (или более) таблицах.Если это не так, не делайте его динамичным.Почему это проблема, подумайте, что произойдет, если пользователь изменит свое «электронное письмо» сейчас, потому что оно существует в 2 таблицах (потенциально), вам придется обновлять его в обоих местах.Но хуже всего то, что это усложняет все, что вы делаете с электронными письмами.
Не видя схемы для ваших таблиц, я могу только догадываться об этом и о том, как это исправить.Но обычно вы используете внешний ключ и связываете запись пользователя с этим.Затем с помощью JOIN вы можете получить доступ к электронной почте без дублирования.
Тем не менее, есть несколько случаев, когда это может быть приемлемым, но я не могу узнать, так ли это в вашем случае.Просто краткий пример - отдельная таблица для пользователей и администраторов (в основном это две пользовательские системы).
Безопасность
Последнее, что нужно сделать очень очень осторожно:
"select * from ".$table_name." where username = :user_email"
Проблема здесь в том, что она открыта для SQL-инъекций.Каждый раз, когда вы объединяете переменную в SQL, вы открываете дверь для инъекционных атак.Ну, вы можете сказать, что я передаю в консервированной строке account
.Что нормально, но в точке отказа нет проверки.Поэтому, возможно, через 5 месяцев вы повторно используете этот код и забудете, что никогда не проверяли имя таблицы.Может быть, и нет, но факт остается фактом: если пользовательские данные могут попасть в этот аргумент, у вас нет никакой защиты от инъекции в имя таблицы, вероятность этого есть.
Что-то столь же простое, как этот
public function verify_user($table_name,$user_email){
$allowed = ['account','users'];
if(!in_array($table_name, $allowed )) throw new Exception('Invalid table name');
}
Видите, теперь практически невозможно вставить что-либо в имя таблицы.Кроме того, потому что это тот же метод (в точке сбоя), вы никогда не потеряете эту защиту.Очень легко быть в спешке и скопировать кусок кода, изменить несколько вещей и… ну, вы знаете.
Только мои 3 цента.
ОБНОВЛЕНИЕ
Таким образом, даже если мала вероятность того, что пользовательский ввод может войти в $table
, вы не можете гарантировать его 100%, поскольку из verify_user
у вас нет возможности узнать, откуда поступили данные, но вы доверяете веречто это не пользовательский ввод.Что касается SQLInjection, вы не можете сказать, что это нормально, потому что я буду называть этот метод только определенным образом.Это должно быть 100% доказательство инъекции или настолько близко, насколько это возможно для человека.
Почему это важно, спросите вы?Представьте себе это.
$userprofileobj->verify_user('account --',$_SESSION['user_email']);
Эти два маленьких --
похожи на //
в PHP, но для SQL они закомментируют оставшуюся часть строки в SQL, поэтому ваш запрос становится таким.
"select * from account -- where username = :user_email"
Или (по существу)
"select * from account"
Итак, мы просто изменили то, что делает ваш запрос.Теперь, к счастью, на самом деле невозможно выполнить 2 запроса одновременно в PDO, вы можете сделать это (с небольшой работой) в MySqli.Но из соображений безопасности они в основном покончили с этой способностью.Причина в этом (или, что еще хуже, в создании пользователей БД).
$userprofileobj->verify_user('account; DROP TABLE account --',$_SESSION['user_email']);
Что, если бы вы могли сделать 2 запроса, сделало бы это:
SELECT * FROM account
DROP TABLE account
В любом случае это опасная вещь,следует избегать любой ценой.Быть ленивым (а я ленивым программистом, так что не поймите это неправильно) - это не тот ответ, который вы хотите дать после того, как ваша база данных была скомпрометирована, и вы предоставили данные пользователя третьей стороне.Это просто не вариант.
Все это делает:
if(!in_array($table_name, ['table1', 'table2', ...])) throw new Exception('Invalid table name');
Выдает ошибку, если "needle" $table_name
не находится в "haystack". Консервированный список имен таблиц.Поэтому, если я сделаю это (используя наш пример выше):
if(!in_array('account --', ['table1', 'table2', ...])) throw new Exception('Invalid table name');
Он не найдет account --
в нашем списке table1
и table2
и взорвется, тем самым предотвратив атаку Injection.
Приветствия.