Бесполезный category php. PHP Категории (Дерево категорий)

05.11.2021

) я показал вам пример использования регулярных выражений для нахождения определенных кусков исходного кода страницы. Сейчас же мы с вами научимся писать их самостоятельно. Данный навык поможет писать , очищать текст от ненужных фрагментов, искать нужные части в больших объемах текста и так далее.

Дата публикации: 20.10.2013

Продолжаем писать парсер статистики посещений liveinternet. Если кто-то присоединился к нам только что, то советую начать с , где мы подключались к сервису без авторизации. Там мы научились получать контент с открытых страничек, которые не защищены паролем. Но, на самом деле, доступ к большинству сайтов закрыт, поэтому пользы от скрипта, который не умеет авторизовываться, очень мало, как вы понимаете. Но не беспокойтесь, сейчас все исправим.

Дата публикации: 08.10.2013

Добрый день, читатели! Пришло время немного попрактиковаться. У вас уже достаточно знаний, чтобы приступить к написанию полезных скриптов, которые будут облегчать вашу работу. Одним из таких может стать парсер. Что это такое я подробно расписывал в своей , так что если не знаете, советую прочитать. Кратко, суть парсера сводится к поиску и вычленению нужной информации из большого куска текста. В случае же с Интернетом, это чаще всего выдергивание требуемых данных из страниц сайтов. В нашем случае это будет Liveinternet. Итак, поехали!

Дата публикации: 10.03.2013

В этой статье речь пойдет о таком мощном инструменте как cURL, а также о библиотеке для php, которая предоставляет доступ к этому инструменту — libcurl. Для чего все это нужно? Для связи с сервером по протоколам передачи данных, например, http или ftp. Остальные протоколы нам не особо интересны, если кто-то хочет углубляться в эту тему, то придется уже копать англоязычные ресурсы, а в этой статье будут основы и примеры использования.

Дата публикации: 24.11.2012

Дата публикации: 05.11.2012

А сейчас будет достаточно большой, но не сложный урок о работе с файлами в php. Для начала, для чего нужны файлы? Ведь можно все хранить в базе данных MySQL или PostgreSQL или любой другой. Но иногда бывают такие задачи, когда использование БД, со всеми обработками и заботой о безопасности соединения, не целесообразно. Например нужно сделать обычный счетчик, а до этого в проекте у нас БД не использовалась. Так что нам, ради одного малюсенького счетчика заводить базу данных и хранить в ней всего пару строк? тут гораздо проще воспользоваться файлами. К тому же, иногда на хостинге вообще нет поддержки баз данных, тогда файлы вообще остаются единственным выходом.

Дата публикации: 04.11.2012

Всякий раз, когда вы позволяете вашим пользователям отправлять текст на ваш сайт (имя, или любая другая информация), вы должны быть осторожны. Нужно быть уверенным, что у вас нет дыр в безопасности сайта, которые злоумышленники могут использовать для взлома. Если все же нужно получать от пользователя данные, то обязательно используйте функцию htmlentities, чтобы предотвратить запуск HTML-кода или скриптов, которые могут быть вредны и опасны!

Дата публикации: 04.11.2012

В этом уроке мы рассмотри приемы передачи данных между формами и страницами. Такими методами являются POST и GET. О каждом мы поговорим отдельно и более подробно. Вообще говоря, это нужно для связи между формами. Например мы заполняем какие то поля на странице и нам нужно их передать в другую страницу для обработки.

Дата публикации: 03.11.2012

На самом деле, как вы могли уже догадаться, цикл do while представляет собой слегка модифицированную версию цикла while, которую мы в прошлых уроках. Если вы вспомните, как работает обычный while, то вам будет проще понять новый цикл. Давайте повторим: тело цикла while выполняется, если условие верно и не выполняется, если не верно, но может и не выполниться ни разу, если условие будет с самого начала ложно. Как же работает do while?

Дата публикации: 03.11.2012

Представьте, что у вас есть ассоциативный массив, который вы хотите перебрать. PHP предоставляет простой способ использовать каждый элемент массива по очереди с помощью Foreach конструкции.

