$mode file mode integer e.g 0664 * @param bool $recursive whether to set recursively (directories) * @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|self $ref * @return bool */ public function equal( $ref ){ return $this->path === (string) $ref; } /** * Normalize path for string comparison, resolves redundant dots and slashes. * @param string $base 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 = []; } else { $b = self::explode( $base, [] ); } $b = self::explode( $path, $b ); $this->setPath( implode('/',$b) ); } $this->base = $base; } return $this->path; } /** * Get real path if file is real, but without altering internal path property. * Also skips call to realpath() when likely to raise E_WARNING due to open_basedir * @return string */ public function getRealPath(){ if( $this->readable() ){ $path = realpath( $this->getPath() ); if( is_string($path) ){ return $path; } } return ''; } /** * @param string $path * @param string[] $b * @return array */ 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 * @param string $base Base path * @return string path relative to given base */ public function getRelativePath( $base ){ $path = $this->normalize(); if( self::abs($path) ){ // base may require normalizing $file = new Loco_fs_File($base); $base = $file->normalize(); $length = strlen($base)+1; // if we are below given base path, return ./relative if( substr($path,0,$length) === $base.'/' ){ if( strlen($path) > $length ){ return substr( $path, $length ); } // else paths were identical 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( $this->readable() ){ 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 wp-content directory * @return bool */ public function underContentDirectory(){ return Loco_fs_Locations::getContent()->check( $this->path ); } /** * Check if path is under WordPress root directory (ABSPATH) * @return bool */ public function underWordPressDirectory(){ return Loco_fs_Locations::getRoot()->check( $this->path ); } /** * Check if path is under the global system directory * @return bool */ public function underGlobalDirectory(){ return Loco_fs_Locations::getGlobal()->check( $this->path ); } /** * @return Loco_fs_Directory|null */ public function getParent(){ $dir = null; $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 * @param string $dest new path * @throws Loco_error_WriteException * @return Loco_fs_File new file */ public function copy( $dest ){ $copy = clone $this; $copy->path = $dest; $copy->clearStat(); $this->getWriteContext()->copy($copy); return $copy; } /** * Move/rename this file for real * @param Loco_fs_File $dest target file with new path * @throws Loco_error_WriteException * @return Loco_fs_File original file that should no longer exist */ public function move( Loco_fs_File $dest ){ $this->getWriteContext()->move($dest); return $this->clearStat(); } /** * Delete this file for real * @throws Loco_error_WriteException * @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 * @param string $ext new extension * @return self */ public function cloneExtension( $ext ){ return $this->cloneBasename( $this->filename().'.'.ltrim($ext,'.') ); } /** * Copy this object with an alternative name under the same directory * @param string $name new name * @return self */ public function cloneBasename( $name ){ $file = clone $this; $file->path = rtrim($file->dirname(),'/').'/'.$name; $file->info = null; return $file; } /** * Ensure full parent directory tree exists * @return Loco_fs_Directory|null */ public function createParent(){ $dir = $this->getParent(); if( $dir instanceof Loco_fs_Directory && ! $dir->exists() ){ $dir->mkdir(); } return $dir; } /** * @param string $data file contents * @return int number of bytes written to file */ public function putContents( $data ){ $this->getWriteContext()->putContents($data); $this->clearStat(); return $this->size(); } /** * Establish what part of the WordPress file system this is. * Value is that used by WP_Automatic_Updater::should_update. * @return string "core", "plugin", "theme" or "translation" */ public function getUpdateType(){ // global languages directory root, and canonical subdirectories $dirpath = (string) ( $this->isDirectory() ? $this : $this->getParent() ); $sub = Loco_fs_Locations::getGlobal()->rel($dirpath); if( is_string($sub) && '' !== $sub ){ list($root) = explode('/', $sub, 2 ); if( '.' === $root || 'themes' === $root || 'plugins' === $root ){ return 'translation'; } } // theme and plugin locations can be at any depth else if( $this->underThemeDirectory() ){ return 'theme'; } else if( $this->underPluginDirectory() ){ return 'plugin'; } // core locations are under WordPress root, but not under wp-content else if( $this->underWordPressDirectory() && ! $this->underContentDirectory() ){ return 'core'; } // else not an update type return ''; } /** * Get MD5 hash of file contents * @return string */ public function md5(){ if( $this->exists() ) { return md5_file( $this->path ); } else { return 'd41d8cd98f00b204e9800998ecf8427e'; } } }