-backup-.~" */ class Loco_fs_Revisions implements Countable/*, IteratorAggregate*/ { /** * @var loco_fs_File */ private $master; /** * Sortable list of backed up file paths (not including master) * @var array */ private $paths; /** * Cached count of backups + 1 * @var int */ private $length; /** * Paths to delete when object removed from memory * @var array */ private $trash = array(); /** * Construct from master file (current version) */ public function __construct( Loco_fs_File $file ){ $this->master = $file; } /** * @internal * Executes deferred deletions with silent errors */ public function __destruct(){ if( $trash = $this->trash ){ $writer = $this->master->getWriteContext(); foreach( $trash as $file ){ if( $file->exists() ){ try { $writer->setFile($file); $writer->delete(false); } catch( Loco_error_WriteException $e ){ // avoiding fatals as pruning is non-critical operation } } } } } /** * Check that file permissions allow a new backup to be created * @return bool */ public function writable(){ return $this->master->getParent()->writable(); } /** * Create a new backup of current version * @return Loco_fs_File */ public function create(){ $vers = 0; $date = date('YmdHis'); $ext = $this->master->extension(); $base = $this->master->dirname().'/'.$this->master->filename(); do { $path = sprintf( '%s-backup-%s%u.%s~', $base, $date, $vers++, $ext); } while ( file_exists($path) ); $copy = $this->master->copy( $path ); // invalidate cache so next access reads disk $this->paths = null; $this->length = null; return $copy; } /** * Delete oldest backups until we have maximuim of $num_backups remaining * @return Loco_fs_Revisions */ public function prune( $num_backups ){ $paths = $this->getPaths(); if( isset($paths[$num_backups]) ){ foreach( array_slice( $paths, $num_backups ) as $path ){ $this->unlinkLater($path); } $this->paths = array_slice( $paths, 0, $num_backups ); $this->length = null; } return $this; } /** * @return array */ public function getPaths(){ if( is_null($this->paths) ){ // build regex for matching backed up revisions of master $regex = preg_quote( $this->master->filename(), '/' ).'-backup-(\\d{14,})?'; if( $ext = $this->master->extension() ){ $regex .= preg_quote('.'.$ext,'/'); } $regex = '/'.$regex.'~/'; // $this->paths = array(); $finder = new Loco_fs_FileFinder( $this->master->dirname() ); /** @var $file Loco_fs_File */ foreach( $finder as $file ){ if( preg_match( $regex, $file->basename(), $r ) ){ $this->paths[] = $file->getPath(); } } // time sort order descending rsort( $this->paths ); } return $this->paths; } /** * Get number of backups plus master * @return int */ public function count(){ if( ! $this->length ){ $this->length = 1 + count( $this->getPaths() ); } return $this->length; } /** * Delete file when object removed from memory. * Previously unlinked on shutdown, but doesn't work with WordPress file system abstraction * @return void */ public function unlinkLater($path){ $this->trash[] = new Loco_fs_File($path); } }