ファイルFIFOキュー

30786 ワード

<?php
/**
 * Filefifo.php    FIFO  
 */
class Filefifo
{
    /**
     * $_file_data,         
     */
    private $_file_data = '';
    
    /**
     * $_file_idx,        
     */
    private $_file_idx = '';

    /**
     * $_file_idx_bak,          ,                
     */
    private $_file_idx_bak = '';

    /**
     * $_f_data,        
     */
    private $_f_data;
    
    /**
     * $_f_idx,       
     */
    private $_f_idx;

    /**
     * $_f_idx_bak,         
     */
    private $_f_idx_bak;

    private static $_instance = array();


    public static function instance($file)
    {
        if (! isset(self::$_instance[$file])) {
            self::$_instance[$file] = new self($file);
        }
        return self::$_instance[$file];
    }

    public function __construct($file)
    {
       $this->attach($file);
    }

    public function __destruct()
    {
        $this->detach();       
    }

    /**
     * attach,         
     */
    public function attach($file)
    {
        /**
         *      
         */
        $this->_file_data = $file;
        $this->_file_idx = "{$file}.idx";
        $this->_file_idx_bak = "{$file}.idx.bak";

        if (! file_exists($file)) {
            $f = fopen($file, 'w+');
            fclose($f);

            if (file_exists($this->_file_idx)) unlink($this->_file_idx);
            if (file_exists($this->_file_idx_bak)) unlink($this->_file_idx_bak);
        }

        $idx_data_bak = '';

        /**
         *           ,           
         */
        if (file_exists($this->_file_idx_bak)) {
            $idx_data_bak = file_get_contents($this->_file_idx_bak);
        } else {
            $f = fopen($this->_file_idx_bak, 'w+');
            fclose($f);
        }

        /**
         *           ,         
         */
        if (! file_exists($this->_file_idx)) {
            $f = fopen($this->_file_idx, 'w+');  
            if ($idx_data_bak) fwrite($f, $idx_data_bak);                     
            fclose($f);
        } else {
            if (! file_get_contents($this->_file_idx) && $idx_data_bak) {
                file_put_contents($this->_file_idx, $idx_data_bak);
            }
        }

        $this->_f_data = fopen($this->_file_data, 'a+b');
        $this->_f_idx = fopen($this->_file_idx, 'rw+b');
        $this->_f_idx_bak = fopen($this->_file_idx_bak, 'rw+b');
    }

    /**
     * detach,         
     */
    private function detach()
    {
        if ($this->_f_data) fclose($this->_f_data);
        if ($this->_f_idx) fclose($this->_f_idx);
        if ($this->_f_idx_bak) fclose($this->_f_idx_bak);
        $this->_f_data = NULL;
        $this->_f_idx = NULL;
        $this->_f_idx_bak = NULL;
    }

    /**
     * rewind,       
     */
    public function rewind()
    {
        flock($this->_f_idx, LOCK_EX);
        ftruncate($this->_f_idx, 0);
        ftruncate($this->_f_idx_bak, 0);
        flock($this->_f_idx, LOCK_UN);
    }

    /**
     * end,       
     */
    public function end()
    {
        flock($this->_f_idx, LOCK_EX);
        //           
        $line = $this->len();
        $file_len = filesize($this->_file_data);
        fseek($this->_f_data, $file_len);   

        ftruncate($this->_f_idx, 0);
        rewind($this->_f_idx);        
        fwrite($this->_f_idx, $file_len.",".$line);

        ftruncate($this->_f_idx_bak, 0);
        rewind($this->_f_idx_bak);
        fwrite($this->_f_idx_bak, $file_len.",".$line);

        flock($this->_f_idx, LOCK_UN);
    }

