SpartakuS, будет многа букв наверно, все что ниже мой личное мнение =)
Что главное при создании библиотеки, не только бд, а любой:
1. удобство (прежде всего для себя самого)
2. лёгкая расширяемость
3. возможность быстро дёрнуть код и использовать в другом проекте
4. отсутствие повторяющегося кода
у тебя пока из 4 пунктов есть только четвёртый, и то с натяжкой…
будем плясать с самого начала, нет методов для получения кол-ва затронутых запросом строк, последнего автоинкримент ключа, дисконнекта плюс сама реализация порочна, так как ты не чистишь за собой память (нет ни одного mysql_close/mysql_free_result)
по коду:
// Уничтожаем объект в духе ООП :)
// сам метод неправильно назван? должен быть __destruct()
public function destroy()
{
// неверно, нужно mysql_close($this->_dbHandle)
// так как unset($this) сборщик мусора сам вызовет после того
// как у тебя не останется ни одного линка на объект
unset($this);
}
теперь если сделаешь как в коде выше, что произойдёт, если сделать так:
$db1 = new DB('dle');
$db2 = new DB('dle');
unset($db1);
$db2->query($sql);
- словишь ошибку в 4 строке, так как деструктор убъёт подключение к бд, это есть плохо, соответственно нужно делать реестр подключений примерно так:
class nyaaDatabase
{
// список подключений к бд
protected static $_instances = array();
/**
* Возвращает инстанс подключения к определённой базе данных
*
* @param string $name имя подключения
* @return nyaaDatabaseDriver
*/
public static function getInstance($name = 'dle')
{
if (!array_key_exists($name, self::$_instances))
{
// если объект ещё не создан, то создаём его
// загружаем конфиг подключения (сама реализация конфига может быть любой, сыть не меняется)
$config = nyaaConfig::get('database.' . $name);
if (is_null($config))
{
// если в конфиге не обнаружены параметры подключения данной бд, то генерируем исключение
throw new nyaaExceptionDatabaseNotConfigure($name);
}
// загружаем драйвер бд
self::$_instances[$name] = self::_loadDriver($name, $config);
}
return self::$_instance[$name];
}
// это статичный класс, запрещаем интснцирование
final private function __construct()
{}
// … дальше реализация метода загрузки драйвера бд
}
// в итоге можно будет сделать так
$db1 = nyaaDatabase::getInstance('dle');
$db2 = nyaaDatabase::getInstance('dle');
unset($db1);
$db2->query($sql); // тут никакой ошибки не возникнет, так как линк на объект не единственный и дектруктор при ансете не вызовется
Что такое nyaaDatabaseDriver? - Подумай, вдруг тебе понадобится реализовать класс работы не с MySQL а постгресс например, тебе придётся либо писать новую библиотеку работы с бд, либо переписывать старую, называть её по другому (если опять же нужно работать одновременно с разными бд) Возможно у этих библиотек ещё будут разные интерфейсы работы, короче минусов куча
Поэтому лучше использовать драйвер, примерно как у меня выше в коде (nyaaDatabaseDriver):
abstract class nyaaDatabaseDriver
{
protected $_config;
protected $_dbHandle = NULL;
protected $_lastQuery = '';
protected $_lastErrorMessage = '';
abstract public function __construct($config);
abstract public function __destruct();
abstract public function connect();
abstract public function query();
abstract public function getAffectedRows();
abstract public function getInsertId();
// и т.д.
}
Потом просто создаёшь драйвер и реализуешь все методы драйвера для примера:
class nyaaDatabaseDriverMysql extends nyaaDatabaseDriver
{
public function __construct($config)
{
$this->_config = $config;
}
public function __destruct()
{
if (!is_null($this->_dbHandle))
{
mysql_close($this->_dbHandle);
}
}
// etc.
}
Почему именно класс nyaaDatabaseDriver, а не интерфейс: стандартные операции можно реализовать прямо в базовом классе драйвера, вот сейчас сразу видно - конструктор только копирует конфиг и все, деструктор рвет соединение с бд, можно сделать так:
abstract class nyaaDatabaseDriver
{
protected $_config;
protected $_dbHandle = NULL;
protected $_lastQuery = '';
protected $_lastErrorMessage = '';
public function __construct($config)
{
$this->_config = $config;
}
public function __destruct()
{
$this->_disconnect();
}
abstract public function connect();
abstract protected function _disconnect();
// и т.д.
}
class nyaaDatabaseDriverMysql extends nyaaDatabaseDriver
{
// реализаия конструктора и деструктора уже ненужна
protected function _disconnect()
{
if (!is_null($this->_dbHandle))
{
mysql_close($this->_dbHandle);
}
}
// и т.д.
}
Почему нет коннекта в конструкторе драйвера? Потому что лучше в query в начале проверять есть ли соединение, и если нет, то делать коннект, так как в дальнейшем возможно сделать кеширование результатов, и коннект вообще не понадобиться, тоесть бд вроде и будет инстанцирована, но соединение не будет сделано, сэкономим пару милисекунд =)
Потом, реализовывать метод fetch прямо в драйвере крайне непредусмотрительно, а если понадобится в процессе работы с одной выборкой сделать ещё один запрос, косяк:
$db = new DB('dle');
if ($db->query($sql))
{
while ($db->fetch('row'))
{
$sql = 'UPDATE чтонибуть';
$db->query($sql);
// и тут наступает писец =)
}
}
// плюс тебе самому нужно вызывать унылую функцию mysql_free_result
Поэтому лучше реализовать ещё один класс - nyaaDatabaseDriverResult или что-то типо того, опять же абстрактный базовый класс, и наследуемые классы для каждого типа подключения
class nyaaDatabaseDriverMysqlResult extends nyaaDatabaseDriverResult
{
public function __construct($result)
{
$this->_result = $result;
$this->_rowNumber = 0;
$this->_rowsCount = mysql_num_rows($result);
}
public function __destruct()
{
if (is_object($this->_result))
{
mysql_free_result($this->_result);
}
}
public function next()
{
$this->_rowNumber += 1;
return mysql_fetch_row($this->_result);
}
// и тд
}
И пусть у тебя метод query бд возвращает нужный nyaaDatabaseDriverResult либо true|false для запросв типа инсерта/апдейта и т.п.
чтото задолбался писать…
а ещё, самое главное =) накогда так не делай:
public function __construct()
{
try {
$this->_linkIdentifier = @mysql_connect($this->_host . ':' . $this->_port, $this->_user, $this->_pswd);
if (!$this->_linkIdentifier){
throw new Exception('Can\'t connect to host: ' . $this->_host);
}
if (!mysql_select_db($this->_database, $this->_linkIdentifier)) {
throw new Exception('Can\'t use database: ' . $this->_database);
}
} catch (Exception $e) {
echo '<b>Caught exception:</b> ' . $e->getMessage();
}
}
Исключение отлавливать нужно снаружи, а если реализовать так как тут, то ты просто никогда не узнаешь создано ли было соединение, в итоге трудноуловимые ошибки.
Work, buy, consume, die