Обновить
109
13.4
FanatPHP @FanatPHP

User

Отправить сообщение

Я не встречал на продакшене использования фуллтекст индексов. Полнотекстовый поиск всегда выносится в Сфинкс/Эластик.

Спасибо за статью, узнал несколько новых для себя вещей. Поставил плюсик.
Впечатления, однако, смешанные.
С одной стороны, хорошо что она есть, но с другой — видно, что написана по разнарядке для продвижения блога компании. Хотелось бы большей вовлечённости. Поменьше источников и побольше реальных юзкейсов из собственного опыта.
Не секрет, что explain используется в первую очередь для оптимизации запросов. А этой теме в статье посвящено до обидного мало. Тому, кто уже разбирается, она даст несколько новых подсказок, но тому, кто захочет с ее помощью научиться оптимизировать запросы, будет очень непросто вычленить ключевые моменты.
Желательно структурировать информацию, выделяя более значимую. К примеру, действительно важные значения столбца Extra стоит дать подробнее, а всякие диковины типа impossible having, которые только на бумаге и встречаются, я бы убрал под спойлер.


Чтобы не быть голословным, несколько вещей из собственного опыта


  • надо обращать внимание на размер key length. Если он большой, то стоит подумать над уменьшением. У нас в практике был случай, когда требовалась уникальность для поля, содержащего довольно большой объем данных. Убрав с него индекс и добавив рядом поле, содержащее md5() от этих данных, получили ощутимый прирост производительности.
  • перемножать значения rows надо не "если не лень", а в обязательном порядке. Ну или точнее — следить чтобы там в идеале были единички во всех дополнительных таблицах, поскольку перемножение строк и является основной причиной медленных запросов
  • не нужно переживать из-за Filesort, если значение Rows небольшое и эти действия производятся на финальной стадии — БД вполне может просто отсортировать уже полученный результат прямо в памяти, это никак не повлияет на скорость
  • если запрос очень большой, то я всегда "упрощаю" его, выкидывая из него различные элементы, следя за тем, чтобы ключевые проблемы в EXPLAIN оставались теми же — помогает увидеть самое основное и не отвлекаться на не имеющие значения детали, а так же сформулировать конкретный вопрос для google/stackoverflow

Вот еще одна жертва некачественных статей.
"фильтрация" — это не "самый надёжный способ" а просто адова ересь.
Скажи, ты смог бы свой комментарий здесь оставить, если бы "подозрительные строки" в нем были "отфильтрованы"?
Не говоря уже о том что все фильтры — это по определению черные списки, который никогда не гарантируют безопасность, в отличие от белых.
Не говоря уже о том, что есть инъекции второго порядка
Не говоря о том, что точек входа в программу может быть несколько, и все не зафильтруешь.

Как я уже писал в другом комментарии, Bind variables работает только для данных. Если нужно сделать order by :column, то такой запрос выдаст ошибку.


И в этом случае надо применять Option 3: Whitelist Input Validation. У Лары как раз с этим была проблема до недавнего времени — данные защищались, а имена полей вообще никак не проверялись, и пропускали инъекцию.

:str называется плейсхолдером или параметром и используется в подготовленном выражении (prepared statement), о котором говорится выше. :str здесь замещает актуальные данные в запросе и таким образом $_GET['name'] не может повлиять на запрос и вызвать ошибку или инъекцию. Это безопасно и как раз является рекомендуемым способом работы с динамическим SQL.


Только код в запросе какой-то очень уж замороченный. Я бы добавил проценты в РНР и сделал


$bindings = [
    'str' => "%{$_GET['name']}%";
];
$query = 'SELECT id, name FROM animals WHERE name like lower(:str)';

так, мне кажется, получается читабельнее. Хотя дело вкуса.

Хранимые процедуры сами по себе ни от чего не защищают. Вообще эта мантра ничем не лучше пресловутого "Escaping All User Supplied Input", поскольку должна сопровождаться длинным пояснительным текстом, что защищают только тогда, когда параметры процедуры подставляются в запрос в виде переменных, а не участвуют в ручной сборке запроса. Т.е.


 INSERT INTO t (a,b,c,d) VALUES (@a, @b, @c, @d);