Для каждого поста и записи wordpress пользователь может задавать одну или несколько рубрик (категорий). Эта возможность позволяет сгруппировать близкие по смыслу записи и предоставить возможность посетителям читать и просматривать только те рубрики, которые им нравятся. Например, когда я создавай свой основной блог Tod’s Blog, то собирался писать обо всех нюансах интернета — начиная с дизайна и заканчивая программированием. Допустим, человек пришел из поисковика на статью про wordpress и захотел бы почитать про систему еще больше – ему пришлось бы рыться в архивах, повторно использовать поиск либо просматривать все посты подряд. Разумеется, этого всего можно было избежать, зайдя в специальную категорию под названием wordpress. Или, например, для те, кто увлекается лишь дизайном, могла быть интересна рубрика для блога.

Если внимательно посмотрите на шапку блога, то можете увидеть своего рода меню, где рубрики wordpress выступают в роли разделов проекта. Как по мне, это достаточно удобный и наглядный способ разделения тематики записей.

В самом центре страницы вы увидите форму для добавления новой категории. Здесь нужно указать ее название (имя), ярлык (часть ссылки url для чпу), родительскую категорию (если такая есть), а также можно задать краткое описание. Родительская категорий позволяет создавать в wordpress разделы с несколькими уровнями вложенности – например, для категории «водрпресс» на каком-то ИТ блоге можно добавить те же шаблоны, плагины и т.п.

Справа на странице Рубрики отображаются все категории wordpress, с возможностью из редактирования либо удаления. Чтобы произвести действия достаточно подвести курсор мышки на имя той или иной категории, после чего увидите небольшое всплывающее меню.

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

Здесь же можно добавлять новые рубрики – кликнув по соответствующей ссылке. Единственный недостаток этого механизма в том, что при создании можно указать лишь имя и родительскую категорию, тогда для как задания поля ярлык придется переходить в раздел «Рубрики» и редактировать информацию там.

Кроме того редактировать категории для постов в блоге можно через их список в меню Записи – Изменить. Там при наведении на ту или иную публикацию вы увидите ссылку «Быстрое редактирование». Нажимаем по ней и видим форму для правки:

Здесь можно изменить и категории, и теги, и всю дополнительную информацию по статье. Вещь очень удобная + работает без перезагрузки страницы.

Функция wp_list_categories для категории wordpress

По традиции рассматриваю не только вопрос работы с теми или иными элементами системы, но и привожу специальные функции для шаблонов. Точно также как я рассказывал про . Итак, для вывода списка категорий со ссылками на них используется wp_list_categories . Она имеет целый ряд аргументов:

  • show_option_all – отображает ссылка на все категории, если в качестве стиля отображения выбрал список.
  • orderby – сортировка для категорий по ID, имени (name), ярлыку (slug), количеству постов (count).
  • order – порядок сортировки (ASC – по увеличению, DESC – по уменьшению).
  • show_last_updated – показывать дату последнего обновления.
  • style – стиль оформления: список (list), разделение через
    (none).
  • show_count – отображать количество постов в категории.
  • hide_empty – скрывать пустые рубрики, где нет записей.
  • use_desc_for_title – использовать описание для атрибута title в ссылке.
  • child_of – вывод только категорий для заданной родительской рубрики.
  • feed – отображение ссылку на фид для категорий.
  • feed_type – тип фида.
  • feed_image – картинка для значка rss.
  • exclude – исключение категорий из списка, при этом параметр child_of автоматически отключается.
  • exclude_tree – исключения целой ветки рубрик.
  • include – обратный параметр, который включает только указанные категории wordpress в список.
  • hierarchical – параметр для отображения подкатегорий.
  • title_li – заголовок списка рубрик.
  • number – количество категорий для отображения (если их слишком много).
  • echo – выводит рубрики, по умолчанию равен True.
  • depth – указывает количество уровней для подкатегорий для вывода.

Напоследок приведу ряд примеров использования wp_list_categories. Во-первых, вариант из шапки этого блога.

"hide_empty=1&exclude=1&title_li=&orderby=count&order=desc&use_desc_for_title=0" ) ; ?>

Здесь задано отображение скрытых категорий, исключение из списка рубрики с, пустая строка для заголовка блока, сортировка по количеству статей и по уменьшению (то есть больше всего статей у меня в разделе ). Последний аргумент не подставляет описание категории в title для ссылки.

