Основная задумка статьи, помочь новичкам типа меня настроить один из частных случаев взаимодействия exif-php-mysql-php-html.
Я за объективную критику, если у вас есть рациональные предложения по улучшению кода, я с радостью их послушаю и приму к сведению.
Еще раз напомню, что это чуть ли не первое моё написание php-скрипта, где необходимо работать с БД на сервере, поэтому у меня действительно по-тупому всё сделано в одном скрипте.
Просьба не судить очень строго, я лишь начинающий в php и mysql, но основная проблема и цель написания данной статьи в том, что в поисках решения своей проблемы я перерыл очень много форумов и сайтов, но адекватного ответа так и не нашел.
В связи с тем, что изображений достаточно много и работать с ними в последствии надо с помощью скриптов/запросов, было принято решение заносить имена файлов и ключевые слова в БД, а после этого SQL-запросами получать необходимые данные.
Был выработан следующий алгоритм действий:
- Получить список файлов (*.jpeg);
- Циклом пройти по всем файлам и вытащить exif данные (а именно тег keywords);
- Занести имена файлов и ключевые слова в базу данных;
- Получение списка файлов по определенному тегу;
-
Самое приятное— исправление ошибок.
Что происходит дальше пока не сильно важно, потому что основные проблемы родились именно на пункте 4.
Итак, разберемся что у нас происходит на каждом этапе:
1. Получить список файлов (*.jpeg)
Тут особо проблем не возникло, спасибо функции glob();
$filelist = glob("*.jpg"); // получаем список файлов (скрипт лежит в папке с файлами)
2. Вытащить exif данные (тег keywords)
Здесь тоже проблем особо не возникает. Единственный момент, для того, чтобы метатеги выводились в адекватном виде, то было бы хорошо, чтобы они были написаны латиницей.
Пара циклов, проверка, как говорится тяп-ляп и готово.
foreach ($filelist as $name) { // Перебираем файлы, имена заносим в переменную $name
$exif = exif_read_data($name, 0, true); // Получаем метаданные из файла
foreach ($exif as $key => $section) { // Циклично получаем все теги метаданных
foreach ($section as $setting => $val) { // Получаем значение тега
if ($key == "IFD0" && $setting == "Keywords") { // Ждем когда доберемся до тега keywords
echo "$key.$name: $val<br />"; // Выводим полученное значение
}
}
}
}
3. Занести полученную информацию в базу данных
Данная статья не про развертывание бд на сервере, поэтому сразу перейдём к делу.
Нам необходимо:
- Подключиться к БД
$con = mysqli_connect('<server_name>','<user_login>','<user_pass>', '<database>') or die ("html>script language='JavaScript'>alert('Не удается подключиться к базе данных. Повторите попытку позже.'),history.go(-1)/script>/html>"); // Подключение к БД с выводом ошибки, если подключение не удалось
- Создать таблицу (если её нет)
Я использовал поля Number (порядковый номер/id), filename (имя файла оригинального изображения) и keywords (ключевые слова)$sql_ins_first = "CREATE TABLE IF NOT EXISTS test (id int(11) NOT NULL AUTO_INCREMENT, filename varchar(255) CHARACTER SET utf8 NOT NULL, keywords varchar(255) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1"; // Создание таблицы (если её нет) mysqli_query($con, $sql_ins_first); // Отправить запрос, который описан выше
- Добавление записей в таблицу
$sql_ins_second = "INSERT INTO <table_name> (filename, keywords) VALUES ('$name', '$val')"; // Запрос на добавление записей в таблицу (на каждой итерации цикла) if (mysqli_query($con, $sql_ins_second)) { // Проверка выполнения запроса echo "$name - New record created successfully<br />"; // Информация успешно занесена в базу } else { echo "Error: " . $sql_ins_second . "<br>" . $con->error; // Ошибка записи данных в БД }
4. Получение списка файлов по определенному тегу
Здесь начинается самое интересное, потому что именно отсюда мы плавно втекаем в ошибки, которые устраняются не совсем очевидным образом
Привожу сразу пример моей функции с комментариями
function select_sql($key) // На вход подаём тег, который нам нужен для вывода конкретного набора файлов
{
/* Запрос: Выбрать поле "filename" из таблицы "test",
где символьная строка "keywords" соответствует шаблону '%$key%' */
$sql_select = "SELECT filename FROM test WHERE keywords LIKE '%$key%'";
$result = mysqli_query($con, $sql_select); // Заносим результат в переменную
/* извлечение ассоциативного массива */
while ($row = $result->fetch_assoc()) { // Обрабатываем все полученные записи в результате нашей выборки
echo "<div><p>".$row['filename']."</p><img src=\"".$row['filename']."\" style=\" width: 200px\" /></div>"; // Вывод на страницу имени файла и превью нашего изображения
}
}
А перед тем, как мы перейдём к следующему пункту, я скажу, что если собрать этот код воедино, то мы получим проблему (возникает не у всех, если сервер настроен правильно со всех сторон, но это не точно), которая решается не совсем тривиально (как мне кажется).
У меня получилась следующая ситуация.
Полученная строка ключевых слов к изображениям в формате ASCII, подаваемая на вход переменная с нужным тегом также в формате ASCII, но при отправке запроса (не важно, через скрипт или через PhpMyAdmin) на выходе получаем NULL.
Попробовав множество вариантов (среди них обязательно был вариант с использованием utf8_encode(), который, кстати, не работал).
5. Самое приятное — исправление ошибок
Первое, что предлагает нам интернет и люди, не до конца разобравшиеся с документацией, это проверить «А склеили ли вы переменную с запросом?», «А нет ли у вас лишних пробелов?», «А правильно у вас собирается запрос?», «А что у вас за БД?» и т.д. т.п
По факту, мы просто получили ситуацию, когда всё работает (теги записались в БД, запрос собирается правильно, но на выходе мы всё равно имеем пустую выборку).
Логически приходим к выводу, что проблема в кодировке… Но что делать, если стандартная функция utf8_encode() не работает?
Скажу честно, я перепробовал безумно много вариантов, но в итоге у меня родилась идея «с промежуточной кодировкой» — я сначала попробовал перевести из ASCII в другую кодировку, а потом уже в UTF-8, но снова ничего не получалось.
Здесь я почуял неладное, а есть ли какой-то список кодировок и есть ли у меня там что-либо?
Немного погуглив, нашел и попробовал установить в начале скрипта mb_detect_order(«eucjp-win,sjis-win,UTF-8»); и сразу же получил кодировку всех строк в eucjp-win. Осознав, что можно попробовать переставить utf-8 на первое место, у меня всё пошло как по маслу, но было очень важно после считывания метаданных из картинок — экранировать символы.
Представляю вашему вниманию итоговый скрипт:
<?php
mb_detect_order("UTF-8,ascii,eucjp-win,sjis-win"); // Установка списка кодировок
$con = mysqli_connect('<server_name>','<user_login>','<user_pass>', '<database>') or die ("html>script language='JavaScript'>alert('Не удается подключиться к базе данных. Повторите попытку позже.'),history.go(-1)/script>/html>"); // Коннект к БД
$sql_ins_first = "CREATE TABLE IF NOT EXISTS test (id int(11) NOT NULL AUTO_INCREMENT, filename varchar(255) CHARACTER SET utf8 NOT NULL, keywords varchar(255) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (id)) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1";
mysqli_query($con, $sql_ins_first); // Проверка наличия/создание таблицы
$filelist = glob("*.jpg"); // Получение списка файлов в директории с расширением *.jpg
foreach ($filelist as $name) { // Цикл по файлам
$exif = exif_read_data($name, 0, true); // Считывание метаданных
foreach ($exif as $key => $section) { // Цикличный проход по тегам
foreach ($section as $setting => $val) { // Получение значения тега
if ($key == "IFD0" && $setting == "Keywords") { // Проверка соответствия нужного нам тега (keywords)
$val1 = mysqli_real_escape_string($con, $val); // Экранирование символов в полученной строке значений тега Keywords
$val1 = str_ireplace(";", "; ", $val1); // Заменяем ";" на "; "
/* При проверке текущего состояния строки после экранирования
мы увидим между всеми символами "\0",
а это значит, что нам необходимо их удалить,
чтобы получить адекватную строку */
$val1 = str_ireplace("\\0", "", $val1); // Заменяем "\0" на "" (пустую строку) и не забываем экранировать слеш
$sql_ins_second = "INSERT INTO test (filename, keywords) VALUES ('$name', '$val1')"; // Запрос на добавление записей в БД
/* Проверка записи данных в БД */
if (mysqli_query($con, $sql_ins_second)) {
echo "$name - New record created successfully<br />";
} else {
echo "Error: " . $sql_ins_second . "<br>" . $con->error;
}
}
}
}
}
$con->close(); // Закрыть подключение
/* Объявление функции select_sql() */
function select_sql($key)
{
$con = mysqli_connect('<server_name>','<user_login>','<user_pass>', '<database>') or die ("html>script language='JavaScript'>alert('Не удается подключиться к базе данных. Повторите попытку позже.'),history.go(-1)/script>/html>"); // Коннект к БД
$sql_select = "SELECT filename FROM test WHERE keywords LIKE '%$key%'"; // Запрос на выборку данных по определенному тегу
$result = mysqli_query($con, $sql_select); // Заносим результат в переменную
while ($row = $result->fetch_assoc()) { // извлечение ассоциативного массива
echo "<div><p>".$row['filename']."</p><img src=\"".$row['filename']."\" style=\" width: 200px\" /></div>"; // Вывод названий файлов и изображений на страницу
}
$con->close(); // Закрыть подключение
}
$string_key = utf8_encode('test'); // Преобразование переменной в кодировку UTF-8 (на всякий случай)
select_sql($string_key); // Вызов функции с нужным тегом
?>
Скорее всего, второе подключение нам не нужно, потому что по хорошему, его подсасывать с другого php-скрипта, но это в будущем, потому что статья ориентировалась на другой аспект.
И, как говорится:
Спасибо за внимание! Я готов выслушать ваши вопросы!
Комментарии (60)
mastacamp
30.11.2021 16:45+3Почему бы не работать напрямую с $exif['IFD0']['Keywords'] ? судя по коду и $key и $setting являются ключами массива, значит к ним можно получить доступ напрямую, не делая лишних foreach.
Кодировку можно задать после соединения с базой, например так: mysqli_query($con,"SET NAMES utf8mb4_bin"); mysqli_query($con,"SET CHARSET utf8mb4");mysqli_query($con,"SET SESSION collation_connection = 'utf8mb4_bin'");
Если функция select_sql() вызывается часто то есть смысл делать соединение с базой вне функции, ну или проверять наличие соединения внутри функции, чтобы не пересоединяться с базой данных ради одного запроса
Ну и сейчас должны пойти комментарии от других комментаторов :)
LearnPC Автор
30.11.2021 18:11Отвечу по пунктам:
Спасибо, учту
Я пробовал что-то подобное, но у меня всё равно выводилось NULL, но я перепроверю
Об этом я написал в конце статьи, что это тупой скрипт для локальной БД для обучения, что по хорошему вынести в отдельный скрипт - понимаю.
Спасибо за обратную связь!
FanatPHP
01.12.2021 19:02.2.Можно, но не нужно. Для этого есть специальная функция mysqli::set_charset()
И при этом очень желательно не путать collation и charset.
debagger
30.11.2021 18:16+1Я не особо знаю пхп, но вот это вот:
$sql_select = "SELECT filename FROM test WHERE keywords LIKE '%$key%'";
выглядит как возможность для sql-иньекции...
LearnPC Автор
30.11.2021 18:40Скажу честно, ещё не изучал защиту от sql-инъекций. Это лишь начало моего пути, прежде чем браться за какой либо проект, я обязательно изучу данный вопрос.
Спасибо за обратную связь!
FanatPHP
01.12.2021 19:13Это всё равно что написать статью в медицинский журнал про то как сделать операцию пациенту, и при этом писать что "вопрос стерилизации я ещё не изучал".
alex-khv
30.11.2021 18:59Судя по
<img src="".$row['filename']."" style=" width: 200px" />
Через инекцию можно будет вытянуть любой файл. Возможно что-то даже записать и повысить привилегии.
BigDflz
30.11.2021 19:09-1используйте хранимые процедуры и это не будет поводом для боязни инъекций
alex-khv
01.12.2021 08:34-2Хранимые процедуры уменьшают поверхность атаки, но не являются панацеей от инъекций.
Автору лучше изучить ORM, это на данном этапе развития даст больше чем закапывание в дебри SQL.
FanatPHP
01.12.2021 18:58+1Учить ORM не выучив SQL - это стрелять себе даже не в ногу, а сразу в голову.
BigDflz
02.12.2021 22:18уменьшают настолько, что мне показали только один вариант, но для этого надо было написать специально подготовленную для этого хранимую процедуру.
FanatPHP
03.12.2021 09:34Вот именно, что только один вариант и что под каждый запрос свою процедуру. То есть в реальном коде это невозможно будет использовать.
Подготовленный запрос сделать в сто раз проще и надёжнее.
BigDflz
03.12.2021 17:17люди делают проекты с 1000+ процедурами - и не останавливаются. возможности хранимых процедур намного больше "подготовленных запросов"
если для Вас невозможно использовать - "Вы проста не умеете их готовить..."
а если говорить о mssql - то хранимые процедуры ещё и компилируются, что так же повышает скорость их работы.
FanatPHP
03.12.2021 17:27+1:)))
Как-то вы очень резко перешли от "Мне тут пацаны показали" к "вы не умеете" :)
Давайте вы будете говорить только на основании собственного опыта. Итак, сколько процедур написали лично вы? Можете показать пример одной?
А я пока дам вам несколько советов:
1000 запросов - это маленькая домашняя страничка. Не забывайте, что вы начали разговор про процедуры в контексте защиты от инъекций. То есть каждый запрос в приложении должен выполняться через хранимую процедуру.
Возможности хранимых процедур действительно больше подготовленных запросов (в кавычки брать совсем не обязательно, это не инопланетная технология), но и поддержка тоже сложнее. Поэтому использовать их для защиты от инъекций - это из пушки по воробьям.
Никогда не ввязывайтесь в дискуссию на тему, про которую вы только что-то слышали краем уха. Тем более в таком снисходительном тоне.
BigDflz
03.12.2021 19:18-1я не просто слышал, я видел этот проект в разработке. и да, я каждый запрос держу в хранимой процедуре, поддержка хранимых процедур не чуть не сложнее любого программного кода , если пользоваться заточенного для этого инструмента. рекомендую DbForge. защита от инъекций в данном случае как приятный бонус.
редактор с подсветом, пошаговая отладка, синхронизация баз. рефакторинг. и куча других плюшек делают работу с базой приятной и быстрой.
вызов хранимки в коде прост, ничуть не сложнее чем запроса. но только одна строка.
в ide основного кода нет подсветки кода sql, это строка, обыкновенная строка. а запросы бывают на 1-3 экрана . в ide для sql его автоматом отформатируют, разукрасят, проверят. одно нажатие и выполнение.
FanatPHP
03.12.2021 19:50Я не буду комментировать всю ту многословную чушь, которую вы тут понаписали вместо одного короткого примера кода из вашего "реального" проекта, а просто порекомендую вылезти из-под той коряги, под которой вы провели последние лет десять, и поставить себе какую-нибудь современную IDE :)
BigDflz
03.12.2021 19:59+1когда нечего сказать - начинаются такие ответы.
любая современная ide не круче другой современной ide специально узко заточенной под конкретную субд.
FanatPHP
03.12.2021 20:12Верно, не круче. Но вы-то утверждаете, что она хуже
Что, мягко говоря, не проходит проверку реальностью ;-)
BigDflz
03.12.2021 21:02кто сказал хуже? посмотрите линейку продуктовDbForge - под каждую субд -своя ide. я пользуюсь для mysql и очень доволен.
FanatPHP
03.12.2021 23:08Вы. Вы воображаете себе, будто "в ide основного кода нет подсветки кода sql". Но на предложение поставить себе современную IDE и убедиться, что это не так, начинаете по своему обыкновению юлить и вертеться как уж на сковородке.
FanatPHP
03.12.2021 18:32+1Интересно, что два года назад вы уже участвовали в дискуссии про защиту от инъекций с тем же самым тезисом, и, хотя ваш оппонент и сам не понимает ни как работают процедуры, ни как с их помощью можно сделать защиту, вроде бы даже согласились с его правотой.
Я не буду здесь углубляться в тонкости синтаксиса процедур, а приведу куда более простой пример инъекции на базе кода из того топика
$id = "0);DELETE FROM users; -- "
$db->query('CALL get_order(' . $id . ')');"Ну что, сынку - помогли тебе твои ляхи?" :)
BigDflz
03.12.2021 19:27прежде чем такое советовать надо проверять
вот это 0);DELETE FROM users; --
в виде like "%0);DELETE FROM users; --%" и будет искаться в поле keywordsFanatPHP
03.12.2021 19:35Совершенно верно! Проверять надо обязательно! Вот и проверьте :)
Введите строчку
get_order(0);DELETE FROM users; -- )
В своем клиенте базы данных, и посмотрите на результат. Только очень прошу - не на боевой базе!
BigDflz
03.12.2021 19:44я то проверял, вот только надо быть полным идиотом, что такое в коде допустить.
я делаю так:
String f = String.format("{call %s(%s,'%s')}", "x13", s[0], s[1]);
CallableStatement proc = con.prepareCall(f);FanatPHP
03.12.2021 20:18О! Да вы не настолько безнадежны, каким хотите казаться :)
Но вам надо определиться: всё таки "вот это 0);DELETE FROM users; --<br>в виде like "%0);DELETE FROM users; --%" и будет искаться в поле keywords ", или "надо быть полным идиотом"
Потому что неувязочка получается :)
BigDflz
03.12.2021 20:24если есть паранойя инъекций - то можно проверять всё что угодно перед вызовом. если у Вас такая строка
get_order(0);DELETE FROM users; -- ) считается реальной ситуацией то мне нечего на это сказать.
ЗЫ <br> заменяется Shift+Enter
FanatPHP
03.12.2021 20:44Во-первых, хочу поблагодарить за доставленное удовольствие! Я прекрасно скоротал время перед концертом !
А во-вторых осмелюсь напомнить исходную фразу, с которой и началась эта увлекательная дискуссия:
используйте хранимые процедуры и это не будет поводом для боязни инъекций
На мой не искушённый взгляд он немного противоречит утверждению "если вас заедает паранойя, то надо проверять что-то ещё".
Или, выражаясь немного вульгарно, вы окончательно заврались :))
FanatPHP
03.12.2021 20:30Ахаха, вы добавили код к своему комментарию! Он просто прекрасен! Вы просто семимильными шагами осваиваете реальную защиту от инъекций! Вы уже продвинулись на целый шаг!
Мы пока оставим в стороне неудобный вопрос, *а зачем нам вообще тогда нужна процедура*, и займёмся куда более насущным - а именно, вопросом *экранирования кавычек* в SQL запросах, который прекрасно освещен в известном комиксе про мальчика по имени Bobby Tables. Вам же, несомненно, он знаком? ;)
BigDflz
03.12.2021 20:56-1если Вас пугают кавычки - то они должны вас пугать и в использовании простых запросах.
я не параноик, поэтому я останавливаюсь на приемлемой стадии защиты.
FanatPHP
03.12.2021 23:11Все верно. Пугают. Как и любого разработчика, знакомого с SQL инъекциями. Именно поэтому я и использую не "простые" запросы, а подготовленные. Видите, вы тоже можете прийти к этому простому выводу. Но ваше самомнение, помноженное на совершенно дремучее невежество, вам в этом очень мешает.
BigDflz
04.12.2021 03:33-1Вы ответили на этот комент, но на комент с проверкой промолчали....
обозвали меня вруном, невежей, и прочее.
и, скорее всего, Вы минуснули в карму.
И это называется демократия по хабру.
BigDflz
04.12.2021 04:18.те же хранимые процедуры могут иметь вид ваших подготовленных запросов
String SQL = "{call getlastname (?, ?)}";
CallableStatement cs = cn.prepareCall(SQL);
cs.setInt(1, 1658468);
cs.registerOutParameter(2,java.sql.Types.VARCHAR);
cs.execute();
String lastName = cs.getString(2);
о чем Вы скромно промолчали.
FanatPHP
04.12.2021 09:35+1Могут. И должны. И все нормальные разработчики именно так и поступают.
Но тогда возникает вопрос - а зачем нам тогда вообще каждый запрос заворачивать в процедуру? Если всё затевалось вроде бы для защиты от инъекций, но оказывается и сам вызов процедуры надо защищать? Именно защищать, без всех этих клоунских "нужно быть идиотом", "если у вас паранойя", "если пугают кавычки".
Но если запрос УЖЕ защищён, если подготовленным запросом можно безопасно выполнить как обычный запрос, так и вызвать процедуру, то зачем тогда нам тогда вообще процедуры? Да, сами по себе, иногда процедуры бывают нужны. Но каждый-то запрос тогда зачем? Если всё равно эта "защита" не работает, и при этом есть действительно надежный способ, который к тому же гораздо проще? Зачем заворачивать каждый запрос в отдельную процедуру? Зачем при изменении запроса не только обновлять код на сервере, но и накатывать миграцию в БД? Зачем писать лишний код и совершать лишние телодвижения?
И это мы ещё не переходили к содержимому процедур, которое вы скромно стесняетесь показывать. И правильно стесняетесь. Потому что сама по себе процедура - не какая-то волшебная палочка, положил в неё запрос, и он "в домике". Сам код процедуры тоже надо писать строго определенным образом. И если этого не делать, то инъекция будет уже в нём.
BigDflz
04.12.2021 10:36-1я показал проверку - Вы в кусты.
зачем заворачивать? если Вам приятно разглядывать в коде строку на несколько экранов без подсветки - Ваше право. Если Вам нравится отлаживать запрос компилируя код - Ваше право.
я предпочитаю все это делать предназначенным для этого инструментом.
да , конечно, код процедуры надо писать строго определённым методом - справа на лево. иначе злой чебурашка в него залезет и скушает.
где увидели в процедурах лишний код?
Вы так усиленно просили выполнить код - я выполнил, он выдал полную несостоятельность Ваших утверждений, причём на практике. поэтому я делаю вывод, что и остальные Ваши претензии имеют такой же уровень осведомлённости. следовательно нет смысла дальше дискутировать.
BigDflz
03.12.2021 21:20не поленился , проверил
вот такой код
String f = String.format("{call %s();DELETE FROM TABLE1 ;-- }", "page3_xxx010 ");
вот что получаю:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELETE FROM TABLE1 ;-- ()' at line 1 at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
slivorezka
30.11.2021 18:20+1Ув. автор, вместо траты время на написания этой статьи в стиле ответа на Stack Overflow, лучше потратьте время на прочтения вот этой статьи PHP: Правильный Путь.
А еще лучше, чтобы избежать многих простых ошибок, установите и настройте в своей IDE: Psalm, PHPStan или PhpCsFixer.Ничего личного, просто при виде этого кода мои глаза наполнились слезами, и это не слезы счастья, увы.
FanatPHP
01.12.2021 20:45+1Извините, а вас не затруднит написать чуть подробнее: какой именно из этих инструментов вы порекомендуете? И с каким набором правил? И какие конкретно проблемы в этом коде он поможет исправить?
alex-khv
30.11.2021 19:00Статья напоминает AI компилятор.
Человек пишет код, а потом в комментариях ему пишут его ошибки.
LearnPC Автор
01.12.2021 19:18Наберусь смелости ответить, что поставленную перед собой задачу я выполнил, а в комментариях больше указывают на доработку с целью обеспечения безопасности, оптимизации и др.
varanio
01.12.2021 17:57Зачем это тут на Хабре? Ужасный код начинающего программиста
LearnPC Автор
01.12.2021 19:16Если данный код настолько ужасен, будьте добры, объясните почему. Без какого либо аргументирования не очень приятно читать подобные комментарии
FanatPHP
01.12.2021 19:28Вам этих аргументов уже насовали полную панамку.
С БД не умеете работать совсем.
Героическая борьба с кодировками на пустом месте, какой-то ад с пересадками.
Банальное неумение обратиться к массиву по индексу (это совсем уж какой-то детский сад)
Если этого мало - классический винегрет из SQL и HTML, которые в нормальном коде вообще никогда не должны пересекаться. Неумение правильно обрабатывать ошибки.
В итоге без ошибок вам удалось написать только одну команду - glob(). И вы всё ещё не уверены, что с вашим кодом не так? Всё ещё терзают сомнения?
FanatPHP
01.12.2021 20:51Но самое плохое, из-за чего я уже жалею, что исправил вам карму - ваши ответы похожи на реплики китайского болваничка: "учту", "прочитаю", "приму к сведению". Когда? Когда-нибудь потом? Вы не задали ни одного уточняющего вопроса. Не попытались разобраться ни в одной теме сразу же. Не сделали самостоятельно ни одного вывода из сделанных вам замечаний. Не открыли для себя ничего нового.
Вот всё это - действительно очень плохо. Ошибки в коде - это ерунда. Но если не горят глаза, если нет интереса сразу же закатать рукава и разобраться, если все исправления сразу же откладываются в долгий ящик - вот тогда ошибки и останутся неисправленными.
LearnPC Автор
01.12.2021 21:24Я не готов задавать уточняющие вопросы до того, как адекватно прочту на что мне указали и с чем это едят.
Мои глаза не потухли, даже с учётом количества какашек в моей "панамке", просто сейчас я пытаюсь уложить тонну материала в голове.
И да, я прекрасно понимаю, что у меня получился "костыль", "говнокод" и мало что здесь можно найти хорошего. Действительно дали кучу полезных комментариев с указанием того, на что стоит обратить внимание, но при этом я не вижу смысла заходить с вердиктом "Говнокод!" без аргументов, если до тебя это уже сказали (это относится к пользователю varanio).
А за карму отдельное спасибо, потому что это одна из причин, почему глаза не потухли после этой статьи
LearnPC Автор
01.12.2021 21:37Ко всему прочему, чтобы банально прочесть что-то по существу написанных комментариев, надо явно не 1 день (с учётом того, что у меня все таки есть основная работа). Поэтому обещаю, что скоро вернусь с вопросами, но более подготовленный
t3chn0ph0b
mysqli_connect? Серьезно?
t3chn0ph0b
Дополню, а то выглядит коммент не очень :)
Если вы пишете для новичков, то приучайте их сразу
не писать на PHPк хорошему. Как минимум использовать PDO для работы с БД.LearnPC Автор
Это был один из самых простых вариантов реализации и я писал, что сам новичок и хочу помочь другим, если у них появится та же проблема, что и у меня.
Спасибо за обратную связь
SerafimArts
С каких пор PDO стал чем-то хорошим? Это два разных инструмента для разного вида задач. В том же PDO нет поддержки асинхронных запросов (в отличие от mysqli), например.
Я уж не говорю о том, что PDO — забагованный (проверял на PHP 8.0 + 8.1) и содержит кучу косяков, начиная с очевидных, вроде флагов PDO::ATTR_ERRMODE, который работает только в том случае, если его вызвать через setAttribute (при передаче через конструктор — не срабатывает в препейрах), заканчивая поломанной работой с транзакциями, если выставлен флаг PDO::ATTR_CASE (казалось бы, как это связано, но как бы вот).
А если говорить о mysqli, то он хоть и прибит гвоздями к одной БД, но зато гибче и надёжнее.
FanatPHP
А можно какие-то пруфы всем этим заявлениям?
У всего мира PDO::ATTR_ERRMODE прекрасно работает в конструкторе.
Асинхронность же мускулявых запросов сильно преувеличена. Учитывая, что запрос в любом случае может быть только один, какой-то разумный вариант применения этой асинхронности в РНР мне как-то не приходит в голову.
SerafimArts
Если говорить про реальные пруфы, которые прям вот сейчас можно взять и проверить, то вот тут: ExecutableQuery.php#L86 не будет возникать никаких исключений, если убрать вот эту строчку: Connection.php#L30 Сама опция
ATTR_ERRMODE
в конструктор, если что, передаётся: Отсюда Driver.php#L49 вот сюда Driver.php#L52Это всё на sqlite + php 8.1 воспроизводится 146%. Именно на этом кейсе столкнулся. Если неудобно через git clone проверить и убедиться, то могу попробовать собрать какой-нибудь гист попроще, без всякого лишнего кода.
P.S. Но вообще выглядит какая-то магия, судя по вашей ссылке, т.к. я не копал глубже, проставил дополнительно
setAttribute
, оно всё починилось и я забил.FanatPHP
Но я же выше уже сделал такой гист. Вот он же с синтаксической ошибкой. Всё прекрасно бросается. Что я делаю не так?
SerafimArts
Да, всё верно. Проблема была именно в синтаксической ошибке. Более того — не имеет смысла даже запускать тот код, что выше по моим ссылкам — там тоже не воспроизводится больше. Не знаю с чем было связано, возможно из-за того что я что-то проглядел, возможно из-за того что это был PHP 8.1 RC что-то там, но саму строчку с setAttribute я спецом коммитил, чтоб пофиксить конкретно эту проблему.
А на счёт ATTR_CASE + транзакций я уже забыл кейс. roxblnfk можешь напомнить пожалуйста что там было? Я точно помню что я показывал тебе этот магический баг в пдо. И кажется эта проблема была именно при работе с mysql.
roxblnfk
Там было что-то связанное с тем, что при выставлении CASE_NATURAL, PDO считал транзакцию запущенной (метод inTransaction() возвращал true).
Мы не документировали этот баг, поэтому можно считать, что его нет :)
FanatPHP
Оба коммента выглядят не очень по совсем другой причине.
Как и многие новички, вы путаете два понятия, драйвер для работы с БД и безопасное составление запросов.
Сам по себе драйвер не панацея. Точно так же у автора будут те же самые инъекции и с PDO.
Поэтому свою мысль надо формулировать немного по-другому, не "PDO rulez, mysqli suxx!!!", а "переменные не должны помещаться напрямую в запрос, а передаваться через привязку к подготовленному выражению". А уж через какой драйвер это делается - дело сто тыщ десятое.
LearnPC Автор
Большое спасибо за ряд комментариев и советов. Приму к сведению