|
2006-03-03
Обсудить на форуме
Принять участие в разработке библиотеки/утилиты можно на GitHub.
Существует множество библиотек для PHP, предоставляющих единый интерфейс для обращения к различным СУБД:
PEAR DB,
ADOdb and
PDO.
Код, написанный с использованием этих библиотек, оказывается довольно "многословным" и
чрезмерно перегруженным деталями. DbSimple предоставляет интерфейс, значительно
более простой и удобный, чем приведенные выше (да и многие другие) библиотеки абстракций
от СУБД.
Скачать
Основные возможности
- Поддержка PHP 4 и 5, СУБД: MySQL, PostgreSQL и InterBase/FireBird.
- Простой и лаконичный интерфейс (см. примеры ниже).
-
Условные макроподстановки в теле SQL-запроса ({}-блоки), позволяющие динамически генерировать даже очень сложные запросы без ущерба читабельности кода.
- Кэширование результатов выполнения запроса (если это необходимо).
-
Поддержка различных видов placeholder-ов (параметров запроса): списковый, ассоциативный, идентификаторный и т. д.
-
Поддержка функции "выборка + подсчет общего числа строк" (для отображения по страницам).
-
Функции непосредственной выборки: всего результата, строки, столбца, ячейки, ассоциативного массива, многомерного массива, связанного дерева и т. д.
- Удобный интерфейс для отслеживания и обработки ошибок.
-
Поддержка расширенного логирования запросов (включая результат выполнения запроса и номер строки вызывающей программу).
-
Поддержка "родных" placeholder-ов СУБД и автоматическая оптимизация "один prepare, много execute".
- Объектный интерфейс работы с BLOB-ами (если это необходимо).
-
Код библиотеки весьма компактен: один файл - базовый класс, один файл - драйвер
конкретной СУБД.
Идеология
-
Лицензия
LGPL (open-source).
-
Библиотека не должна выравнивать различия между разными СУБД на уровне языка SQL.
-
Интерфейс должен быть максимально лаконичным и удобным в использовании.
-
"Выполнение запроса" и "получение результирующих данных" должны быть объединены в одну операцию.
-
Если запрос формируется "кусками" (динамически), это не должно делаться в ущерб читабельности.
-
Оптимизация вида "один prepare, много execute" должна быть скрыта от пользователя и выполняться автоматически.
Чем неудобны другие библиотеки
- PEAR DB, ADOdb:
библиотеки не упрощают работу с СУБД, они просто предоставляют единый (и многословный) интерфейс; отладочные возможности в зачаточном состоянии.
- PDO:
требует PHP 5; неудобная работа с placeholder-ами и результатами выборки.
-
Стандартные функции PHP для работы с СУБД: низкая читабельность кода, значительные неудобства в отладке,
подверженность уязвимостям вида SQL Injection.
Интерфейс библиотеки (кратко)
Полное описание интерфейса см. в подробной документации.
- mixed connect(string $dsn)
- Соединение с указанной СУБД, используя DSN.
- mixed select(string $query [,$arg1...])
- Выборка двумерного массива (список строк).
- hash selectRow(string $query [,$arg1...])
- Выборка однострочного результата запроса (одна строка).
- array selectCol(string $query [,$arg1...])
- Выборка одноколоночного результата запроса (один столбец).
- scalar selectCell(string $query [,$arg1...])
- Выборка скалярного результата запроса (одна ячейка).
- mixed selectPage(int &$total, string $query [,$arg1...)
- Выборка ограниченного двумерного массива с занесением общего числа записей в переменную.
- mixed query(string $query [,$arg1...])
- Вызов не-SELECT запроса; для автоинкрементных полей в INSERT-запросах возвращает ID вставленной записи.
- mixed transaction([mixed $parameters])
- Запускает новую транзакцию.
- mixed commit() / mixed rollback()
- Подтверждает/отменяет текущую транзакцию.
Для изменения формата представления результата дополнительно используются зарезервированные имена столбцов (ARRAY_KEY* и т. д.) и атрибутные комментарии (например, для включения кэширования). См. ниже примеры их использования.
Примеры использования
|
Листинг 1: Подключение к СУБД
|
require_once "DbSimple/Generic.php";
$DB = DbSimple_Generic::connect("pgsql://login:password@host/database"); |
|
Листинг 2: Выборка всего результата
|
$rows = $DB->select('SELECT * FROM ?_user LIMIT 10');
foreach ($rows as $numRow => $row) {
echo $row['user_name'];
} |
|
Листинг 3: Выборка одной страницы результата
|
// В $totalNumberOfUsers запишется общее число строк.
$rows = $DB->selectPage(
$totalNumberOfUsers,
'SELECT * FROM ?_user LIMIT ?d, ?d',
$pageOffset, $numUsersOnPage
); |
|
Листинг 4: Макроподстановки в запросах
|
$rows = $DB->select('
SELECT *
FROM goods
WHERE
category_id = ?
{ AND activated_at > ? }
LIMIT ?d
',
$categoryId,
(empty($_POST['activated_at'])? DBSIMPLE_SKIP : $_POST['activated_at']),
$pageSize
); |
|
Листинг 5: Макроподстановки в запросах #2
|
$rows = $DB->select('
SELECT *
FROM
goods g
{ JOIN category c ON c.id = g.category_id AND 1 = ? }
WHERE
1 = 1
{ AND c.name = ? }
LIMIT ?d
',
(empty($_POST['cat_name'])? DBSIMPLE_SKIP : 1),
(empty($_POST['cat_name'])? DBSIMPLE_SKIP : $_POST['cat_name']),
$pageSize
); |
|
Листинг 6: Макроподстановки в запросах #3
|
$rows = $DB->select('
SELECT * FROM user
WHERE
1=0
{ OR user_id IN(?a) }
',
$listOfUserIdsMayBeEmpty
// Если пусто, разворачивается в 1=0, т.е. false.
); |
|
Листинг 7: Кэширование результатов запроса по времени
|
$DB->setCacher('myCacher');
$row = $DB->select('
CACHE: 10h 20m 30s
SELECT * FROM table WHERE id=123
');
// Определяем функцию работы с кэш-хранилищем.
function myCacher($key, $value)
{
// Если $value !== null, то следует записать его в кэш с ключом $key.
// Если $value === null, то следует вернуть значение кэша с ключом $key.
} |
|
Листинг 8: Кэширование результатов запроса с зависимостью от изменения таблиц
|
// Здесь forum.modified и topic.modified - поля типа TIMESTAMP.
$row = $DB->select('
CACHE: 10h 20m 30s, forum.modified, topic.modified
SELECT *
FROM forum JOIN topic ON topic.forum_id=forum.id
WHERE id=123
'); |
|
Листинг 9: Выборка ассоциативного массива
|
$rows = $DB->select('SELECT user_id AS ARRAY_KEY, ?_user.* FROM ?_user');
foreach ($rows as $userId => $userData) {
echo $userData['user_name'];
} |
|
Листинг 10: Списковый placeholder
|
$ids = array(1, 101, 303);
$DB->select('SELECT name FROM tbl WHERE id IN(?a)', $ids);
// SELECT name FROM tbl WHERE id IN(1, 101, 303) |
|
Листинг 11: Ассоциативный placeholder
|
$row = array(
'id' => 10,
'date' => "2006-03-02"
);
$DB->query('UPDATE tbl SET ?a', $row);
// MySQL: UPDATE tbl SET `id`='10', `date`='2006-03-02' |
|
Листинг 12: Идентификаторный placeholder
|
$DB->select('SELECT ?# FROM tbl', 'date');
// MySQL: SELECT `date` FROM tbl
// FireBird: SELECT "date" FROM tbl |
|
Листинг 13: Идентификаторно-списковый placeholder
|
$user = array('user_id' => 101, 'user_name' => 'Rabbit', 'user_age' => 30);
$newUserId = $DB->query(
'INSERT INTO user(?#) VALUES(?a)',
array_keys($row),
array_values($row)
); |
|
Листинг 14: Префиксный placeholder{Prefix-based placeholder}
|
$DB->setIdentPrefix('phpbb_');
$DB->select('SELECT * FROM ?_user');
// SELECT * FROM phpbb_users |
|
Листинг 15: Выборка одной строки
|
$row = $DB->selectRow('SELECT * FROM ?_user WHERE user_id=?', $uid); |
|
Листинг 16: Выборка одной ячейки
|
$userName = $DB->selectCell(
'SELECT user_name FROM ?_user WHERE user_id=?',
$uid
); |
|
Листинг 17: Выборка столбца
|
$cities = $DB->selectCol('SELECT city_name FROM ?_cities');
$citiesById = $DB->selectCol(
'SELECT city_id AS ARRAY_KEY, city_name FROM ?_cities'
); |
|
Листинг 18: Выборка многомерного массива
|
$messagesByTopics = $DB->select('
SELECT
message_topic_id AS ARRAY_KEY_1,
message_id AS ARRAY_KEY_2,
message_subject, message_text
FROM
?_message
');
// $messagesByForumsAndTopics[topicId][messageId] = messageData |
|
Листинг 19: Выборка связанного дерева
|
$forest = $DB->select('
SELECT
person_id AS ARRAY_KEY,
person_father_id AS PARENT_KEY,
*
FROM ?_person
'); |
|
Листинг 20: Оптимизация prepare ... execute
|
foreach ($array as $item) {
// DbSimple понимает, что prepare нужно выполнить всего один раз!
$DB->query('INSERT INTO tbl(field) VALUES(?)', $item);
} |
|
Листинг 21: Обработка ошибок
|
// File connect.php
$DB = DbSimple_Generic::connect('mysql://test:test@localhost1/non-existed-db');
$DB->setErrorHandler('databaseErrorHandler');
function databaseErrorHandler($message, $info)
{
if (!error_reporting()) return;
echo "SQL Error: $message<br><pre>"; print_r($info); echo "</pre>";
exit();
}
// В результате получим:
SQL Error: Unknown MySQL Server Host 'localhost1' (11001) at .../connect.php line 17
Array
(
[code] => 2005
[message] => Unknown MySQL Server Host 'localhost1' (11001)
[query] => mysql_connect()
[context] => .../connect.php line 17
) |
|
Листинг 22: Временное отключение ошибок
|
// Обратите внимание на "@"!
// Также по полю id должен быть создан уникальный индекс.
if (!@$DB->query('INSERT INTO tbl(id, field) VALUES(1, ?)', $field)) {
// Здесь идет реакция на ошибку, если она возникла.
// Контекст ошибки можно получить через $DB->error.
$DB->query('UPDATE tbl SET field=? WHERE id=1', $field);
} |
|
Листинг 23: Логирование запросов
|
$DB->setLogger('myLogger');
$rows = $DB->select('SELECT * FROM U_GET_PARAM_LIST');
function myLogger($db, $sql)
{
$caller = $db->findLibraryCaller();
$tip = "at ".@$caller['file'].' line '.@$caller['line'];
// Печатаем запрос (конечно, Debug_HackerConsole лучше)
echo "<xmp title=\"$tip\">"; print_r($sql); echo "</xmp>";
}
// Будет выведено что-то типа:
SELECT * FROM U_GET_PARAM_LIST;
--- 13 ms = 4+3+6; returned 30 row(s); |
|
|
Пользуетесь? Нравится?.. Пожертвуйте!
Пожертвование пойдет в качестве "спасибо" прямиком
на поддержание мотивации автора. А то он даже не знает, какое
количество людей пользуется той или иной библиотекой
или утилитой...
|