Ну и еще парочку простых ситуаций. Использование исключений и включений категорий.

Если есть что дополнить про рубрики и категории wordpress, пишем в комментариях.

Update: Также вам может пригодится небольшой хак чтобы . В wordpress по умолчанию определяется текст title что-то вроде «просмотреть все записи в рубрике ….», можно вместо этого просто оставить название рубрики — читаем статью по ссылке выше.

Возвращает массив объектов содержащих информацию о категориях.

Параметры передаваемые этой функции очень похожи на параметры передаваемые функции wp_list_categories() и могут быть переданы как в виде массива, так и в виде строки запроса: type=post&order=DESC .

✈ 1 раз = 0.005625с = очень медленно | 50000 раз = 11.98с = медленно | PHP 7.1.11, WP 4.9.5

Использование

$categories = get_categories($args);

Шаблон использования

$categories = get_categories(array("taxonomy" => "category", "type" => "post", "child_of" => 0, "parent" => "", "orderby" => "name", "order" => "ASC", "hide_empty" => 1, "hierarchical" => 1, "exclude" => "", "include" => "", "number" => 0, "pad_counts" => false, // полный список параметров смотрите в описании функции http://wp-kama.ru/function/get_terms)); if($categories){ foreach($categories as $cat){ // Данные в объекте $cat // $cat->term_id // $cat->name (Рубрика 1) // $cat->slug (rubrika-1) // $cat->term_group (0) // $cat->term_taxonomy_id (4) // $cat->taxonomy (category) // $cat->description (Текст описания) // $cat->parent (0) // $cat->count (14) // $cat->object_id (2743) // $cat->cat_ID (4) // $cat->category_count (14) // $cat->category_description (Текст описания) // $cat->cat_name (Рубрика 1) // $cat->category_nicename (rubrika-1) // $cat->category_parent (0) } } taxonomy(строка) Название таксономии, которую нужно обрабатывать. Добавлено с версии 3.0.
По умолчанию: "category" type(строка)
  • post - категории для постов (по умолчанию);
  • link - разделы ссылок.
    По умолчанию: "post"
child_of(строка) Получить дочерние категории (включая все уровни вложенности), указанной категории. В параметре указывается ID родительской категории (категория, вложенные категории которой нужно показать). parent(число) Получает категории родительская категория которых равна указанному в параметре ID. Отличие от child_of в том, что будет показан один уровень вложенности.
По умолчанию: "" orderby(строка)

Сортировка полученных данных по определенным критериям. Например, по количеству постов в каждой категории или по названию категорий. Доступны следующие критерии:

  • ID - сортировка по ID;
  • name - сортировка по названию (по умолчанию);
  • slug - сортировка по алт. имени (slug);
  • count - по количеству записей в категории;
  • term_group - по группе.

По умолчанию: "name"

Order(строка)

Направление сортировки, указанной в параметре "orderby":

  • ASC - по порядку, от меньшего к большему (1, 2, 3; a, b, c);
  • DESC - в обратном порядке, от большего к меньшему (3, 2, 1; c, b, a).

По умолчанию: "ASC"

Hide_empty(логический)

Получать или нет пустые категории (не имеющие записей):

  • 1 (true) - не получать пустые,
  • 0 (false) - получать пустые.

По умолчанию: true

Hierarchical(логический) Если параметр установлен в true , то в результат будут включены пустые дочерние категории, дочерние категории которых имеют записи (непустые).
По умолчанию: true exclude(строка/массив) Исключить какие-либо категории из списка. Нужно указывать ID категорий через запятую или в массиве. Если этот параметр указан, параметр child_of будет отменен.
По умолчанию: "" include(строка/массив) Вывести списком только указанные категории. Указывать нужно ID категорий через запятую или в массиве.
По умолчанию: "" number(число) Лимит. Число категорий, которые будут получены. По умолчанию без ограничений - будут получены все категории. pad_counts(логический) Если передать true, то число которое показывает количество записей в родительских категориях будет суммой своих записей и записей из дочерних категорий.
По умолчанию: false

Примеры

#1 Выпадающий список

Для того, чтобы создать выпадающий список из категорий мы можем воспользоваться другой специальной для этой цели, функцией wp_dropdown_categories() :

Wp_dropdown_categories(array("hide_empty" => 0, "name" => "category_parent", "orderby" => "name", "selected" => $category->parent, "hierarchical" => true, "show_option_none" => __("None")));

однако с таким подходом мы лишимся определенной гибкости в настройке списка, так как мы получи уже полностью сформированный список.

Поэтому, в некоторых случаях будет логичнее создать выпадающий список с помощью функции get_categories() . Вот пример (предполагается что нам нужно вывести подкатегории (дочерние) категории 10):

#2 Список категорий и их описание

Этот пример покажет нам как можно вывести списком ссылки на категории, где сразу после каждой ссылки будет идти описание категории (указывается при создании/редактировании категории):

"name", "order" => "ASC")); foreach($categories as $category){ echo "

Category: term_id) . "" title="" . sprintf(__("View all posts in %s"), $category->name) . "" " . ">" . $category->name."

