/** * Open metatable file * @param string * @param int defaults to self::READWRITE | self::STRINGS_GC * @return metatable|bool */ public static function open($filename, $flags = 6) { // check flags if (($flags & self::READONLY) === self::READONLY && ($flags & self::READWRITE) === self::READWRITE) { return FALSE; } // initialize $tries_left = self::LOCK_TRIES; $inode = @fileinode($filename); $handle = @fopen($filename, 'rb' . (($flags & self::READWRITE) === self::READWRITE ? '+' : '')); $locking = NULL; $tmp = NULL; $structure = array(); $handle_filename = NULL; $tmp_filename = NULL; // open metatable file if ($handle) { // file exists while (true) { // try to lock if (!flock($handle, ($flags & self::READONLY) === self::READONLY ? LOCK_SH : LOCK_EX)) { fclose($handle); return FALSE; } // check inode clearstatcache(); $current_inode = fileinode($filename); if ($current_inode === $inode) { break; } if ($tries_left <= 0) { fclose($handle); return FALSE; } // try to open again fclose($handle); $tries_left--; $inode = $current_inode; $handle = @fopen($filename, 'rb' . (($flags & self::READWRITE) === self::READWRITE ? '+' : '')); if (!$handle) { return FALSE; } } } else { // file does not exist if (($flags & self::READONLY) === self::READONLY) { return FALSE; } $handle = NULL; } // read structure $structure = array('signature' => self::MAGIC_STRING, 'version' => self::VERSION, 'frames' => array(), 'frames_indexes' => array()); $frames = array(); if ($handle) { $structure = self::structure_read($handle); } // open write and temporary file if (($flags & self::READWRITE) === self::READWRITE) { $locking = $handle; list($tmp_filename, $tmp) = self::tmp(); if (!$tmp_filename || !$tmp) { if ($locking) { fclose($locking); } return FALSE; } list($handle_filename, $handle) = array($filename . '~handle', @fopen($filename . '~handle', 'w+')); if (!$handle_filename || !$handle) { fclose($tmp); unlink($tmp_filename); if ($locking) { fclose($locking); } return FALSE; } // copy data to temporary if ($locking) { foreach ($structure['frames'] as $i => $frame) { if ($frame['name'] === self::FRAME_STRINGS) { $structure['frames'][$i]['used_at_start'] = $structure['frames'][$i]['used']; $structure['frames'][$i]['offset_at_start'] = $structure['frames'][$i]['offset']; } if (!(($flags & self::STRINGS_GC) === self::STRINGS_GC && $frame['name'] === self::FRAME_STRINGS)) { if (!(fseek($locking, $frame['offset'], SEEK_SET) !== -1 && fseek($handle, $frame['offset'], SEEK_SET) !== -1 && stream_copy_to_stream($locking, $handle, $frame['used']) === $frame['used'])) { fclose($locking); fclose($handle); unlink($handle_filename); fclose($tmp); unlink($tmp_filename); return FALSE; } } } } } // create instance $that = new self(); $that->flags = $flags; $that->filename = $filename; $that->handle_filename = $handle_filename; $that->tmp_filename = $tmp_filename; $that->locking = $locking; $that->handle = $handle; $that->tmp = $tmp; $that->structure = $structure; if (!isset($structure['frames_indexes'][self::FRAME_INDEXES])) { $that->frame_create(self::FRAME_INDEXES, 0); } if (!isset($structure['frames_indexes'][self::FRAME_STRINGS])) { $that->frame_create(self::FRAME_STRINGS, 1); } if (!isset($structure['frames_indexes'][self::FRAME_DATA])) { $that->frame_create(self::FRAME_DATA, 2); } // get indexes fseek($that->handle, $that->structure['frames'][$that->structure['frames_indexes'][self::FRAME_INDEXES]]['offset'], SEEK_SET); for ($i = 0, $N = $that->structure['frames'][$that->structure['frames_indexes'][self::FRAME_INDEXES]]['used'] / self::SIZEOF_INDEX_RECORD; $i < $N; $i++) { $data = fread($that->handle, self::SIZEOF_INDEX_RECORD); if (strlen($data) < self::SIZEOF_INDEX_RECORD) { continue; } $data = unpack(self::INDEX_RECORD_UNPACK, $data); $that->indexes[$data['start']] = array($data['lower'], $data['upper']); } // return return $that; }