Необходима более устойчивая и масштабируемая технология хранения простых данных, чем текстовых файлов.
Для доступа к базе данных типа DBM следует использовать уровень абстракции DBA:
$dbh = dba_open('fish.db','c','gdbm') or die($php_errormsg);
// извлекаем и модифицируем значения
if (dba_exists('flounder',$dbh)) {
$flounder_count = dba_fetch('flounder',$dbh);
$flounder_count++;
dba_replace('flounder',$flounder_count);
print "Updated the flounder count.";
} else {
dba_insert('flounder',1);
print "Started the flounder count.";
}
// больше нет ни одной тилапии
dba_delete('tilapia',$dbh);
// какая у нас рыбка?
for ($key = dba_firstkey($dbh); $key !== false; $key = dba_nextkey($dbh)) {
$value = dba_fetch($key);
print "$key: $value\n";
}
dba_close($dbh);
PHP способен поддерживать несколько различных типов машин баз
данных DBM: GDBM, NDBM, DB2, DB3, DBM и CDB. Уровень абстракции DBA позволяет использовать одни и те же функции на любой машине DBM. Все машины хранят пары ключ/значение. Можно выполнять циклы по всем ключам базы данных, извлекать значение, связанное с конкретным ключом, и определять, есть ли определенный ключ.
И ключи, и значения представляют собой строки.
Следующая программа поддерживает список имен пользователей и
паролей с помощью базы данных DBM. Имя пользователя – это первый аргумент командной строки, а пароль – второй аргумент. Если
имя пользователя уже существует в базе данных, то пароль заменяется данным паролем; в противном случае комбинация из имени пользователя и пароля добавляется в базу данных:
$user = $_SERVER['argv'][1];
$password = $_SERVER['argv'][2];
$data_file = '/tmp/users.db';
$dbh = dba_open($data_file,'c','gdbm') or die("Can't open db $data_file");
if (dba_exists($user,$dbh)) {
print "User $user exists. Changing password.";
} else {
print "Adding user $user.";
}
dba_replace($user,$password,$dbh) or die("Can't write
to database $data_file");
dba_close($dbh);
Функция dba_open() возвращает дескриптор файла DBM (или false в
случае ошибки). Она принимает три аргумента. Первый аргумент –
это имя файла DBM, а второй аргумент – режим открытия файла. Режим 'r' открывает доступ к существующей базе данных только на чтение, а 'w' открывает существующую базу данных на чтение и запись.
Режим 'c' открывает доступ к базе данных на чтение/запись и создает
базу данных, если она не существует. Последний режим, 'n', делает то
же самое, что и режим 'c', но если база данных уже существует, то
очищает ее. Третий аргумент функции dba_open() указывает используемый DBM-обработчик; в данном примере это 'gdbm'. Чтобы определить, какой DBM-обработчик был скомпилирован во время инсталляции PHP, загляните в секцию «DBA» вывода функции phpinfo(). Строка «Supported handlers» показывает то, что было выбрано.
Функция dba_exists() позволяет определить, есть ли такой ключ в базе
данных DBM. Она принимает два аргумента: строку-ключ и дескриптор файла DBM. Она ищет ключ в файле DBM и возвращает true, если
находит его (или false, если поиск неудачен). Функция dba_replace()
принимает три аргумента: ключ-строку, строковое значение и дескриптор файла DBM. Она помещает ключ/значение в файл DBM. Если
элемент с данным ключом уже существует, она заменяет этот элемент
новым значением.
Для закрытия базы данных вызовите функцию dba_close(). Файл
DBM, открытый функцией dba_open(), автоматически закрывается при
завершении работы сценария, но необходимо явно вызвать функцию
dba_close(), чтобы закрыть постоянные соединения, созданные функцией dba_popen().
С помощью функций dba_firstkey() и dba_nextkey() можно пройти в
цикле по всем ключам в файле DBM, а функция dba_fetch() позволяет
извлекать значения, связанные с каждым ключом. Приведенная ниже
программа вычисляет общую длину всех паролей в файле DBM:
$data_file = '/tmp/users.db';
$total_length = 0;
if (! ($dbh = dba_open($data_file,'r','gdbm'))) {
die("Can't open database $data_file");
}
$k = dba_firstkey($dbh);
while ($k) {
$total_length += strlen(dba_fetch($k,$dbh));
$k = dba_nextkey($dbh);
}
print "Total length of all passwords is $total_length characters.";
dba_close($dbh);
Функция dba_firstkey() инициализирует переменную $k значением первого ключа в файле DBM. При каждом прохождении цикла while функция dba_fetch() извлекает значение, соответствующее ключу $k, и переменная $total_length увеличивается на длину значения (вычисленного
посредством функции strlen()). С помощью функции dba_nextkey() переменной $k присваивается значение следующего ключа из файла.
Функция serialize() позволяет реализовать хранение сложных данных в файле DBM – точно так же, как это делается в случае текстового файла. Однако данные в файле DBM могут быть индексированы ключом:
$dbh = dba_open('users.db','c','gdbm') or die($php_errormsg);
// читаем данные и выполняем обратное преобразование
// из последовательной формы
if ($exists = dba_exists($_REQUEST['username'])) {
$serialized_data = dba_fetch($_REQUEST['username'])
or die($php_errormsg);
$data = unserialize($serialized_data);
} else {
$data = array();
}
// обновляем значения
if ($_REQUEST['new_password']) {
$data['password'] = $_REQUEST['new_password'];
}
$data['last_access'] = time();
// записываем данные обратно в файл
if ($exists) {
dba_replace($_REQUEST['username'],serialize($data));
} else {
dba_insert($_REQUEST['username'],serialize($data));
}
dba_close($dbh);
Несмотря на то что код этого примера может сохранять данные нескольких пользователей в одном файле, нельзя найти, например, время последней регистрации пользователя без выполнения цикла по
всем ключам файла. Структурные данные такого типа относятся к базам данных SQL.
В некоторых областях каждый DBM-обработчик ведет себя по-разному. Например, GDBM предоставляет внутреннюю блокировку. Если
один процесс открыл файл GDBM в режиме чтения/записи, то еще
один вызов функции dba_open() для открытия того же самого файла в
режиме чтения/записи закончится неудачей. Однако обработчик DB3
не предоставляет внутренней блокировки; для этого необходимо написать дополнительный код, как объясняется в рецепте Блокировка файла для текстовых файлов. Две DBA-функции также имеют особенности, связанные с типами баз данных: dba_optimize() и dba_sync().
Функция dba_optimize() вызывает специфическую для обработчика функцию оптимизации файла DBM. В настоящее время она реализована только для
GDBM, при этом вызывается его функция gdbm_reorganize(). Функция
dba_sync() вызывает специфическую для обработчика функцию синхронизации файла DBM. В случае DB2 и DB3 вызывается их функция sync(). В случае GDBM вызывается его функция gdbm_sync(). Если применяются другие DBM-обработчики, то ничего не выполняется.
Использование базы данных DBM представляет собой шаг вперед по
сравнению с текстовыми файлами, но при этом большинство возможностей SQL-баз данных недоступны. Структура данных ограничена парами ключ/значение, а устойчивость блокировки сильно зависит от
DBM-обработчика. Все же выбор DBM-обработчиков может быть вполне оправдан, если требуется в основном только чтение данных при
большой нагрузке.
Документацию по функциям DBA на http://www.php.net/dba; подробную информацию по DBM-обработчикам для баз данных DB2и DB3 на http://www.sleepycat.com/faq.html#program; информацию по GDBM на http://www.gnu.org/direc-tory/gdbm.html или на http://www.mit.edu:8001/afs/athena.mit.edu/project/gnu/doc/html/gdbm_toc.html; информацию по CDB на http://cr.yp.to/cdb.html;