У меня проблема с использованием функции flock()
PHP. Мне нужно написать две разные переменные ( $O
и $o
), но часто она не записывает вторую переменную ( $o
), может быть, потому, что файл записывается два раза подряд.
Вот код:
include_once "changevar.php"; changevar("O",$seguimedia,$filename,0); changevar("o",$offerta,$filename,0);
$seguimedia
, $filename
и $offerta
установлены правильно.
changevar.php:
function changevar($varname,$newval,$filename,$type) { while(!$fp=fopen($filename,"c+")) { usleep(100000); } while(!flock($fp,LOCK_EX)) { usleep(100000); } while(!include($filename)) { usleep(100000); } ftruncate($fp,0); rewind($fp); $$varname=$newval; if($type==0) { foreach(array("u","p","t","d") as $v){$$v=str_replace("\\","\\\\",$$v);} $text="<?\$o=$o;\$u=\"$u\";\$c=$c;\$m=$m;\$p=\"$p\";\$C=$C;\$id=\"$id\";\$t=\"$t\";\$d=\"$d\";\$O=$O;?>"; } else { $text="<?\$impressions=$impressions;\$clickunici=$clickunici;\$clicknulli=$clicknulli;\$creditiguadagnati=$creditiguadagnati;\$creditiacquistati=$creditiacquistati;\$creditiutilizzati=$creditiutilizzati;?>"; } fwrite($fp,$text); flock($fp,LOCK_UN); fclose($fp); }
вfunction changevar($varname,$newval,$filename,$type) { while(!$fp=fopen($filename,"c+")) { usleep(100000); } while(!flock($fp,LOCK_EX)) { usleep(100000); } while(!include($filename)) { usleep(100000); } ftruncate($fp,0); rewind($fp); $$varname=$newval; if($type==0) { foreach(array("u","p","t","d") as $v){$$v=str_replace("\\","\\\\",$$v);} $text="<?\$o=$o;\$u=\"$u\";\$c=$c;\$m=$m;\$p=\"$p\";\$C=$C;\$id=\"$id\";\$t=\"$t\";\$d=\"$d\";\$O=$O;?>"; } else { $text="<?\$impressions=$impressions;\$clickunici=$clickunici;\$clicknulli=$clicknulli;\$creditiguadagnati=$creditiguadagnati;\$creditiacquistati=$creditiacquistati;\$creditiutilizzati=$creditiutilizzati;?>"; } fwrite($fp,$text); flock($fp,LOCK_UN); fclose($fp); }
вfunction changevar($varname,$newval,$filename,$type) { while(!$fp=fopen($filename,"c+")) { usleep(100000); } while(!flock($fp,LOCK_EX)) { usleep(100000); } while(!include($filename)) { usleep(100000); } ftruncate($fp,0); rewind($fp); $$varname=$newval; if($type==0) { foreach(array("u","p","t","d") as $v){$$v=str_replace("\\","\\\\",$$v);} $text="<?\$o=$o;\$u=\"$u\";\$c=$c;\$m=$m;\$p=\"$p\";\$C=$C;\$id=\"$id\";\$t=\"$t\";\$d=\"$d\";\$O=$O;?>"; } else { $text="<?\$impressions=$impressions;\$clickunici=$clickunici;\$clicknulli=$clicknulli;\$creditiguadagnati=$creditiguadagnati;\$creditiacquistati=$creditiacquistati;\$creditiutilizzati=$creditiutilizzati;?>"; } fwrite($fp,$text); flock($fp,LOCK_UN); fclose($fp); }
вfunction changevar($varname,$newval,$filename,$type) { while(!$fp=fopen($filename,"c+")) { usleep(100000); } while(!flock($fp,LOCK_EX)) { usleep(100000); } while(!include($filename)) { usleep(100000); } ftruncate($fp,0); rewind($fp); $$varname=$newval; if($type==0) { foreach(array("u","p","t","d") as $v){$$v=str_replace("\\","\\\\",$$v);} $text="<?\$o=$o;\$u=\"$u\";\$c=$c;\$m=$m;\$p=\"$p\";\$C=$C;\$id=\"$id\";\$t=\"$t\";\$d=\"$d\";\$O=$O;?>"; } else { $text="<?\$impressions=$impressions;\$clickunici=$clickunici;\$clicknulli=$clicknulli;\$creditiguadagnati=$creditiguadagnati;\$creditiacquistati=$creditiacquistati;\$creditiutilizzati=$creditiutilizzati;?>"; } fwrite($fp,$text); flock($fp,LOCK_UN); fclose($fp); }
Является ли PHP flock()
хорошим способом избежать подобных проблем?
Какой язык / функции мне нужно использовать?
Проблема заключается в вызове fopen()
.
Вы открываете файл в режиме c+
. Это означает, что указатель файла находится в начале файла, что приведет к перезаписыванию записи, которая уже существует. Чтобы добавить оскорбление к травме, вы вызываете ftruncate()
, обрезая файл до 0 байт прямо перед записью – поэтому перед каждой записью вы вручную удаляете весь файл. Таким образом, этот код гарантирует, что останется только последняя запись в файл, вручную заботясь о стирании каждого другого.
Вызов fopen()
должен выполняться с режимом a+
, и обе функции ftruncate()
и ftruncate()
должны идти (последняя также имеет эффект ввода указателя файла в начале).
Неважно, если вы пишете два раза из одного сценария. Но на самом деле это имеет значение, когда вы пробуете это из двух разных процессов и используете блокировку файлов …
Во всяком случае, ваша функция changevar () на самом деле обрезает файл каждый раз, поэтому я предполагаю, что поэтому «кажется», что был написан только один var.
Честно говоря, я действительно думаю, что это очень очень плохая идея чтения и написания PHP-файла. Если вы смотрите на конфигурации, используйте ini
или json
.
Если вы действительно хотите читать и писать в файл, это может быть просто:
$file = __DIR__ . "/include/config.json"; $var = new FileVar($file); $var['o'] = "Small o"; $var['O'] = "Big O"; $var->name = "Simple json"; echo file_get_contents($file);
Вывод
{ "o": "Small o", "O": "Big O", "name": "Simple json" }
Другой пример
// To remove unset($var['O']); // to update $var['o'] = "Smaller o";
с// To remove unset($var['O']); // to update $var['o'] = "Smaller o";
Вывод
{ "o": "Smaller o", "name": "Simple json" }
Обратите внимание: папка include содержит этот .htaccess
<Files "*"> Order Deny,Allow Deny from all </Files>
Чтобы проверить, действительно ли работает эта lock
я использовал pthreads для эмуляции состояния гонки
for($i = 0; $i < 100; $i ++) { $ts = array(); // Generate Thread foreach(range("A", "E") as $letter) { $ts[] = new T($file, $letter); } // Write all files at the same time foreach($ts as $t) { $t->start(); } // Wait for all files to finish foreach($ts as $t) { $t->join(); } } // What do we have echo file_get_contents($file);
Основной класс
class FileVar implements ArrayAccess { private $file; private $data; private $timeout = 5; function __construct($file) { touch($file); $this->file = $file; $this->data = json_decode(file_get_contents($file), true); } public function __get($offset) { return $this->offsetGet($offset); } public function __set($offset, $value) { $this->offsetSet($offset, $value); } public function offsetSet($offset, $value) { if (is_null($offset)) { $this->data[] = $value; } else { $this->data[$offset] = $value; } $this->update(); } public function offsetExists($offset) { return isset($this->data[$offset]); } public function offsetUnset($offset) { unset($this->data[$offset]); $this->update(); } public function offsetGet($offset) { return isset($this->data[$offset]) ? $this->data[$offset] : null; } private function update() { // Open file with locking $time = time(); while(! $fp = fopen($this->file, "c+")) { if (time() - $time > $this->timeout) throw new Exception("File can not be accessed"); usleep(100000); } // Lock the file for writing flock($fp, LOCK_EX); // Overwrite the old data ftruncate($fp, 0); rewind($fp); // Write the new array to file fwrite($fp, json_encode($this->data, 128)); // Unlock the file flock($fp, LOCK_UN); // Close the file fclose($fp); } }
неclass FileVar implements ArrayAccess { private $file; private $data; private $timeout = 5; function __construct($file) { touch($file); $this->file = $file; $this->data = json_decode(file_get_contents($file), true); } public function __get($offset) { return $this->offsetGet($offset); } public function __set($offset, $value) { $this->offsetSet($offset, $value); } public function offsetSet($offset, $value) { if (is_null($offset)) { $this->data[] = $value; } else { $this->data[$offset] = $value; } $this->update(); } public function offsetExists($offset) { return isset($this->data[$offset]); } public function offsetUnset($offset) { unset($this->data[$offset]); $this->update(); } public function offsetGet($offset) { return isset($this->data[$offset]) ? $this->data[$offset] : null; } private function update() { // Open file with locking $time = time(); while(! $fp = fopen($this->file, "c+")) { if (time() - $time > $this->timeout) throw new Exception("File can not be accessed"); usleep(100000); } // Lock the file for writing flock($fp, LOCK_EX); // Overwrite the old data ftruncate($fp, 0); rewind($fp); // Write the new array to file fwrite($fp, json_encode($this->data, 128)); // Unlock the file flock($fp, LOCK_UN); // Close the file fclose($fp); } }
Класс тестирования
class T extends Thread { function __construct($file, $name) { $this->file = $file; $this->name = $name; } function run() { $var = new FileVar($this->file); $var[$this->name] = sprintf("Letter %s", $this->name); } }