« ЧПУ (Человеко-понятные УРЛы) в java | Доступ к базам данных из php. Часть 3 » |
Доступ к базам данных из php. Часть 2
Сегодня мы продолжим разговор об различных методиках организации доступа к базам данных из php. Основной акцент я уделю не используемому языку и даже не библиотекам (стандартным, или сделанным сторонними компаниями или энтузиастами), а, прежде всего, сложившимся и зарекомендовавшими себя подходам, алгоритмам “успеха” применяя которые в типовых ситуациях можно избежать типовых и (столь привычных) для новичков ошибкам.Пару лет назад считалось модным создавать для любого мало-мальского сайта собственную библиотечку для работы с БД. Фактически на идею данной серии статей меня толкнула ситуация, когда меня попросили разобраться и чуть-чуть подправить один старый сайт. В нем я столкнулся с таким “велосипедом” – еще одной попыткой написать собственную библиотеку для доступа к БД – и понял что “наболело”. С одной стороны это хорошая тренировка в проектировании и программировании (особенно за счет заказчика) - с другой стороны – не желание познакомиться с, пусть, не де-юре (стандартными и встроенными в php средствами), так с де-факто библиотеками (интересными и используемыми серьезными разработчиками) и почерпнуть из них новые идеи. Не зря же сказал Ньютон “Современные ученые — это карлики, стоящие на плечах гигантов”.
В прошлый раз я остановился на том, что рассказал о паттернах проектирования. Рассказал об особом их семействе – паттерны доступа к данным. Сегодня мы продолжим рассмотрение средств позволяющих упростить проектирование модели данных и доступа к ней, плюс (на этом будет один из основных акцентов) поговорим о безопасности.
Первый паттерн – Table Data Gateway - заключался в том, чтобы создать набор функций, которые, будучи организованы в виде библиотеки, будут покрывать все множество типовых действий для вашей задачи – играть роль api. Так создавая даже сайт-каталог товаров, вы реализуете функции, работающие с объектами каталога: товарами, секциями. Эти функции умеют почти все: поиск товаров, сортировки товаров, отбор товаров по заданному набору характеристик, также задачи редактирования информации, добавления новых товаров или удаления старых. Эти функции получают в качестве входных данных все необходимое для работы. А результаты этой работы отдают в максимально обобщенном виде второму слою вашего приложения, которое может представлять собой панель управления сайта (backside) – то чем пользуется администрация для наполнения каталога информацией. Или для frontside – отобранную на первом слое информацию о товарах в разделе каталога следует на втором слое визуализировать согласно дизайну сайта. Слово “слой” я впервые употребил в прошлой статье. Так разделяя приложение на слои (и избегая побочных связей между ними), вы получаете массу преимуществ: скорость разработки растет из-за снижения числа возможных ошибок, количество переделок, которые необходимо выполнить при изменении одной части проекта, не растет снежной лавиной по всему коду, а локализуется только в одном слое. Плюс, можно грамотно разделить работу между несколькими участниками: код первого слоя пишут программисты – специалисты в области БД. Второй слой создается html-кодерами с небольшими навыками в области программирования. Если грамотно согласовать интерфейс первого слоя – названия функций, их параметры, то, что они будут делать, то вы можете запустить разработку этих двух слоев вашего приложения (сайта) параллельно. Для проекта, который планирует интенсивно развиваться, послойная организация дает лишние шансы на масштабируемость проекта, на смену используемой СУБД, дает возможность переделывать дизайн (работу второго слоя) не заставляя html-кодеров или дизайнеров лезть в топи первого слоя. Естественно, что о методиках такого разделения труда мечтают давно, разработаны и продолжают совершенствоваться различные паттерны (наиболее известен среди них MVC) и конкретные программные продукты. С другой стороны на внедрение этих методик требуются начальные затраты и обучение персонала – так, что если вы планируете всю жизнь делать проекты трудоемкостью в пару дней (недель) и дальнейшего развития для которого не предусматривается, то вам лучше перевернуть страницу и почитать про что-нибудь другое. Но довольно философии – давайте назовем конкретные имена библиотек позволяющих реализовать паттерн Table Data Gateway. Итак … это абсолютно все библиотеки. Некоторые, правда, представляют собой дополнительную функциональность и реализуют другие паттерны: Row Gateway, ActiveRecord, DataMapper. Вы можете не искать очередную “серебряную пулю” в виде сторонней библиотеки, а взять стандартные функции mysql или mysqli расширения php. Они прекрасно решают свои задачи. Давайте переформулируем: нам нужна библиотека, которая должна позволять записывать компактно и локализуемо sql-выражения, позволять избегать основных ошибок набора – глупых опечаток, запись sql-команд должна быть удобочитаемой, библиотека не должна позволять забывать об элементарной безопасности кода перед попытками взлома “злобных” хакеров. Сегодня я расскажу о библиотеке dbSimple от Д.Котерова (сайт dklab.ru). Во вступлении к описанию своей библиотеки Дмитрий говорит, что пытается, и чего не пытается делать его библиотека, просто добавлю пару своих копеек к его словам.
Выравнивание кода.
Я крайне негативно отношусь к программным продуктам, которые ставят своей целью эмуляцию возможностей одной СУБД средствами другой, или создания программной надстройки выполняющей такую эмуляцию. Рекламный лозунг “вместе с нашей библиотекой утром вы работаете с oracle, в полдень с mssql, а вечером с mysql” глуп и смешон. Я понимаю историческую и коммерческую целесообразность разработчиков “серьезных” СУБД отойти от рекомендуемого для всех стандарта SQL разных ревизий. Понимаю, что унификация набирает темп, и то, что достичь ее никогда не удастся. Мой любимый пример с получением информации о наличных базах данных на сервере, таблицах, полях, ограничениях – все, что называется словом метаинформация. Так ранее каждая СУБД предлагала свой набор ключевых слов, для того чтобы получить эти сведения, сейчас же почти повсеместной стала поддержка INFORMATION_SCHEMA (грубо говоря, существует некоторая виртуальная база данных – таблицы которой содержат информацию о других, уже реальных, таблицах, полях, view, и прочее). Смена используемой СУБД – по определению не может обойтись малой кровью. Чтобы писать быстрый и качественный код вы должны “выжимать все соки” из вашей СУБД, а не ограничиваться наиболее простыми, общими для многих СУБД командами. Итак, dbSimple не занимается выравниванием кода, и не занимается эмулированием недостающих возможностей некоторой СУБД. Классический пример: постраничная выборка данных, когда мы говорим, что нам нужна не все содержимое таблицы товаров, а только с десятой по двадцатую позиции – и отброс лишних записей выполняется средствами СУБД. Как прекрасно эта функция реализована в mysql, также ужасно в microsoft sql server до 2005 версии (но решение ведь все равно было). Так что, если вы все еще хотите библиотеку-выравниватель, то вам лучше еще раз перечитать книжку по вашей СУБД.
Кавычки, слэши и экранирование.
Когда я рассказал одному знакомому о плане написать серию статей посвященных php и базам данных, он скривил нос и заявил, что для php сложилась репутация примитивного и плохо спланированного языка, которому уже ничего не поможет – лучше уж я бы писал про java или ruby. Репутация действительно заслуженная – чего только стоит история с экранированием спец. символов. Спец. символы – это символы, которые играют особую роль для некоторого приложения, библиотеки, … Также под спецсимволами можно понимать символы которые обозначают не сами себя, а некоторый другой символ, например, \n – для перевода строки или \t для символа табуляции. Применительно к php&mysql – яркими представителями спец.символов являются кавычки – двойные или одинарные. Именно внутрь кавычек мы заключаем строки текста, которые вносятся в базу данных.
Но что делать, если же вносимый текст содержит кавычки (или иные спец.символы), но которые мы хотим интерпретировать не как спец.символ, а как обычный символ? Например, в ФИО нашего человека будет второе имя в кавычках см. рис. 1.
Обратите внимание на ошибку, и последующий запрос с постановкой перед внутренними кавычками обратного слэша. Экранировка жизненно необходима при вставке данных в любую таблицу, и дело не только в возможной генерации ошибки, а в возможном взломе вашего сайта на основе sql-injеction. Подробнее про это можно узнать по адресу http://ru.php.net/manual/ru/security.database.sql-injection.php или в журнале phpinside #5 (http://mag.phpclub.net/jul-aug2004/phpi5_2004.pdf). Суть взлома с помощью sql-injеction в том, чтобы манипулировать sql-кодом, внедрить в него опасную команду. Например, вы делаете сайт-каталог товаров, в нем страница, где по номеру или названию раздела отбираются вложенные товары. Страницы каталога запрашиваются по правилу:
catalog.php?section=12
SELECT * FROM articles WHERE section_id = 12
mysql_query (‘SELECT * FROM articles WHERE section_id = ’ . $_REQUEST[‘section’])
catalog.php?section=12;truncate articlesА, следовательно, будет выполнена уже следующая команда:
SELECT * FROM articles WHERE section_id =12;truncate articles
После такого длительного введения в суть проблемы вернемся к php и нашим кавычкам. Говорят (и я этому даже верю), что разработчики языка php, будучи не в силах заставить основную массу php-программистов писать качественный код, решили позаботиться о безопасности наших СУБД, и ввели автоматическое добавление слешей перед спец. символами. Слэши добавляются на основании директив php.ini (magic_quotes_gpc и magic_quotes_runtime). Директивы имеют общее название "волшебные кавычки", я же зову их “адские кавычки”. Действительно, в грамотно написанном приложении необходимость в автоматическом закавычивании отсутствует, более того лишние кавычки мешают, и их приходится удалять.
Первая директива: magic_quotes_gpc - означает, что PHP автоматически добавляет слеши к данным, пришедшим от пользователя - из POST, GET запросов и cookies. Вторая переменная - magic_quotes_runtime - означает, что слеши добавляются к данным, полученным во время исполнения скрипта - например, из файла или базы данных, так некоторые функции представляющие подобную информацию выполняют ее закавычивание. Если вы хотите отказаться от столь навязчивого сервиса, то либо вы (в той редкой и счастливой ситуации, когда вы полновластный владелец сервера) в файле php.ini отключаете эти конфигурационные переменные. Либо (если вы, конечно, не размещаете сайт на бесплатном хостинге) вы можете внести изменения в файл .htaccess. Это файл в котором находятся локальные – для одного каталога, а не для всего сервера, настройки apache, и добавьте в него следующие строки:
php_flag magic_quotes_gpc 0 php_flag magic_quotes_runtime 0Внимание: когда вы формируете запрос, вносящий данные в СУБД и выполнили добавление экранирующих слэшей, то эти слэши будут автоматически удалены сервером СУБД при сохранении изменений, поэтому частая ошибка начинающих в том, чтобы удалять слэши после получения (select выборки) данных из СУБД.
Итак, как итог, когда вы записываете команду sql, то не забывайте все строки помещать в кавычки и выполнять экранирование самих строк – не полагайтесь на “адские кавычки”. Для экранирования спец.символов следует применять только и только функцию mysql_real_escape_string – эта функция представляет несколько дополнительных сервисов по сравнению со своими младшими братьями “закавычивающими” addslashes или addcslashes. Прежде всего, эта функция может работать только после установления соединения с mysql-сервером и в своей работе она ориентируется на особенности настроек mysql, учитывает кодировку базы данных, кодировку соединения, правильно обрабатывает unicode-строки. Таким образом, конструирование строки запроса должно выглядеть так:
$sql="SELECT * FROM articles WHERE category_name = ' ".mysql_real_escape_string($_REQUEST['category']) . "' ";
SELECT * FROM ?_user WHERE fio = ? LIMIT ?d, ?d
Теперь поговорим о проблеме “хочу массив”. Дело в том, что стандартные функции php-mysql получив строку запроса возвращают вам не собственно набор отобранных данных, а некоторый ресурс – указатель, по которому с помощью других функций, можно получить собственно значения полей. Это хорошо и плохо одновременно. Хорошо – “тюнингом”, оптимизацией программы, экономией ресурсов памяти и CPU. Плохо, тем, что требуется выполнять рутинные и однообразные действия по преобразованию данных в формат “привычный” для php: массивы, А-массивы (ассоциативный массив). Так вот, dbSimple содержит ряд функций для отбора данных, которые возвращают записи в виде массива состоящего из А-массивов – каждая запись таблицы в виде А-массива. Есть функции для отбора скалярных величин.
Но об этом в следующий раз, когда я продолжу рассказ о DbSimple, также нас ждет следующая порция теории о паттернах доступа к данным и практика с парочкой интересных библиотек.
« ЧПУ (Человеко-понятные УРЛы) в java | Доступ к базам данных из php. Часть 3 » |