PHP使用群文件锁定的问题


PHP issues using flock - file locking

我使用PHP flock()函数有问题。我需要写两个不同的变量($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);
}

PHP flock()是避免这种问题的好方法吗?
我需要使用哪些语言/函数?

问题实际上是在fopen()调用。

您正在以c+模式打开文件。这意味着文件指针位于文件的开头,这将导致任何写操作覆盖已经在那里的内容。雪上加霜的是,您调用ftruncate(),在写入之前将文件截断为0字节——因此,在每次写入之前,您都要手动擦除整个文件。因此,该代码是,保证只保留对文件的最后一次写入,手动清除其他所有写入。

fopen()调用应该使用模式a+,并且ftruncate()rewind()都需要去(后者也具有将文件指针放在开头的效果)。

如果在同一个脚本中编写两次也没关系。但事实上,当您在两个不同的进程中尝试此操作并使用文件锁定时,这确实很重要…

无论如何,你的changevar()函数实际上每次都截断文件,所以我猜这就是为什么它"似乎"只写了一个var。

老实说,我真的认为这是一个非常非常非常坏的主意读取和编写PHP文件。如果您正在查看配置,请使用inijson

如果你真的想读写文件,那么它可以像这样简单:

$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";

输出
{
    "o": "Smaller o",
    "name": "Simple json"
}

请注意包含文件夹包含这个.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 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);
    }
}