безопасно, а если там что-то вроде


SET @sql=CONCAT('SELECT * FROM t WHERE f=\'',@a,'\'');

То разумеется никакая процедура не поможет.


Отделение данных от логики — уже ближе, только называется подготовленными выражениями или параметризованными запросами обычно.

Гарантирует, если не метаться между наваленными в кучу пунктами, а использовать 1 и 3 для данных и идентификаторов/операторов соответственно.


А то что инъекции в топ 10 — это только благодаря статьям в интернете, которые рассказывают что защиты нет и надо обязательно заказывать аудит.

В 2007 году у него не было аккаунта который надо попиарить

П.3 нужен если работаем не с данными, а с другими частями запроса, например именами полей.
Остальное в этом списке — чушь собачья, особенно п.4, который сам по себе адъ и позор.

Забавно, что в (поздне)советское время не было никакого итар, поскольку в нем, по очевидным причинам, не было и никакой нужды. В общем, проверено — метод работает :)

Хорошо, пиши. Но сначала покажи мне что получилось

Здравствуй, Отус.
Я знаю что не в тему, но уже давно хочу спросить, да всё случай не представлялся. Однажды на русском стаковерлое я увидел ссылку на вот это:


    $login = $_POST['login']; 
    $password = $_POST['password'];
    //запрашивается строка из базы данных с логином, введённым пользователем      
    $rez = mysql_query("SELECT * FROM users WHERE login=$login"); 
    //если нашлась одна строка, значит такой юзер существует в базе данных       
    if (mysql_num_rows($rez) == 1) 
    {           
        $row = mysql_fetch_assoc($rez);             
         //сравнивается хэшированный пароль из базы данных 
        //с хэшированными паролем, введённым пользователем 
        if (md5(md5($password).$row['salt']) == $row['password']) 

Ну то есть я понимаю, что технически это реклама курса "Backend-разработчик на PHP", и наглядная, так сказать, демонстрация конечного продукта. Но… честно говоря, даже по меркам "Курсы по пэхапе" это немного перебор. Я три раза дату проверил. Там реально стоит 2019 год, а не 1999.


Переводы — это конечно хорошо, но куда интереснее актуальное онлайн-образование, которое у вас можно получить.

Не знаю.
Могу только сказать, что им будет очень непросто.
Ты в курсе, наверное, как Зеев отчаянно бился против отмены коротких тегов, причем практически в одиночку? Учитывая, что его гешефт собственно и как раз построен на поддержке легаси, такая отмена означала бы ровно то самое переписывание огромных объемов кода на пустом месте, о котором ты говоришь.
И если теги ему удалось отстоять, то с undefined variable уже не получилось. Теперь, чтобы продолжать их игнорировать, нужно ужимать уровень отображения ошибок, а это очень рискованное занятие.


Никита в одном интервью как-то сказал, что веб за 20 лет кардинально изменился. И тот — молодой, зеленый РНР, который появился вместе с вебом, уже не отвечает требованиям времени. И чтобы выжить, должен меняться. Причем в первую сторону.

Я думаю что основная проблема в этом треде, которая и вызывает столь массовое несогласие — это совершенно необоснованное преувеличение,


Откровенно говоря php переборщил с несовместимостями со старыми версиями

которое ничего общего с реальностью не имеет.


Да, если перепрыгивать сразу через две мажорные версии, плюс изначально некачественный код — тут я согласен, могут быть проблемы. Но сваливать это на РНР как-то все-таки неправильно. РНР-то как раз всё делает нормально: все несовместимости вводятся постепенно, несколько лет идут предупреждения, несовместимые изменения делаются только в мажорных версиях.


А если уж апгрейдить с какой-нибудь допотопной 5.2 сразу на 8-ку, то надо это делать с умом. Сначала проапгрейдить на 5.6, потом на 7.4, и только потом уже пытаться это натянуть на 8.
Плюс параллельно рефакторить код под новые стандарты.

Ой, вот только insert, delete, update методов здесь не нужно. К обертке над ПДО они не имеют никакого отношения. Хочется сделать crud — делай отдельный класс, который будет использовать DB как сервис.
Но автору до этого ещё далеко, ему бы сначала с сырыми запросами попрактиковаться.

Ну, хорошо что ты хоть сам это понимаешь.
Как правильно выше сказали, уровень этого кода никак не тянет на то чтобы кого-то чему-то учить. Неадекватность оценки собственного уровня — это большая проблема. Почему-то некоторые новички совсем не считают себя новичками — а наоборот, только написав свой первый скрипт, сразу бегут учить других. Даже не попробовав показать свой код не новичкам, чтобы получить замечания. И даже не попробовав использовать в реальном проекте. Ну нельзя так: написать код и тут же бежать на Хабр, писать статью, которая "будет полезна новичкам". Вот из-за таких статей над РНР все и смеются, думая что это и есть обычный РНР код. Надо было хотя бы попробовать свой хелпер в реальном проекте.


Из ключевых проблем:


  • не задаётся режим выброса исключений, то есть ты никак не узнаешь, в чем проблема, если запрос не выполнится
  • не задается кодировка. А потом начнутся вопросы, "у меня кракозябры выводятся!"
  • также при подключении желательно отключать режим эмуляции. Иначе попытка использовать плейсхолдеры для LIMIT окончится печально.
  • приватная переменная, которая содержит инстанс ПДО. То есть обратиться к методам ПДО нельзя. При том что их функционал не продублирован в своем классе. Это делает его абсолютно нежизнеспособным. Я понимаю что транзакции для тебя это что-то вроде высшей математики, но как ты обойдешься без получения insert id?
  • класс не должен сам ничего инклюдить. а должен получать конфигурацию через параметр конструктора
  • универсальный метод для выполнения запросов тут же пытается что-то фетчить. А это ничего, что запросы бывают не только SELECT? И попытка сделать fetchAll() после UPDATE может вызвать ошибку? И как теперь получить affected rows?
  • жестоко урезана функциональность PDO. getOne(), getCol() — это всё есть в ПДО. Как и множество других вариантов получения результата, о которых ты даже не подумал, например пары ключ-значение. Метод query() должен возвращать стейтмент. Который уже потом можно использовать либо напрямую в коде, либо в методах-хелперах класса
  • методы, которые за тебя пишут два слова от SQL запроса — это просто глупость. Я понимаю что это стремление сократить код, но в данном случае это не то на чем следует экономить. Полный текст SQL запроса читается и воспринимается гораздо лучше чем какой-то огрызок. Не говоря уже о том, что не всегда надо выбирать *
  • про биндинг в цикле уже много раз написали. От себя добавлю, что плейсхолдеры бывают не только именованные, но и позиционные. Которые твой класс не поддерживает, из-за этого самого цикла.

Но я оговорюсь: хотя как пособие для новичков этот код никуда не годится, но как "мой первый ПДО враппер" он неплох. Сама идея метода query() очень правильная, она не всем приходит в голову. В целом класс сделан нормально, идеи в целом верные. Не хватает только опыта в программировании и хотя бы минимальной обкатки кода в реальном проекте. Очень хорошо что класс компактный, в нем нету кучи ненужного мусора, который обычно натаскивают в такой враппер.


Если что-то непонятно по пунктам выше — спрашивай. А вот новую статью писать не надо. Рановато ещё.

Очень хорошо что нет "ни одной обработки ошибок". Потому что ни одной здесь и не нужно.

Апдейтим на 7.4.х в основном
А когда в class Name { function Name вдруг функция перестала быть конструктором?
это не выдает ошибки

https://3v4l.org/kWSbk


Но виноват, конечно же, РНР

Судя по некоторым признакам (ужжжжасная сложность апгрейда и упорное нежелание приводить конкретные примеры), товарищ имеет в виду старое доброе mysql ext.


Других вариантов настолько сильно страдать при апгрейде я не представляю.

Понятное дело. Но со временем таких задач становится всё меньше. А таких, в которых изменение в одном месте кода может вызвать проблемы в другом — всё больше.
И тут уже без "паттернов" в какой-то момент становится совсем никуда.

Информация

В рейтинге
579-й
Зарегистрирован
Активность

Специализация

Backend Developer, Web Developer
Middle
От 140 000 ₽
PHP
OOP
MySQL
Linux
Git
SQL
Database
Nginx
Bash
Laravel