"; echo "

Description:". $category->description . "

"; echo "

Post Count: ". $category->count . "

"; } ?>

Заметки

  • Смотрите: get_terms() Type of arguments that can be changed.

Список изменений

С версии 2.1.0 Введена.

Код get categories : wp-includes/category.php WP 5.3.2

"category"); $args = wp_parse_args($args, $defaults); /** * Filters the taxonomy used to retrieve terms when calling get_categories(). * * @since 2.7.0 * * @param string $taxonomy Taxonomy to retrieve terms from. * @param array $args An array of arguments. See get_terms(). */ $args["taxonomy"] = apply_filters("get_categories_taxonomy", $args["taxonomy"], $args); // Back compat if (isset($args["type"]) && "link" == $args["type"]) { _deprecated_argument(__FUNCTION__, "3.0.0", sprintf(/* translators: 1: "type => link", 2: "taxonomy => link_category" */ __("%1$s is deprecated. Use %2$s instead."), "type => link", "taxonomy => link_category")); $args["taxonomy"] = "link_category"; } $categories = get_terms($args); if (is_wp_error($categories)) { $categories = array(); } else { $categories = (array) $categories; foreach (array_keys($categories) as $k) { _make_cat_compat($categories[ $k ]); } } return $categories; }

Я взялся за написание класса, реализующего изложенные в ней идеи.
А точнее, поскольку ключевой функционал уже использовался в рамках рабочего фремворка, я занялся выделением его в самостоятельный класс. Пользуясь случаем, хочу поблагодарить участников PHPClub-а за помощь в исправлении нескольких критических ошибок и полезные замечания. Ниже я постараюсь описать основные особенности, но сначала небольшой

дисклеймер

Есть несколько способов работы с SQL - можно использовать квери-билдер, можно ORM, можно работать с чистым SQL. Я избрал последний вариант, потому что мне он ближе. Я совсем не считаю первые два плохими. Просто лично мне всегда было тесно в их рамках. Но я ни в коем случае не утверждаю, что мой вариант лучше. Это просто ещё один вариант. Который можно использовать, в том числе, и при написании ORM-а. В любом случае, я считаю, что наличие безопасного способа работать с чистым SQL не может принести какой-либо вред. Но при этом, возможно, поможет последним оставшимся приверженцам использования mysql_* в коде приложения, отказаться, наконец, от этой порочной практики.


В двух словах, класс строится вокруг набора функций-хелперов, позволяющих выполнять большинство операций с БД в одну строку, обеспечивая при этом (в отличие от стандартных API) полную защиту от SQL инъекций, реализованную с помощью расширенного набора плейсхолдеров, защищающих любые типы данных, которые могут попадать запрос.
В основу класса положены три базовых принципа:
  1. 100% защита от SQL инъекций
  2. При этом защита очень удобная в применении, делающая код короче, а не длиннее
  3. Универсальность, портабельность и простота освоения
Остановлюсь чуть подробнее на каждом из пунктов.

Безопасность

обеспечивается теми самыми двумя правилами, которые я сформулировал в статье:
  1. Любые - без исключений! - динамические элементы попадают в запрос только через плейсхолдеры.
  2. Всё, что не получается подставить через плейсхолдеры - прогоняется сначала через белый список.
