ambalus/dbsimple

用于与各种数据库交互的接口。

dev-master 2017-03-28 04:26 UTC

This package is not auto-updated.

Last update: 2024-09-29 02:13:31 UTC


README

DbSimple:与各种关系型数据库交互的最简单但功能强大的接口。(C) Dmitry Koterov, http://en.dklab.ru/lib/DbSimple/

摘要

有许多PHP库为我们提供了统一的数据库访问接口:PEAR DB、ADOdb 和 PDO。使用这些库编写的代码通常非常冗长,并且被无用的细节过度加载。DbSimple 引入了一个比上述(以及许多其他流行)抽象库都要简单易用的接口。

主要功能

  • 支持PHP 5,DBMS:MySQL、PostgreSQL、InterBase/FireBird。
  • 简单简洁的接口(请见以下示例)。
  • SQL体中的条件宏块({}-块),允许动态生成甚至非常复杂的查询,而不会损害代码的可读性。
  • 查询结果缓存(如有必要)。
  • 支持各种占位符(查询参数)类型:基于列表、关联、基于标识符等。
  • 支持“select + 计算总行数”操作(用于分页显示的数据)。
  • 直接获取结果的功能:所有结果行、一行、一列、一个单元格、关联数组、多维关联数组、链接树等。
  • 非常方便的接口用于监视和调试查询错误。
  • 支持增强的查询日志(包括查询结果和调用者行号)。
  • 支持“原生”数据库占位符和自动优化“单次准备,多次执行”。
  • 基于对象的BLOB访问接口(如有必要)。
  • 库代码相当紧凑:一个文件是基类,一个文件是特定数据库驱动程序。

理念

  • 许可证LGPL(开源)。
  • 库不应封装不同DBMS之间SQL语言的差异。
  • 接口必须非常简洁,便于实际使用。
  • “查询执行”和“结果获取”必须合并为单一操作。
  • 如果查询是“逐块”构建的(通过PHP代码动态进行),则不应损害可读性。
  • “单次准备,多次执行”优化必须透明地自动执行。

为什么不使用其他库?

  • PEAR DB、ADOdb:这些库并没有简化DBMS访问,它们只是提供了单一(且过重)的访问接口;调试功能太差。
  • PDO:需要PHP 5;处理占位符和查询结果不太方便。
  • 标准的PHP数据库访问函数:代码可读性差,调试困难,容易受到SQL注入攻击。

库接口(概述)

mixed connect(string $dsn) 使用DSN语法连接任何数据库的静态函数。

mixed select(string $query [,$arg1...]) 执行查询并返回2D数组的结果。

hash selectRow(string $query [,$arg1...]) 获取单行查询的结果。

array selectCol(string $query [,$arg1...]) 获取一列。

scalar selectCell(string $query [,$arg1...]) 获取一个查询结果单元格。

mixed selectPage(int &$total, string $query [,$arg1...) 获取包含计算找到的行总数的2D数组。

mixed query(string $query [,$arg1...]) 执行非SELECT查询;对于自增字段和INSERT查询,返回最后一个插入的ID。

mixed transaction([mixed $parameters]) 开始新的事务。

mixed commit() / mixed rollback() 提交/回滚当前事务。

此外,您还可以使用保留的列别名(ARRAY_KEY*等)和属性SQL注释(例如,用于启用缓存)来修改结果表示的格式。请参见以下使用示例。

概要

添加到您的 composer.json 文件中

    "require": {
       "dklab/dbsimple": "dev-master"
    },
    "repositories":[
        {
            "type": "git",
            "url": "https://github.com/Ambalus/DbSimple.git"
        }
    ]

列表 1:连接到数据库管理系统

require_once './vendor/autoload.php';
$DB = DbSimple\Generic::connect("pgsql://login:password@host/database");

或者

require_once './vendor/autoload.php';
$DB = new DbSimple\Connect("pgsql://login:password@host/database");

或者

您可以使用 PDO DSN 语法(当前仅支持 MySQL 提供商的套接字连接)

require_once './vendor/autoload.php';
$DB = new DbSimple\Connect("mysqli:unix_socket=/cloudsql/app:instance;user=root;pass=;dbname=testdb");

列表 2:获取所有结果行

$rows = $DB->select('SELECT * FROM ?_user LIMIT 10');
foreach ($rows as $numRow => $row) {
    echo $row['user_name'];
}

列表 3:获取一页

// Variable $totalNumberOfUsers will hold total number of found rows.
$rows = $DB->selectPage(
    $totalNumberOfUsers,
    'SELECT * FROM ?_user LIMIT ?d, ?d',
    $pageOffset, $numUsersOnPage
);

列表 4:SQL 查询中的宏替换

$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:SQL 查询中的宏替换 #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:SQL 查询中的宏替换 #3

$rows = $DB->select('
        SELECT * FROM user
        WHERE 
            1=0 
          { OR user_id IN(?a) }
    ',
    $listOfUserIdsMayBeEmpty
    // If empty, resulted to 1=0 which means false.
);

列表 7:按时间缓存查询结果

$DB->setCacher('myCacher');
$row = $DB->select('
    -- CACHE: 10h 20m 30s
    SELECT * FROM table WHERE id=123
');

// Define caching function.
function myCacher($key, $value)
{
    // If $value !== null then we must store it to the cache with key $key.
    // If $value === null then we must return the value stored in the cache with key $key.
}

列表 8:基于表修改的查询结果缓存

// Here forum.modified and topic.modified are TIMESTAMPs.
$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:基于列表的占位符

$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:关联占位符

$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:基于标识符的占位符

$DB->select('SELECT ?# FROM tbl', 'date');
// MySQL: SELECT `date` FROM tbl
// FireBird: SELECT "date" FROM tbl

列表 13:基于标识符列表的占位符

$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:{基于前缀的占位符}

$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 underatands that it should execure "prepare" only once!
  $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();
}

// As a result we will get:
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:临时错误禁用

// Please note the "@" operator usage!
// Also an unique index must be created for id field.
if (!@$DB->query('INSERT INTO tbl(id, field) VALUES(1, ?)', $field)) {
  // Here goes the reaction on the query error.
  // You may fetch error context using $DB->error property.
  $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'];
  // Print the query (of course it's better to use Debug_HackerConsole).
  echo "<xmp title=\"$tip\">"; print_r($sql); echo "</xmp>";
}

// Will be printed something like:
SELECT * FROM U_GET_PARAM_LIST;
  --- 13 ms = 4+3+6; returned 30 row(s);