форум вебмастероff на AlfaSpace.NET |
Ubuntu и Linux
|реактивный бесплатный хостинг
| Киноклуб. ВСЁ БЕСПЛАТНО!
база знаний хостинга
|
правила форума
| Начало ° Ответить ° Статистика ° Опрос ° Регистрация ° Поиск ° FAQ ° |
| Форум вебмастеров на AlfaSpace.NET / The Matrix Has You / Эффективная работа с файлами данных в php |
| Автор | Сообщение |
| Андрей Алексеевич 2125 |
# Дата: 3 Май 2007 09:20 ° Поправил: Андрей Это не статья, а скорее размышления на тему хранения массивов на сервере в файлах.. Часто встречаются ситуации, когда хранить ту, или иную информацию приходиться в файлах, а не в базе данных. И обоснованно это не только отстутствием последней, но и другими факторами: например, нельзя хранить реквизиты подключения к бд в ней самой, и не рационально в ней хранить настройки системы (которые имеют вид ключ => значение). После не очень долгих размышлений я выбрал два наиболее рациональные способы храниения данных на диске. Кстати, скажу сразу, я рассматривал только случаи, когда разметкой файла занимаются (в большей степени) родные функции php, так что вариант: file.dat:
val1|val2|val3
parse.php:
list($var1, $var2, $var3) = explode('|', file_get_contents('file.dat'));
Я рассматривать не стал. Это самый простой, самый не гибкий метод хранения, и он подходит, разве что, для записи логов, где больше записи, чем чтения. А я выбирал варианты, где чтение происходит во много раз чаще, чем запись. Кроме того, я так же не рассматриваю разметку xml, так как, считаю что использование xml не подходит для хранения данных, которые очень часто будут читаться и записываться, во всяком случае в реалиях текущего состояния php(4) и хостингов; xml лучше использовать для экпортируемых или импортируемых данных и в качестве транспорта. Запись var_export() массивов в php файл: Область применения этого метода — запись массивов в файл, лучше подходит для создания конфигурационных файлов, где есть вероятность того, что файл придётся редактировать вручную, и где запись производится довольно редко относительно чтения. Достоинства и недостатки: + Функция var_export() делает addslashes, а возврат массива автоматически делает stripslashes, по-этому не надо заботиться о фильтрации. + Не нужно сторонних функций для чтения файла (только include). + Если открыть файл в браузере, ничего не выполнится и не отобразится, по-этому сторонняя защита файла не требуется. (только если разрешение файла «.php») + Легко редактировать вручную, так как нет офсетов и файл будет прочтён верно. + У Zend машины есть схемы кеширования «.php» файлов. - Функия var_export не предназначена для записи массивов на диск, по-этому её поведение в будущих версиях php непредсказуемо. - Массив читается полностью, нельзя сделать индексированное чтение. - Неподходит для записи больших таблиц (как в базе данных). - Ошибка записи может вызвать фатальную ошибку чтения, кроме того, файл, с разрешением на запись в формате «.php» является повышенно уязвимым. - Скорость записи ниже чем у второго метода, причём чем больше объём, тем больше проигрыш. Пример функции записи массива на диск:
/**
* $file — имя файла
* $array - записываемый массив
* $create - флаг, создать файл, если он не существует.
*/
function dump_write($file, $array, $create = true)
{
@ignore_user_abort(true);
if ($create === false && !is_file($file))
return false;
// Тут используется «безопасный» метод записи,
// когда файл сначало создаётся в temp папке, а
// а затем заменяется. Это уменьшает вероятность
// обнуления файла.
// (Укажите вместо 'temp' папку с правами на запись)
$tmp = tempnam('temp' ,'FL');
$fp = fopen($tmp, 'wb');
if(is_resource($fp))
{
// Собственно сам перевод массива в строку php кода:
fputs($fp, '<?php return('. var_export($array, true) .'); ?>');
fclose($fp);
// Проверка на возможность переименовки (для windows)
if (@!rename($tmp, $file))
{
@unlink($file);
@rename($tmp, $file);
@unlink($tmp);
}
@ignore_user_abort(false);
return true;
}
@ignore_user_abort(false);
return false;
}
/**
* Пример записи файла:
* ------
* dump_write('file.php', array( 'var1' => 'val1', 'var2' => 'val2', 'array1' =>
array( 'var3' => 'val3', 'var4' => 'val4' ) ));
* Пример записанного файла (file.php):
* ------
* <?php return(array (
* 'var1' => 'val1',
* 'var2' => 'val2',
* 'array1' =>
* array (
* 'var3' => 'val3',
* 'var4' => 'val4',
* ),
* )); ?>
*
* Пример чтения файла:
* ------
* $array = include('file.php');
*/
Это лучший вариант для хранения одномерных и несложных многомерных массивов. Запись serialized данных в файл: Область применения этого способа — хранение любых данных в файлах. Можно хранить массивы любой вложенности, кроме того, можно хранить ссылки и объёкты. Подходит для хранения относительно большого объёма данных в файлах, например, подобие таблиц базы данных. Достоинства и недостатки: + Функция serialize сделана специально для хранения данных, так что её поведение вряд ли изменится. + Функции serialize и unserialize работают достаточно быстро. + Функции serialize и unserialize можно перевести на другой язык, например на JavaScript, и парсить ими уже там. + Файл, с серилизованными данными можно читать не весь, а, например, записывать серилизованный массив на каждую строку файла, что экономит ресурсы при чтении и позволяет сделать отдельный индекс и искать по файлу быстрее (брать только требуемую чать файла). + Файл читается через функцию unserialize, и код, записанный в нём не может выполнится, в отличие от первого способа. - Редактирование вручную очень вероятно может привести к полной нечитаемости данных функцией unserialize, т.к нарушение offset является фатальной ошибкой для парсера serialized данных. - Файл с серилизованными данными требует отдельной защиты, так как по-умолчанию может быть прочитан в браузере, по-этому нужно сделать защиту либо через .htaccess, либо через права чтения. - Перед десериализацией файла, нужно его прочитать, что тоже выделяет память. Пример функции записи массива на диск:
/**
* $file — имя файла
* $array - записываемый массив
* $create - флаг, создать файл, если он не существует.
*/
function dump_write($file, $array, $create = true)
{
@ignore_user_abort(true);
if ($create === false && !is_file($file))
return false;
// Тут используется «безопасный» метод записи,
// когда файл сначало создаётся в temp папке, а
// а затем заменяется. Это уменьшает вероятность
// обнуления файла.
// (Укажите вместо 'temp' папку с правами на запись)
$tmp = tempnam('temp' ,'FL');
$fp = fopen($tmp, 'wb');
if(is_resource($fp))
{
// Собственно сам перевод массива в строку php кода:
fputs($fp, serialize($array));
fclose($fp);
// Проверка на возможность переименовки (для windows)
if (@!rename($tmp, $file))
{
@unlink($file);
@rename($tmp, $file);
@unlink($tmp);
}
@ignore_user_abort(false);
return true;
}
@ignore_user_abort(false);
return false;
}
/**
* Пример записи файла:
* ------
* dump_write('file.dat', array( 'var1' => 'val1', 'var2' => 'val2', 'array1' =>
array( 'var3' => 'val3', 'var4' => 'val4' ) ));
* Пример записанного файла (file.php):
* ------
* a:3:{s:4:"var1";s:4:"val1";s:4:"var2";s:4:"val2";s:6:"array1";
* a:2:{s:4:"var3";s:4:"val3";s:4:"var4";s:4:"val4";}}
*
* Пример чтения файла:
* ------
* $array = unserialize(file_get_contents('file.dat'));
*/
Этот способ хорошо подходит для хранения массивов с довольно длинными элементами, для хранения кешей, и для использования в качестве базы данных. Сорри, что немного ломано написал, всё таки не в публицистической форме.. Интересно будет услышать чужие мнения. |
| acsid Alfa Guru 2388 |
# Дата: 3 Май 2007 11:59 Добавлю пару слов. Безусловно всё сказанное выше правда, но основная проблема работы с фаилами заключается в том что при перекрёстных запросах к одному и томуже фаилу , можна потерять либо фаил либо его содержимое.Предположем у нас 3ри запроса -один на дозапись, другой на чтение, третий на перезапись.Вероятность возникновения ошибки достаточно мала т.к. вероятность того что эти 3ри запроса пересекуться тоже мизерна.Хорошо теперь предположим что у нас 400 пользователей одновременно работают с ресурсом , тобиж вызывают на сервере перезапись одного и тогоже фаила в с относительно мизерным интервалом времени.От такого даже флок не спасает. И тогда уже нет разницы какми из указанных в посте выше сбособами мы пользуемся .В результате мы имеем дело с потерей данных.Вот впринцепи основной косяк работы с файлами при больших нагрузках. |
| Андрей Алексеевич 2125 |
# Дата: 3 Май 2007 13:19 ° Поправил: Андрей acsid Ну я рассматривал случаи, где производится очень много чтений, и не очень много записей, по-этому тут проблема обнуления стоит не очень остро. Кстати, в коде у меня был фрагмент: $tmp = tempnam('temp' ,'FL');
$fp = fopen($tmp, 'wb');
if(is_resource($fp))
{
fputs($fp, $content);
fclose($fp);
if (@!rename($tmp, $file))
{
@unlink($file);
@rename($tmp, $file);
@unlink($tmp);
}
@ignore_user_abort(false);
return true;
}Т.е мы сначало создаём уникальный файл в папке temp, и записываем контент в него, после этого заменяем старый файл файлом из папки temp, и удаляем последний. Таким образом можно избавиться от опасности обнуления файла (во всяком случае до ~80%), так как файловые функции, по типу копирования, работают очень быстро и кроме того, файл заменяется полностью. Если к этому приписать функции flock (надо принять во внимание, что сам flock юзать не рекомендуется, так как она работает корректно на очень малом кол-ве ОС), то может получиться довольно неплохая защита против перекрёстной записи. Остаётся проблема, когда один пользователь(1) начал работать со старым файлом, а в этот момент произошла замена вторым пользователем(2), и после перезаписи, изменения внесённые (2) не сохранятся, так как файл был прочитан до его начала работы. Но вероятность таких событий намного меньше, чем кажется на первый взгляд, особенно если не просто напрямую работать с файлом, а использовать замену из temp-ового файла. |
| acsid Alfa Guru 2388 |
# Дата: 3 Май 2007 13:53 ° Поправил: acsid Я тоже работал над этим, когда реализовывал алкочат. По моей схеме при работе с фаилом создаёться временный фаил который не даёт другим пользователям начать работу с фаилом.Короче все ждут в цикле пока операция не закончена. Короче написал несколько функций в алтернативу базовым , для работы с фаилами.
$Lock_dir = "lock"; папка где бубут временные фаилы
function touchString($file) {
global $Lock_dir;
if(!is_writeable($Lock_dir)){
return false;
}
$tmp = "$Lock_dir/".$file.".tmp";
while(1) {
if ( is_file($tmp)) {
while( file_exists($tmp)){
$file_exist++;
if($file_exist > 5){ break;}
clearstatcache();
sleep(1);
}
}
return touch($tmp);
}
}
function delString($file) {
global $Lock_dir;
$tmp = "$Lock_dir/".$file.".tmp";
if( is_file($tmp)){ return unlink($tmp);}
else{
return false;
}
}
function FileArray($file) {
if (!is_readable($file)){
return false;
}
else{
touchString($file);
$get_file_st=@file_get_contents($file);
$bufer = @file($file);
delString($file);
return $arr=array($bufer,$get_file_st);
}
}
function OpenFile($file, $mode) {
touchString($file);
if(is_writeable($file)){
$get_file_st=@file_get_contents($file);
$bufer = @file($file);
$fido = fopen($file, $mode);
@flock ($fido, LOCK_EX+LOCK_NB);
set_file_buffer ($fido,0);
return $fp = array($fido,$bufer,$get_file_st);
}
else{
return false;
}
}
function CloseFile($fp, $file) {
if(empty($fp)){
delString($file);
return false;
}
else{
@flock ($bufer, LOCK_UN+LOCK_NB);
$bufer = fclose($fp);
delString($file);
return $bufer;
}
}
//-----------------чтение из фаила-----------------
$f = FileArray ("database/file_base.dat");
$file=$f[0];/ /фаил массивом как после file();
$file_get_com=$f[1]; //фаил целиком(строкой) как после file_get_contents();
//-----------------запись/перезапись
$fp_ar = OpenFile ("фаил", "w"); // конект к фаилу если хотим записать или пере |
|
Powered by miniBB forum software © 2001-2008
Powered by miniBB-gzipper. Original size:35878, gzipped size:12478 |