К сожалению, стандартные библиотеки не предоставляют полной защиты от инъекций, защищая с помощью prepared statements только строки и числа.
Поэтому, чтобы сделать защиту полной, пришлось отказаться от заведомо ограниченной концепции prepared statements в пользу более широкого понятия - плейсхолдеров. Причём плейсхолдеров типизованных (эта вещь всем нам известна по семейству функций printf(): %d - плейсхолдер, который подсказывает парсеру, как обрабатывать подставляемое значение, в данном случае - как целое число). Нововведение оказалось настолько удачным, что разом решило множество проблем и значительно упростило код. Подробнее о типизованных плейсхолдерах я напишу ниже.
Поддержка же фильтрации по белым спискам обеспечивается двумя функциями, несколько притянутыми за уши, но, тем не менее, необходимыми.

Удобство и краткость кода приложения

Здесь мне также здорово помогли типизованные плейсхолдеры, которые позволили сделать вызовы функций однострочными, передавая сразу и запрос, и данные для него. Плюс набор хелперов, напоминающих таковые в PEAR::DB - функций, сразу возвращающих результат нужного типа. Все хелперы организованы по одной и той же схеме: в функцию передаётся один обязательный параметр - запрос с плейсхолдерами, и сколько угодно опциональных параметров, количество и порядок которых должны совпадать с количеством и порядком расположения плейсхолдеров в запросе. У функций семейства Ind используется ещё один обязательный параметр - имя поля, по которому осуществляется индексация возвращаемого массива.
Исходя из своего опыта, я пришёл к следующему набору возвращаемых значений (и, как следствие - хелперов):
  • query() - возвращает mysqli resource. Может использоваться традиционно, с fetch() и т.д.
  • getOne() - возвращает скаляр, первый элемент первой строки результата
  • getRow() - одномерный массив, первую строку результата
  • getCol() - одномерный массив скаляров - колонку таблицы
  • getAll() - двумерный массив, индексированный числами по порядку
  • getInd() - двумерный массив, индексированный значениями поля, указанного первым параметром
  • getIndCol() - массив скаляров, индексированный полем из первого параметра. Незаменимо для составления словарей вида key => value
В итоге большинство обращений к БД сводится одно-двух строчным конструкциям (вместо 5-10 при традиционном подходе):
$data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
В этом коде есть только необходимые и значащие элементы, но нет ничего лишнего и повторяющегося. Все потроха аккуратно упрятаны внутрь класса: хелпер getAll() позволяет получить сразу нужный результат без написания циклов в коде приложения, а типизованные плейсхолдеры позволяют безопасно добавлять в запрос динамические элементы любых типов без прописывания привязок (bind_param) вручную. Extra DRY код! В случаях использования плейсхолдеров?a и?u разница в количестве кода становится ещё больше:
$data = $db->getAll("SELECT * FROM table WHERE category IN (?a)",$ids);

Универсальность и простота освоения

стоят на трех китах:
  1. Очень маленький API - пол-дюжины плейсхолдеров и столько же хелперов.
  2. Мы работаем со старым добрым SQL, который не надо заново учить.
  3. На первый взгляд незаметная, но невероятно полезная функция parse(), которая изначально предназначалась только для отладки, но в итоге выросла до ключевого элемента при составлении сложных запросов.
В итоге все сложносочинённые запросы собираются по-старинке - например в цикле - но при этом при соблюдении всех правил безопасности!
Приведу небольшой пример (примеры посложнее можно найти в документации по ссылке внизу статьи):
Довольно частый случай, когда нам надо добавить в запрос условие при наличии переменной

