Проблемы с PHP с использованием блокировки файловых файлов

У меня проблема с использованием функции 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); } }