Джино: хостинг и веб-сервисы

Система Orphus
Russian version
Добавить на Del.icio.us
English version
Добавить на Digg.com

 dkLab | Конструктор | DbSimple: лаконичная работа с различными СУБД 

Карта сайта :: Форум «Лаборатории» :: Проект «Денвер»
Проект «Orphus» :: Куроводство: наблы :: Конструктор


2006-03-03
Обсудить на форуме

Принять участие в разработке библиотеки/утилиты можно на GitHub.

Лирическое отступление 
Вы можете также посмотреть полную версию документации для библиотеки DbSimple (на русском языке).

Существует множество библиотек для 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);







Дмитрий Котеров, Лаборатория dk. ©1999-2016
GZip
Добавить на Del.icio.us   Добавить на Digg.com   Добавить на reddit.com