$sqlpart = ""; if (!empty($var)) { $sqlpart = $db->parse(" AND field = ?s", $var); } $data = $db->getAll("SELECT * FROM table WHERE a=?i ?p", $id, $sqlpart);
Здесь важно отметить несколько моментов.
Во-первых поскольку мы не связаны родным API, никто не запрещает нам пропарсить не весь запрос целиком, а только его часть. Это оказывается супер-удобно для запросов, собирающихся в соответствии какой-либо логикой: мы парсим только часть запроса, а затем она подставляется в основной запрос через специальный «холостой» плейсхолдер, чтобы избежать повторного парсинга (и соблюсти правило «любые элементы подставляются только через плейсхолдер»).
Но, к сожалению, это является слабым местом всего класса. В отличие от всех остальных плейсхолдеров (которые, даже будучи использованы неверно, никогда не приведут к инъекции) некорректное использование плейсхолдера?p может к ней привести.
Однако защита от дурака сильно усложнила бы класс, но при этом все равно никак не защитила бы от тупой вставки переменной в строку запроса. Поэтому я решил оставить как есть. Но если вы знаете способ, как без слишком большого оверинжиниринга решить эту проблему - я был бы благодарен за идеи.

Тем не менее, в итоге мы получили мощный и лёгкий генератор запросов, который с лихвой оправдывает этот небольшой недостаток.
Мощный потому, что мы не ограничены синтаксисом квери-билдера, «SQL-ем, написанным на PHP» - мы пишем чистый SQL.
Лёгкий потому, что весь API составления запросов состоит из полудюжины плейсхолдеров и функции parse()
Вот мой любимый пример - вставка с использованием функций Mysql
$data = array("field"=>$value,"field2"=>$value); $sql = "INSERT INTO table SET ts=unix_timestamp(), ip=inet_aton(?s),?u"; $db->query($sql, $ip, $data);
С одной стороны, мы сохраняем синтаксис SQL, с другой - делаем его безопасным, а с третьей - капитально сокращаем количество кода.

Подробнее о типизованных плейсхолдерах

Сначала ответим на вопрос, почему плейсхолдеры вообще?
Это, в общем, уже общее место, но, тем не менее, повторюсь - любые динамические данные должны попадать в запрос только через плейсхолдеры по следующим причинам:
  • самое главное - безопасность. Добавив переменную черз плейсхолдер, мы можем быть уверены в том, что она будет корректно отформатирована.
  • локальность форматирования. Это не менее важный момент. Во-первых, данные форматируются непосредственно перед попаданием в запрос, и не затрагивают исходную переменную, которая потом может быть использована где-то ещё. Во-вторых, данные форматируются ровно там, где нужно, а не до начала работы скрипта, как при magic quotes, и не в десяти возможных местах кода несколькими разработчиками, каждый из которых может понадеяться на другого.
Развивая эту концепцию далее, мы приходим к мысли, что пейсхолдеры обязательно должны быть типизованными . Но почему?
Тут я бы хотел ненадолго остановиться, и проследить историю развития программистской мысли в области защиты от инъекций.
Сначала был хаос - вообще никакой защиты, пихаем всё как есть.
Дальше стало не сильно лучше, с парадигмой «искейпим всё, что пришло в скрипт от пользователя» и кульминацией в виде magic quotes.
Дальше лучшие умы пришли к тому, что правильно говорить не об экранировании, а о форматировании. Поскольку форматирование не всегда сводится к одному искейпингу. Так в PDO появился метод quote(), который делал законченное форматирование строки - не только экранировал в ней спецсимволы, но и заключал её в кавычки, не надеясь на программиста. В итоге, даже если программист использовал эту функцию не на месте (например, для числа), то инъекция всё равно не проходила (а в случае с голым экранированием через mysql_real_escape_string она легко проходит, если мы поместили в запрос число, не заключая его в кавычки). Будучи же использованной для форматирования идентификатора, эта функция приводила к ошибке на этапе разработки, что подсказывало автору кода о том, что он немножечко неправ.
К сожалению, на этом авторы PDO и остановились, поскольку в головах разработчиков до сих пор крепко сидит мысль о том, что форматировать в запросе надо только строки. Но на самом деле в запросе гораздо больше элементов самых различных типов. И для каждого нужен свой собственный тип форматирования! То есть, единственный метод quote() нас никак не устроит - нужно много разных quotes. Причём не в качестве исключения, «нате вам quoteName()», а как одна из главных концепций: каждому типу - свой формат. Ну а раз типов форматирования оказывается много - тип надо как-то указывать. И типизованный плейсхолдер для этого подходит лучше всего.