    /**
     * pos,         
     */
    public function pos()
    {
        flock($this->_f_idx, LOCK_EX);
        rewind($this->_f_idx);
        $data_idx = fgets($this->_f_idx, 1024);
        $data_idx = explode(",", $data_idx);
        $pos = (int) trim($data_idx[0]);
        $line = isset($data_idx[1]) ? (int) trim($data_idx[1]) : 0;
        flock($this->_f_idx, LOCK_UN);

        return array('pos' => $pos, 'line' => $line);

    }

    /**
     * len,        
     */
    public function len()
    {
        flock($this->_f_data, LOCK_EX);

        $old_pos = ftell($this->_f_data);
        rewind($this->_f_data);
        $line = 0;
        while (fgets($this->_f_data, 1024) !== FALSE) $line ++; 
        fseek($this->_f_data, $old_pos);

        flock($this->_f_data, LOCK_UN);

        return $line;
    }
  

    /**
     * pop,             
     *     
     * @param int $num,          
     * @param array $cur_pos,            、         
     * @return array | boolean,          ,     FALSE
     */
    public function pop($num = 1, & $cur_pos = array())
    {
        $num = $num < 1 ? 1 : $num;

        /**
         *       ,      
         */
        flock($this->_f_idx, LOCK_EX);
        rewind($this->_f_idx);
        $data_idx = fgets($this->_f_idx, 1024);
        $data_idx = explode(",", $data_idx);
        $pos = (int) trim($data_idx[0]);
        $line = isset($data_idx[1]) ? (int) trim($data_idx[1]) : 0;

        $data_all = array();
        for ($i = 0; $i < $num; $i ++) {
            /**
             *       ,      
             */
            fseek($this->_f_data, $pos);
            $data = fgets($this->_f_data, 8192);

            /**
             *              
             */
            if ($data !== FALSE) {
                $pos = ftell($this->_f_data);
                $line ++;

                rewind($this->_f_idx);
                ftruncate($this->_f_idx, 0);
                fwrite($this->_f_idx, "{$pos},{$line}");   

                rewind($this->_f_idx_bak);
                ftruncate($this->_f_idx_bak, 0);
                fwrite($this->_f_idx_bak, "{$pos},{$line}");         
            } else {
                break;
            }

            $data_all[$line] = $data;
        }


        flock($this->_f_idx, LOCK_UN);

        $cur_pos = array(
            'pos' => $pos,
            'line' => $line,
        );

        return $data_all ? $data_all : FALSE;
    }

    /**
     * push,         
     *
     * @param string | array $data,      ,        ,         
     * @return int,          
     */
    public function push($data)
    {
        if (! is_array($data)) {
            $data = array($data);
        }

        $count = 0;

        /**
         *       ,    
         */
        flock($this->_f_data, LOCK_EX);
        if (is_array($data)) {
            foreach ($data as $line) {
                fwrite($this->_f_data, $line."\r
"); $count ++; } } flock($this->_f_data, LOCK_UN); return $count; } /** * del, */ public function del() { $this->detach(); unlink($this->_file_data); unlink($this->_file_idx); unlink($this->_file_idx_bak); return TRUE; } }

 
        FIFO  ,           ,      。    QQ             。      14  ,  1  。


主な操作
キュー$fifo=Filefifo::instance('ファイルパス');
デキュー$data=$fifo->pop(‘デキューする行数、デフォルト1’);
エンキュー$fifo->push(‘エンキューするデータ’)
その他の操作
データファイル$fifo->attach('ファイルパス')を接続します.
現在のキューファイル$fifo->detach()を分離
キューヘッダ$fifo->rewind()に移動
キューの最後に$fifo->end()
現在の位置$fifo->pos()を取得します.
取得キューの合計長(ファイルの合計行数)$fifo->len()
キュー$fifo->del()を削除します.
 
demo:
<?php
$file = ‘qq.txt’;
$list = Filefifo::instance($file);
$start = microtime(TRUE);

// push
for ($i = 0; $i < 1000; $i ++) {
$list->push($i);
}
// pop
do {
$data = $list->pop();
} while ($data !== FALSE);

echo (microtime(TRUE) - $start ) * 1000;

?>