File manager - Edit - /home/theblueo/tv/fb4e3b/fs.tar
Back
Link.php 0000666 00000000671 15214036525 0006165 0 ustar 00 <?php /** * */ class Loco_fs_Link extends Loco_fs_File { /** * @return Loco_fs_File */ public function resolve(){ $path = $this->getPath(); if( $real = realpath($path) ){ if( is_dir($path) ){ return new Loco_fs_Directory($real); } return new Loco_fs_File($real); } // else fail return null; } } Revisions.php 0000666 00000010234 15214036525 0007245 0 ustar 00 <?php /** * Manages revisions (backups) of a file. * Revision file names have form "<filename>-backup-<date>.<ext>~" */ 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); } } FileList.php 0000666 00000004772 15214036525 0007011 0 ustar 00 <?php /** * Simple list of file paths */ class Loco_fs_FileList extends ArrayIterator implements Loco_fs_FileListInterface { /** * Hash map for ensuring files only added once * @var array */ private $unique = array(); /** * Override to ensure original list is indexed */ public function __construct( $a = array() ){ parent::__construct( array() ); foreach( $a as $file ){ $this->add( $file ); } } /** * Like getArrayCopy, but exports string paths * @return array */ public function export(){ $a = array(); foreach( $this as $file ){ $a[] = (string) $file; } return $a; } /** * @internal */ public function __toString(){ return implode( "\n", $this->getArrayCopy() ); } /** * Generate a unique key for file. */ private function hash( Loco_fs_File $file ){ $path = $file->normalize(); // if file is real, we must resolve its real path if( $file->exists() && ( $real = realpath($path) ) ){ $path = $real; } return $path; } /** * @codeCoverageIgnore */ public function offsetSet( $index, $value ){ throw new Exception('Use Loco_fs_FileList::add'); } /** * Add a file uniquely collection * @return Loco_fs_FileList */ public function add( Loco_fs_File $file ){ $hash = $this->hash( $file ); if( ! isset($this->unique[$hash]) ){ $this->unique[$hash] = true; parent::offsetSet( null, $file ); } return $this; } /** * Check if given file is already in list * @return bool */ public function has( Loco_fs_File $file ){ $hash = $this->hash( $file ); return isset($this->unique[$hash]); } /** * Get a copy of list with only files not contained in passed list * @return Loco_fs_FileList */ public function diff( Loco_fs_FileList $not_in ){ $list = new Loco_fs_FileList; foreach( $this as $file ){ $not_in->has($file) || $list->add( $file ); } return $list; } /** * Merge another list of the SAME TYPE uniquely on top of current one * @return Loco_fs_FileList */ public function augment( loco_fs_FileList $list ){ foreach( $list as $file ){ $this->add( $file ); } return $this; } } FileMode.php 0000666 00000003223 15214036525 0006750 0 ustar 00 <?php /** * Object representing a file's permission bits */ class Loco_fs_FileMode { /** * inode protection mode * @var int */ private $i; /** * Instantiate from integer file mode */ public function __construct( $mode ){ $this->i = (int) $mode; } /** * @return string */ public function __toString(){ return sprintf('%03o', $this->i & 07777 ); } /** * rwx style friendly formatting * @return string */ public function format(){ $mode = $this->i; $setuid = $mode & 04000; $setgid = $mode & 02000; $sticky = $mode & 01000; return $this->type(). ( $mode & 0400 ? 'r' : '-' ). ( $mode & 0200 ? 'w' : '-' ). ( $mode & 0100 ? ($setuid?'s':'x') : ($setuid?'S':'-') ). ( $mode & 0040 ? 'r' : '-' ). ( $mode & 0020 ? 'w' : '-' ). ( $mode & 0010 ? ($setgid?'s':'x') : ($setgid?'S':'-') ). ( $mode & 0004 ? 'r' : '-' ). ( $mode & 0002 ? 'w' : '-' ). ( $mode & 0001 ? ($sticky?'t':'x') : ($sticky?'T':'-') ); } /** * File type bit field: * http://man7.org/linux/man-pages/man2/stat.2.html */ public function type(){ $mode = $this->i & 0170000; switch( $mode ){ case 0010000: return '-'; case 0040000: return 'd'; case 0120000: return 'l'; case 0140000: return 's'; case 0060000: return 'c'; default: return '-'; } } } LocaleFile.php 0000666 00000005450 15214036525 0007267 0 ustar 00 <?php /** * A file with metadata about the locale it relates to */ class Loco_fs_LocaleFile extends Loco_fs_File { /** * @var Loco_Locale */ private $locale; /** * @var string */ private $suffix; /** * @var string */ private $prefix; /** * Lazy handling of localized path info * @return array [ prefix, suffix ] */ public function split(){ if( is_null($this->suffix) ){ $parts = explode( '-', $this->filename() ); $this->suffix = array_pop( $parts ); $this->prefix = implode( '-', $parts ); // handle situations where suffixless name is wrongly taken as the prefix // e.g. "de.po" is valid but "hello.po" is not. // There are still some ambigous situations, e.g. "foo-bar.po" is valid, but nonsense if( ! $this->prefix && ! $this->getLocale()->isValid() ){ $this->prefix = $this->suffix; $this->suffix = ''; $this->locale = null; } } return array( $this->prefix, $this->suffix ); } /** * @var Loco_Locale */ public function getLocale(){ if( ! $this->locale ){ if( $tag = $this->getSuffix() ){ $this->locale = Loco_Locale::parse($tag); } else { $this->locale = new Loco_Locale(''); } } return $this->locale; } /** * @return Loco_fs_LocaleFile */ public function cloneLocale( Loco_locale $locale ){ $this->split(); $path = (string) $locale; if( $str = $this->prefix ){ $path = $str.'-'.$path; } if( $str = $this->extension() ){ $path .= '.'.$str; } if( $dir = $this->getParent() ){ $path = $dir->getPath().'/'.$path; } return new Loco_fs_LocaleFile($path); } /** * Get prefix (or stem) from name that comes before locale suffix * @return string */ public function getPrefix(){ $info = $this->split(); return $info[0]; } /** * Get suffix (or locale code) from name that comes after "-" separator * @return string */ public function getSuffix(){ $info = $this->split(); return $info[1]; } /** * Test if file is suffix only, e.g. "en_US.po" * @return bool */ public function hasSuffixOnly(){ $info = $this->split(); return $info[1] && ! $info[0]; } /** * Test if file is prefix only, e.g. "incorrect.po" * @return bool */ public function hasPrefixOnly(){ $info = $this->split(); return $info[0] && ! $info[1]; } } LocaleFileList.php 0000666 00000003173 15214036525 0010123 0 ustar 00 <?php /** * File list indexed by locale codes */ class Loco_fs_LocaleFileList extends Loco_fs_FileList { /** * Look up locale entries by their tag * @var array */ private $index = array(); /** * @return Loco_fs_LocaleFileList */ public function addLocalized( Loco_fs_LocaleFile $file ){ $i = count($this); $this->add( $file ); if( count($this) !== $i ){ if( $key = $file->getSuffix() ){ $this->index[$key][] = $i; } } return $this; } /** * Get a new list containing just files for a given locale (exactly) * @return Loco_fs_LocaleFileList */ public function filter( $tag ){ $list = new Loco_fs_LocaleFileList; if( isset($this->index[$tag]) ){ foreach( $this->index[$tag] as $i ){ $list->addLocalized( $this[$i] ); } } return $list; } /** * Get a unique list of valid locales for which there are files * @return array<Loco_Locale> */ public function getLocales(){ $list = array(); foreach( array_keys($this->index) as $tag ){ $locale = Loco_Locale::parse($tag); if( $locale->isValid() ){ $list[$tag] = $locale; } } return $list; } /** * {@inheritdoc} * @return Loco_fs_LocaleFileList */ public function augment( Loco_fs_FileList $list ){ foreach( $list as $file ){ $this->addLocalized( $file ); } return $this; } } FileFinder.php 0000666 00000025572 15214036525 0007306 0 ustar 00 <?php /** * Lazy file iterator. Pulls directory listings when required. */ class Loco_fs_FileFinder implements Iterator, Countable, Loco_fs_FileListInterface { /** * Top-level search directories * @var Loco_fs_FileList */ private $roots; /** * All directories to search, including those recursed into * @var Loco_fs_FileList */ private $subdir; /** * whether directories all read into memory * @var Loco_fs_FileList */ private $cached; /** * File listing already matched * @var Loco_fs_FileList */ private $cache; /** * internal array pointer for whole list of paths * @var int */ private $i; /** * internal pointer for directory being read * @var int */ private $d; /** * current directory being read * @var resource */ private $dir; /** * Path of current directory being read * @var string */ private $cwd; /** * Whether directories added to search will be recursive by default * @var bool */ private $recursive = false; /** * Whether currently recursing into subdirectories * This is switched on and off as each directories is opened * @var bool */ private $recursing; /** * Whether to follow symlinks when recursing into subdirectories * Root-level symlinks are always resolved when possible * @var bool */ private $symlinks = true; /** * List of file extensions to filter on and group by * @var array */ private $exts; /** * List of directory names to exclude from recursion * @var array */ private $excluded; /** * Create initial list of directories to search */ public function __construct( $root = '' ){ $this->roots = new Loco_fs_FileList; $this->excluded = array(); if( $root ){ $this->addRoot( $root ); } } /** * Set recursive state of all defined roots * @return Loco_fs_FileFinder */ public function setRecursive( $bool ){ $this->invalidate(); $this->recursive = $bool; /* @var $dir Loco_fs_Directory */ foreach( $this->roots as $dir ){ $dir->setRecursive( $bool ); } return $this; } /** * @return Loco_fs_FileFinder */ public function followLinks( $bool ){ $this->invalidate(); $this->symlinks = (bool) $bool; return $this; } /** * */ private function invalidate(){ $this->cached = false; $this->cache = null; $this->subdir = null; } /** * @return Loco_fs_FileList */ public function export(){ if( ! $this->cached ){ $this->rewind(); while( $this->valid() ){ $this->next(); } } return $this->cache; } /** * @return array */ public function exportGroups(){ $this->cached || $this->export(); return $this->exts; } /** * Add a directory root to search. * @return Loco_fs_FileFinder */ public function addRoot( $root, $recursive = null ){ $this->invalidate(); $dir = new Loco_fs_Directory($root); $this->roots->add( $dir ); // new directory inherits current global setting unless set explicitly $dir->setRecursive( is_bool($recursive) ? $recursive : $this->recursive ); return $this; } /** * Get all root directories to be searched * @return Loco_fs_FileList */ public function getRootDirectories(){ return $this->roots; } /** * Group results by file extension * @return Loco_fs_FileFinder */ public function group(){ return $this->groupBy( func_get_args() ); } /** * Group results by file extensions given in array * @return Loco_fs_FileFinder */ public function groupBy( array $exts ){ $this->invalidate(); $this->exts = array(); foreach( $exts as $ext ){ $this->exts[ trim($ext,'*.') ] = new Loco_fs_FileList; } return $this; } /** * Add one or more paths to exclude from listing * @param string e.g "node_modules" * @return Loco_fs_FileFinder */ public function exclude(){ $this->invalidate(); foreach( func_get_args() as $path ){ $file = new Loco_fs_File($path); // if path is absolute, add straight onto list if( $file->isAbsolute() ){ $file->normalize(); $this->excluded[] = $file; } // else append to all defined roots else { foreach( $this->roots as $dir ) { $file = new Loco_fs_File( $dir.'/'.$path ); $file->normalize(); $this->excluded[] = $file; } } } return $this; } /** * Export excluded paths as file objects * @return array<Loco_fs_File> */ public function getExcluded(){ return $this->excluded; } /** * @return resource */ private function open( Loco_fs_Directory $dir ){ $path = $dir->getPath(); $recursive = $dir->isRecursive(); if( is_link($path) ){ $link = new Loco_fs_Link($path); if( $dir = $link->resolve() ){ $dir->setRecursive( $recursive ); return $this->open( $dir ); } }// @codeCoverageIgnore /*if( ! is_dir($path) ){ throw new InvalidArgumentException('Path is not a readable directory, '.$path ); }*/ $this->cwd = $path; $this->recursing = $recursive; return $this->dir = opendir( $path ); } private function close(){ closedir( $this->dir ); $this->dir = null; $this->recursing = null; } /** * Test if given path is matched by one of our exclude rules * TODO would prefer a method that didn't require iteration * @param string * @return bool */ public function isExcluded( $path ){ /* @var $excl Loco_fs_File */ foreach( $this->excluded as $excl ){ if( $excl->equal($path) ){ return true; } } return false; } /** * Read next valid file path from root directories * @return Loco_fs_File */ private function read(){ $path = null; if( is_resource($this->dir) ){ while( $f = readdir($this->dir) ){ if( '.' === $f{0} ){ continue; } $path = $this->cwd.'/'.$f; // follow symlinks (subdir hash ensures against loops) if( is_link($path) ){ if( ! $this->symlinks ){ continue; } $link = new Loco_fs_Link($path); if( $file = $link->resolve() ){ $path = $file->getPath(); } else { continue; } } // add subdirectory to recursion list // this will result in breadth-first listing if( is_dir($path) ){ if( $this->recursing && ! $this->isExcluded($path) ){ $subdir = new Loco_fs_Directory($path); $subdir->setRecursive(true); $this->subdir->add( $subdir ); } continue; } else if( $this->isExcluded($path) ){ continue; } // file represented as object containing original path $file = new Loco_fs_File( $path ); $this->add( $file ); $this->i++; return $file; } $this->close(); } // try next dir if nothing matched in this one $d = $this->d + 1; if( isset($this->subdir[$d]) ){ $this->d = $d; $this->open( $this->subdir[$d] ); return $this->read(); } // else at end of all available files $this->cached = true; } /** * Implement FileListInterface::add */ public function add( Loco_fs_File $file ){ if( $this->exts ){ $ext = $file->extension(); if( ! isset($this->exts[$ext]) ){ return; } $this->exts[$ext]->add( $file ); } $this->cache->add( $file ); } /** * @return int */ public function count(){ return count( $this->export() ); } /** * @return Loco_fs_File|null */ public function current(){ $i = $this->i; if( is_int($i) && isset($this->cache[$i]) ){ return $this->cache[$i]; } } /** * @return Loco_fs_File|null */ public function next(){ if( $this->cached ){ $i = $this->i + 1; if( isset($this->cache[$i]) ){ $this->i = $i; return $this->cache[$i]; } } else if( $path = $this->read() ){ return $path; } // else at end of all directory listings $this->i = null; return null; } public function key(){ return $this->i; } public function valid(){ // may be in lazy state after rewind // must do initial read now in case list is empty return is_int($this->i); } /** * @return void */ public function rewind(){ if( $this->cached ){ reset( $this->cache ); $this->i = key($this->cache); } else { $this->d = 0; $this->dir = null; $this->cache = new Loco_fs_FileList; // add only root directories that exist $this->subdir = new Loco_fs_FileList; /* @var Loco_fs_Directory */ foreach( $this->roots as $root ){ if( $root->exists() && ! $this->isExcluded( $root->getPath() ) ){ $this->subdir->add( $root ); } } if( $root = reset($this->subdir) ){ $this->i = -1; $this->open( $root ); $this->next(); } else { $this->i = null; $this->subdir = null; $this->cached = true; } } } /** * test whether internal list has been fully cached in memory */ public function isCached(){ return $this->cached; } } File.php 0000666 00000031415 15214036525 0006147 0 ustar 00 <?php /** * */ class Loco_fs_File { /** * @var Loco_fs_FileWriter */ private $w; /** * Full original path to file * @var string */ private $path; /** * Cached pathinfo() data * @var array */ private $info; /** * Base path which path has been normalized against * @var string */ private $base; /** * Flag set when current path is relative * @var bool */ private $rel; /** * Check if a path is absolute and return fixed slashes for readability * @return string fixed path, or "" if not absolute */ public static function abs( $path ){ if( $path = (string) $path ){ $chr1 = $path{0}; // return unmodified path if starts "/" if( '/' === $chr1 ){ return $path; } // Windows drive path if "X:" or network path if "\\" if( isset($path{1}) ){ $chr2 = $path{1}; if( ':' === $chr2 || ( '\\' === $chr1 && '\\' === $chr2 ) ){ return strtoupper($chr1).$chr2.strtr( substr($path,2), '\\', '/' ); } } } // else path is relative, so return falsey string return ''; } /** * @internal */ public function __construct( $path ){ $this->setPath( $path ); } /** * Internally set path value and flag whether relative or absolute */ private function setPath( $path ){ $path = (string) $path; if( $fixed = self::abs($path) ){ $path = $fixed; $this->rel = false; } else { $this->rel = true; } if( $path !== $this->path ){ $this->path = $path; $this->info = null; } return $path; } /** * @return array */ public function isAbsolute(){ return ! $this->rel; } /** * @internal */ public function __clone(){ $this->cloneWriteContext( $this->w ); } /** * Copy write context with oursel * @return */ private function cloneWriteContext( Loco_fs_FileWriter $context = null ){ if( $context ){ $context = clone $context; $this->w = $context->setFile($this); } return $this; } /** * Get file system context for operations that *modify* the file system. * Read operations and operations that stat the file will always do so directly. * @return Loco_fs_FileWriter */ public function getWriteContext(){ if( ! $this->w ){ $this->w = new Loco_fs_FileWriter( $this ); } return $this->w; } /** * @internal */ private function pathinfo(){ return is_array($this->info) ? $this->info : ( $this->info = pathinfo($this->path) ); } /** * @return bool */ public function exists(){ return file_exists( $this->path ); } /** * @return bool */ public function writable(){ return $this->getWriteContext()->writable(); } /** * @return bool */ public function deletable(){ $parent = $this->getParent(); if( $parent && $parent->writable() ){ // sticky directory requires that either the file its parent is owned by effective user if( $parent->mode() & 01000 ){ $writer = $this->getWriteContext(); if( $writer->isDirect() && ( $uid = Loco_compat_PosixExtension::getuid() ) ){ return $uid === $this->uid() || $uid === $parent->uid(); } // else delete operation won't be done directly, so can't pre-empt sticky problems // TODO is it worth comparing FTP username etc.. for ownership? } // defaulting to "deletable" based on fact that parent is writable. return true; } return false; } /** * Get owner uid * @return int */ public function uid(){ return fileowner($this->path); } /** * Get group gid * @return int */ public function gid(){ return filegroup($this->path); } /** * Check if file can't be overwitten when existant, nor created when non-existant * This does not check permissions recursively as directory trees are not built implicitly * @return bool */ public function locked(){ if( $this->exists() ){ return ! $this->writable(); } if( $dir = $this->getParent() ){ return ! $dir->writable(); } return true; } /** * Check if full path can be built to non-existant file. * @return bool */ public function creatable(){ $file = $this; while( $file = $file->getParent() ){ if( $file->exists() ){ return $file->writable(); } } return false; } /** * @return string */ public function dirname(){ $info = $this->pathinfo(); return $info['dirname']; } /** * @return string */ public function basename(){ $info = $this->pathinfo(); return $info['basename']; } /** * @return string */ public function filename(){ $info = $this->pathinfo(); return $info['filename']; } /** * @return string */ public function extension(){ $info = $this->pathinfo(); return isset($info['extension']) ? $info['extension'] : ''; } /** * @return string */ public function getPath(){ return $this->path; } /** * @return int */ public function modified(){ return filemtime( $this->path ); } /** * @return int */ public function size(){ return filesize( $this->path ); } /** * @return int */ public function mode(){ if( is_link($this->path) ){ $stat = lstat( $this->path ); $mode = $stat[2]; } else { $mode = fileperms($this->path); } return $mode; } /** * Set file mode * @return Loco_fs_File */ public function chmod( $mode, $recursive = false ){ $this->getWriteContext()->chmod( $mode, $recursive ); return $this->clearStat(); } /** * Clear stat cache if any file data has changed * @return Loco_fs_File */ public function clearStat(){ $this->info = null; // PHP 5.3.0 Added optional clear_realpath_cache and filename parameters. if( version_compare( PHP_VERSION, '5.3.0', '>=' ) ){ clearstatcache( true, $this->path ); } // else no choice but to drop entire stat cache else { clearstatcache(); } return $this; } /** * @return string */ public function __toString(){ return $this->getPath(); } /** * Check if passed path is equal to ours * @param string * @return bool */ public function equal( $path ){ return $this->path === (string) $path; } /** * Normalize path for string comparison, resolves redundant dots and slashes. * @param string path to prefix * @return string */ public function normalize( $base = '' ){ if( $path = self::abs($base) ){ $base = $path; } if( $base !== $this->base ){ $path = $this->path; if( '' === $path ){ $this->setPath($base); } else { if( ! $this->rel || ! $base ){ $b = array(); } else { $b = self::explode( $base, array() ); } $b = self::explode( $path, $b ); $this->setPath( implode('/',$b) ); } $this->base = $base; } return $this->path; } /** * */ private static function explode( $path, array $b ){ $a = explode( '/', $path ); foreach( $a as $i => $s ){ if( '' === $s ){ if( 0 !== $i ){ continue; } } if( '.' === $s ){ continue; } if( '..' === $s ){ if( array_pop($b) ){ continue; } } $b[] = $s; } return $b; } /** * Get path relative to given location, unless path is already relative * @return string */ public function getRelativePath( $base ){ $path = $this->normalize(); if( $abspath = self::abs($path) ){ // base may needs require normalizing $file = new Loco_fs_File($base); $base = $file->normalize(); $length = strlen($base); // if we are below given base path, return ./relative if( substr($path,0,$length) === $base ){ ++$length; if( isset($path{$length}) ){ return substr( $path, $length ); } // else paths were idenitcal return ''; } // else attempt to find nearest common root $i = 0; $source = explode('/',$base); $target = explode('/',$path); while( isset($source[$i]) && isset($target[$i]) && $source[$i] === $target[$i] ){ $i++; } if( $i > 1 ){ $depth = count($source) - $i; $build = array_merge( array_fill( 0, $depth, '..' ), array_slice( $target, $i ) ); $path = implode( '/', $build ); } } // else return unmodified return $path; } /** * @return bool */ public function isDirectory(){ if( file_exists($this->path) ){ return is_dir($this->path); } return ! $this->extension(); } /** * Load contents of file into a string * @return string */ public function getContents(){ return file_get_contents( $this->path ); } /** * Check if path is under a theme directory * @return bool */ public function underThemeDirectory(){ return Loco_fs_Locations::getThemes()->check( $this->path ); } /** * Check if path is under a plugin directory * @return bool */ public function underPluginDirectory(){ return Loco_fs_Locations::getPlugins()->check( $this->path ); } /** * Check if path is under a global system directory * @return bool */ public function underGlobalDirectory(){ return Loco_fs_Locations::getGlobal()->check( $this->path ); } /** * @return Loco_fs_Directory */ public function getParent(){ $path = $this->dirname(); if( '.' !== $path && $this->path !== $path ){ $dir = new Loco_fs_Directory( $path ); $dir->cloneWriteContext( $this->w ); return $dir; } } /** * Copy this file for real * @throws Loco_error_Exception * @return Loco_fs_File new file */ public function copy( $dest ){ $copy = clone $this; $copy->path = $dest; $copy->clearStat(); $this->getWriteContext()->copy( $copy ); return $copy; } /** * Delete this file for real * @throws Loco_error_Exception * @return Loco_fs_File */ public function unlink(){ $recursive = $this->isDirectory(); $this->getWriteContext()->delete( $recursive ); return $this->clearStat(); } /** * Copy this object with an alternative file extension * @return Loco_fs_File */ public function cloneExtension( $ext ){ $snip = strlen( $this->extension() ); $file = clone $this; if( $snip ){ $file->path = substr_replace( $this->path, $ext, - $snip ); } else { $file->path .= '.'.$ext; } $file->info = null; return $file; } /** * Ensure full parent directory tree exists * @return Loco_fs_Directory */ public function createParent(){ if( $dir = $this->getParent() ){ if( ! $dir->exists() ){ $dir->mkdir(); } } return $dir; } /** * @return int bytes written to file */ public function putContents( $data ){ $this->getWriteContext()->putContents($data); $this->clearStat(); return $this->size(); } } Directory.php 0000666 00000001762 15214036525 0007236 0 ustar 00 <?php /** * */ class Loco_fs_Directory extends Loco_fs_File { /** * Recursive flag for internal use * @var bool */ private $r = false; /** * @override */ public function isDirectory(){ return true; } /** * Set recursive flag for use when traversing directory trees * @return Loco_fs_Directory */ public function setRecursive( $bool ){ $this->r = (bool) $bool; return $this; } /** * @return bool */ public function isRecursive(){ if( func_num_args() ){ throw new InvalidArgumentException('Did you mean to use setRecursive?'); } return $this->r; } /** * Create this directory for real. * * @throws Loco_error_WriteException * @return Loco_fs_Directory */ public function mkdir(){ if( ! $this->exists() ){ $this->getWriteContext()->mkdir(); } return $this; } } DummyFile.php 0000666 00000005512 15214036525 0007162 0 ustar 00 <?php /** * Dummy file that just holds content in memory. * Use when you don't want to commit data to disk but you need to pass a typed file object */ class Loco_fs_DummyFile extends Loco_fs_File { /** * @var string */ private $contents = ''; /** * @var int */ private $mtime = 0; /** * @var int */ private $fmode = 0644; /** * @var int */ private $uid = 0; /** * @var int */ private $gid = 0; public function __construct($path){ parent::__construct($path); $this->mtime = time(); } /** * {@inheritdoc} */ public function exists(){ return false; } /** * {@inheritdoc} */ public function getContents(){ return $this->contents; } /** * {@inheritdoc} */ public function size(){ return strlen($this->contents); } /** * {@inheritdoc} */ public function putContents( $contents ){ $this->contents = (string) $contents; return $this; } /** * {@inheritdoc} */ public function modified(){ return $this->mtime; } /** * Allow forcing of modified stamp for testing purposes * @return Loco_fs_File */ public function touch( $modified ){ $this->mtime = (int) $modified; return $this; } /** * {@inheritdoc} */ public function mode(){ return $this->fmode; } /** * {@inheritdoc} */ public function chmod( $mode, $recursive = false ){ $this->fmode = (int) $mode; return $this; } /** * TODO implement in parent */ public function chown( $uid = null, $gid = null ){ if( is_int($uid) ){ $this->uid = $uid; } if( is_int($gid) ){ $this->gid = $gid; } return $this; } /** * {@inheritdoc} */ public function copy( $dest ){ $copy = clone $this; $copy->path = $dest; return $copy; } /** * {@inheritdoc} */ public function uid(){ return $this->uid; } /** * {@inheritdoc} */ public function gid(){ return $this->gid; } /** * {@inheritdoc} * @codeCoverageIgnore */ public function writable(){ $mode = $this->mode(); // world writable if( $mode & 02 ){ return true; } // group writable if( ( $mode & 020 ) && $this->gid() === Loco_compat_PosixExtension::getgid() ){ return true; } // owner writable if( ( $mode & 0200 ) && $this->uid() === Loco_compat_PosixExtension::getuid() ){ return true; } // else locked: return false; } } LocaleDirectory.php 0000666 00000005547 15214036525 0010363 0 ustar 00 <?php /** * */ class Loco_fs_LocaleDirectory extends Loco_fs_Directory { /** * Get location identifier which signifies the type if translation storage. * * - "plugin": bundled inside a plugin (official/author) * - "theme": bundled inside a theme (official/author) * - "wplang": under the global languages directory and probably installed by auto-updates * - "custom": Loco protected directory * - "other": anywhere else * * @return string */ public function getTypeId(){ // paths must be compared with trailing slashes so "/foo" doesn't match "/foo-bar" $path = trailingslashit( $this->normalize() ); // anything under Loco's protected directory is our location for custom overrides $prefix = trailingslashit( loco_constant('LOCO_LANG_DIR') ); if( substr($path,0,strlen($prefix) ) === $prefix ){ return 'custom'; } // standard subdirectories of WP_LANG_DIR are under WordPress auto-update control $prefix = trailingslashit( loco_constant('WP_LANG_DIR') ); if( substr($path,0,strlen($prefix) ) === $prefix ){ if( $path === $prefix || $path === $prefix.'plugins/' || $path === $prefix.'themes/' ){ return 'wplang'; } } else { // anything under a registered theme directory is bundled $dirs = Loco_fs_Locations::getThemes(); if( $dirs->check($path) ){ return 'theme'; } // anything under a registered plugin directory is bundled $dirs = Loco_fs_Locations::getPlugins(); if( $dirs->check($path) ){ return 'plugin'; } } // anything else, which includes subdirectories of WP_LANG_DIR etc.. return 'other'; } /** * Get translated version of getTypeId * @return string */ public function getTypeLabel( $id ){ switch( $id ){ case 'theme': case 'plugin': // Translators: Refers to bundled plugin or theme translation files - i.e. those supplied by the author return _x('Author','File location','loco'); case 'wplang': // Translators: Refers to system-installed translation files - i.e. those under WP_LANG_DIR return _x('System','File location','loco'); case 'custom': // Translators: Refers to translation files in Loco's custom/protected directory return _x('Custom','File location','loco'); case 'other': // Translators: Refers to translation files in an alternative location that isn't Author, System or Custom. return _x('Other','File location','loco'); } throw new InvalidArgumentException('Invalid location type: '.$id ); } } FileWriter.php 0000666 00000017333 15214036525 0007347 0 ustar 00 <?php /** * Provides write operation context via the WordPress file system API */ class Loco_fs_FileWriter { /** * @var Loco_fs_File */ private $file; /** * @var WP_Filesystem_Direct */ private $fs; public function __construct( Loco_fs_File $file ){ $this->file = $file; $this->connect( new WP_Filesystem_Direct(null) ); } /** * @return Loco_fs_FileWriter */ public function setFile( Loco_fs_File $file ){ $this->file = $file; return $this; } /** * Connect to alternative file system context * @return Loco_fs_FileWriter * @throws Loco_error_WriteException */ public function connect( WP_Filesystem_Base $fs, $disconnected = true ){ if( $disconnected && ! $fs->connect() ){ $errors = $fs->errors; if( is_wp_error($errors) ){ foreach( $errors->get_error_messages() as $reason ){ Loco_error_AdminNotices::warn($reason); } } throw new Loco_error_WriteException( __('Failed to connect to remote server','loco') ); } $this->fs = $fs; return $this; } /** * Get mapped path for use in indirect file system manipulation * @return string */ public function getPath(){ return $this->mapPath( $this->file->getPath() ); } /** * @internal */ private function mapPath( $path ){ /*/ restrict file extensions to Gettext files for additional layer of security // disabled until configurable, too annoying when using safely (e.g. zip files) if( ( $ext = $this->file->extension() ) && ! preg_match('/^(po|mo|pot)~?$/',$ext) ){ throw new Loco_error_WriteException('Unwriteable file extension: *.'.$ext.' disallowed'); }*/ // sanitize writeable locations $remote = ! $this->isDirect(); $base = rtrim( loco_constant('WP_CONTENT_DIR'), '/' ); $snip = strlen($base); if( substr( $path, 0, $snip ) !== $base ){ if( $remote ){ throw new Loco_error_WriteException('Remote path must be under WP_CONTENT_DIR'); } /*/ Allowing direct file system access, because symlinks and also get_temp_dir() else { throw new Loco_error_WriteException('Direct path must be under WP_CONTENT_DIR'); }*/ } // map virtual path for remote file system if( $remote ){ $virt = $this->fs->wp_content_dir(); if( false === $virt ){ throw new Loco_error_WriteException('Failed to find WP_CONTENT_DIR via remote connection'); } $virt = rtrim( $virt, '/' ); $path = substr_replace( $path, $virt, 0, $snip ); } return $path; } /** * Test if a direct (not remote) file system * @return bool */ public function isDirect(){ return $this->fs instanceof WP_Filesystem_Direct; } /** * @return bool */ public function writable(){ return ! $this->disabled() && $this->fs->is_writable( $this->getPath() ); } /** * @return Loco_fs_FileWriter * @throws Loco_error_WriteException */ public function chmod( $mode, $recursive = false ){ $this->authorize(); if( ! $this->fs->chmod( $this->getPath(), $mode, $recursive ) ){ throw new Loco_error_WriteException( sprintf( __('Failed to chmod %s','loco'), $this->file->basename() ) ); } return $this; } /** * @return Loco_fs_FileWriter * @throws Loco_error_WriteException */ public function copy( Loco_fs_File $copy ){ $this->authorize(); $source = $this->getPath(); $target = $this->mapPath( $copy->getPath() ); // bugs in WP file system "exists" methods mean forcing $overwrite, but checking reliably first if( $copy->exists() ){ throw new Loco_error_WriteException( __('Refusing to copy over an existing file','loco') ); } if( ! $this->fs->copy( $source, $target, true ) ){ throw new Loco_error_WriteException( sprintf( __('Failed to copy %s to %s','loco'), basename($source), basename($target) ) ); } return $this; } /** * @return Loco_fs_FileWriter * @throws Loco_error_WriteException */ public function delete( $recursive = false ){ $this->authorize(); if( ! $this->fs->delete( $this->getPath(), $recursive ) ){ throw new Loco_error_WriteException( sprintf( __('Failed to delete %s','loco'), $this->file->basename() ) ); } return $this; } /** * @return Loco_fs_FileWriter * @throws Loco_error_WriteException */ public function putContents( $data ){ $this->authorize(); $file = $this->file; if( $file->isDirectory() ){ throw new Loco_error_WriteException( sprintf( __('"%s" is a directory, not a file','loco'), $file->basename() ) ); } // avoid chmod of existing file if( $file->exists() ){ $mode = $file->mode(); } // may have bypassed definition of FS_CHMOD_FILE else { $mode = defined('FS_CHMOD_FILE') ? FS_CHMOD_FILE : 0644; } $path = $this->getPath(); if( ! $this->fs->put_contents( $path, $data, $mode ) ){ // provide useful reason for failure if possible if( $file->exists() && ! $this->fs->is_writable($path) ){ throw new Loco_error_WriteException( __("Permission denied to update file",'loco') ); } // else check directory exists in which to create a new file else if( ( $dir = $file->getParent() ) && ! $dir->exists() ){ throw new Loco_error_WriteException( __("Parent directory doesn't exist",'loco') ); } // else reason for failure is not established throw new Loco_error_WriteException( __('Failed to save file','loco') ); } return $this; } /** * @return Loco_fs_FileWriter * @throws Loco_error_WriteException */ public function mkdir(){ $this->authorize(); $fs = $this->fs; // may have bypassed definition of FS_CHMOD_DIR $mode = defined('FS_CHMOD_DIR') ? FS_CHMOD_DIR : 0755; // find first ancestor that exists while building tree $stack = array(); $here = $this->file; /* @var $parent Loco_fs_Directory */ while( $parent = $here->getParent() ){ array_unshift( $stack, $this->mapPath( $here->getPath() ) ); if( $parent->exists() ){ // have existant directory, now build full path foreach( $stack as $path ){ if( ! $fs->mkdir( $path, $mode ) ){ throw new Loco_error_WriteException( __('Failed to create directory','loco') ); } } return true; } $here = $parent; } throw new Loco_error_WriteException( __('Failed to build directory path','loco') ); } /** * Check whether write operations are permitted, or throw * @throws Loco_error_WriteException * @return Loco_fs_FileWriter */ public function authorize(){ if( $this->disabled() ){ throw new Loco_error_WriteException( __('File modification is disallowed by your WordPress config','loco') ); } return $this; } /** * Check if file system modification is banned * @return bool */ public function disabled(){ return loco_constant('DISALLOW_FILE_MODS'); } } Locations.php 0000666 00000005415 15214036525 0007224 0 ustar 00 <?php /** * Handles various file locations */ class Loco_fs_Locations extends ArrayObject { /** * Singleton of global languages directories * @var Loco_fs_Locations */ private static $langs; /** * Singleton of registered theme paths * @var Loco_fs_Locations */ private static $theme; /** * Singleton of registered plugin locations * @var Loco_fs_Locations */ private static $plugin; /** * Clear static caches. */ public static function clear(){ self::$langs = null; self::$theme = null; self::$plugin = null; } /** * @return Loco_fs_Locations */ public static function getGlobal(){ if( ! self::$langs ){ self::$langs = new Loco_fs_Locations( array( loco_constant('WP_LANG_DIR'), ) ); } return self::$langs; } /** * @return Loco_fs_Locations */ public static function getThemes(){ if( ! self::$theme ){ $roots = isset($GLOBALS['wp_theme_directories']) ? $GLOBALS['wp_theme_directories'] : array(); if( ! $roots ){ $roots[] = trailingslashit( loco_constant('WP_CONTENT_DIR') ).'themes'; } self::$theme = new Loco_fs_Locations( $roots ); } return self::$theme; } /** * @return Loco_fs_Locations */ public static function getPlugins(){ if( ! self::$plugin ){ self::$plugin = new Loco_fs_Locations( array( loco_constant('WP_PLUGIN_DIR'), ) ); } return self::$plugin; } /** * @internal */ public function __construct( array $paths ){ $cache = array(); foreach( $paths as $i => $path ){ // path should be normalized absolute path and be compared only with others of the same $path = Loco_fs_File::abs($path); if( ! $path ){ throw new InvalidArgumentException('Location must be absolute path'); } // path must have trailing slash, otherwise "/plugins/foobar" would match "/plugins/foo/" $path = trailingslashit($path); $cache[$path] = strlen($path); } parent::__construct( $cache ); } /** * Check if a given path begins with any of the registered ones * @param string absolute path * @return bool whether path matched */ public function check( $path ){ $path = Loco_fs_File::abs($path); foreach( $this as $prefix => $length ){ if( substr($path,0,$length) === $prefix ){ return true; } } return false; } } FileListInterface.php 0000666 00000000203 15214036525 0010613 0 ustar 00 <?php interface Loco_fs_FileListInterface extends Countable, Iterator { public function add( Loco_fs_File $file ); }
| ver. 1.4 |
Github
|
.
| PHP 7.0.33 | Generation time: 0 |
proxy
|
phpinfo
|
Settings