Кроме того, типизованный плейсхолдер - это ОЧЕНЬ удобно!
Во-первых, потому что становится ненужным специальный оператор для привязки значения к плейсхолдеру (но при этом сохраняется возможность указать тип передаваемого значения!)
Во-вторых, раз уж мы изобрели типизованный плейсхолдер - мы можем налепить этих плейсхолдеров огромное количество, для решения множества рутинных задач по составлению SQL запросов.
В первую очередь сделаем плейсхолдер для идентификаторов - нам его отчаянно не хватает в реальной, а не воображаемой авторами стандартных API, жизни. Как только девелопер сталкивается с необходимостью динамически добавить в запрос имя поля - каждый начинает извращаться по-своему, кто в лес, кто по дрова. Здесь же всё унифицировано с остальными элементами запроса, и добавление идентификатора становится не сложнее добавления строки. Но при этом идентификатор форматируется не как строка, а в соответствии со своими собственными правилами - заключается в обратные кавычки, а внутри эти кавычки экранируются удвоением.
Дальше - больше. Следующая головная боль любого разработчика, когда-либо пытавшегося использовать стандартные prepared statements в реальной жизни - оператор IN(). Вуаля, у нас есть плейсхолдер и для этой операции! Подстановка массива становится не сложнее любых других элементов, плюс она унифицирована с ними - никаких отдельных функций, меняется всего лишь буква в плейсхолдере.
Точно таким же образом делаем и плейсхолдер для SET. Не удержусь и продемонстрирую, насколько простым становится код для такого замороченного запроса, как INSERT… ON DUPLICATE:
$data = array("offers_in" => $in, "offers_out" => $out); $sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u"; $db->query($sql,$pid,$data,$data);
В данный момент классом поддерживается 6 типов плейсхолдеров

  • ?s («string») - строки (а также DATE, FLOAT и DECIMAL).
  • ?i («integer») - целые числа.
  • ?n («name») - имена полей и таблиц
  • ?p («parsed») - для вставки уже обработанных частей запроса
  • ?a («array») - набор значений для IN (строка вида "a","b","c")
  • ?u («update») - набор значений для SET (строка вида `field`="value",`field`="value")
Что вполне достаточно для моих задач, но этот набор всегда можно расширить любыми другими плейсхолдерами, например, для дробных чисел. Делать отдельный плейсхолдер для NULL я не вижу смысла - его можно всегда вписать прямо в запрос.
Автоматическую трансляцию PHP-шного NULL в SQL-евский NULL я решил не делать. Возможно, это чуть усложнит код (в тех редких случаях, когда это нужно), но зато уменьшит его неоднозначность.

Кстати, как многие могли заметить, этот класс во многом напоминает библиотеку DbSimple Дмитрия Котерова. Но у меня есть принципиальные расхождения с некоторыми идеями, заложенными в неё.
Во-первых, я противник любой магии, когда одна и та же функция может возвращать различный результат в зависимости от типа переданных данных. Это, возможно, чуть упрощает написание, но при этом чудовищно затрудняет сопровождение и отладку кода. Поэтому в моем классе вся магия сведена к минимуму, а все операции и типы данных всегда прописываются явно.
Во-вторых, в DbSimple немного, на мой взгляд, переусложнённый синтаксис. С одной стороны, фигурные скобки - гениальная идея. С другой - а зачем, если в нашем распоряжении вся мощь PHP? Поэтому я решил пойти другим путём и весто «внутренней» - заведомо ограниченной - логики ввёл «внешнюю», ограниченную лишь синтаксисом РНР. Главное, чтобы любые динамические элементы попадали в запрос только через плейсхолдеры, а остальное зависит лишь от фантазии разработчика (и функции parse()).

Код класса доступен на Гитхабе, github.com/colshrapnel/safemysql/blob/master/safemysql.class.php
Cheat sheet с основными командами и примерами: phpfaq.ru/misc/safemysql_cheatsheet_ru.pdf
Хорошее представление о возможностях можно получить на странице примеров документации (к сожалению, ещё не законченной), phpfaq.ru/safemysql
Там же есть ответы на часто задаваемые вопросы, такие как «почему ты не используешь родные prepared statements?» и пр.
Тем не менее, буду рад ответить на любые вопросы в комментариях, а так же улучшить по вашим замечаниям как сам класс, так и эту статью.