From: Aaron Schulz Date: Tue, 18 Oct 2016 22:36:24 +0000 (-0700) Subject: Move FSFile classes to /fsfile X-Git-Tag: 1.31.0-rc.0~5078^2 X-Git-Url: http://git.cyclocoop.org//%27%40script%40/%27?a=commitdiff_plain;h=f13f42d1499a8545ce2ddc0ecf5aa956e6fb3891;p=lhc%2Fweb%2Fwiklou.git Move FSFile classes to /fsfile Change-Id: I21f3a9ac52b1f953ca09da0705cac868785193e1 --- diff --git a/autoload.php b/autoload.php index 1beb00c5f2..3a2d06ff20 100644 --- a/autoload.php +++ b/autoload.php @@ -436,7 +436,7 @@ $wgAutoloadLocalClasses = [ 'ExternalStoreHttp' => __DIR__ . '/includes/externalstore/ExternalStoreHttp.php', 'ExternalStoreMedium' => __DIR__ . '/includes/externalstore/ExternalStoreMedium.php', 'ExternalStoreMwstore' => __DIR__ . '/includes/externalstore/ExternalStoreMwstore.php', - 'FSFile' => __DIR__ . '/includes/libs/filebackend/FSFile.php', + 'FSFile' => __DIR__ . '/includes/libs/filebackend/fsfile/FSFile.php', 'FSFileBackend' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php', 'FSFileBackendDirList' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php', 'FSFileBackendFileList' => __DIR__ . '/includes/libs/filebackend/FSFileBackend.php', @@ -1415,7 +1415,7 @@ $wgAutoloadLocalClasses = [ 'TableDiffFormatter' => __DIR__ . '/includes/diff/TableDiffFormatter.php', 'TablePager' => __DIR__ . '/includes/pager/TablePager.php', 'TagLogFormatter' => __DIR__ . '/includes/logging/TagLogFormatter.php', - 'TempFSFile' => __DIR__ . '/includes/libs/filebackend/TempFSFile.php', + 'TempFSFile' => __DIR__ . '/includes/libs/filebackend/fsfile/TempFSFile.php', 'TempFileRepo' => __DIR__ . '/includes/filerepo/FileRepo.php', 'TemplateParser' => __DIR__ . '/includes/TemplateParser.php', 'TemplatesOnThisPageFormatter' => __DIR__ . '/includes/TemplatesOnThisPageFormatter.php', diff --git a/includes/libs/filebackend/FSFile.php b/includes/libs/filebackend/FSFile.php deleted file mode 100644 index dacad1cb26..0000000000 --- a/includes/libs/filebackend/FSFile.php +++ /dev/null @@ -1,223 +0,0 @@ -path = $path; - } - - /** - * Returns the file system path - * - * @return string - */ - public function getPath() { - return $this->path; - } - - /** - * Checks if the file exists - * - * @return bool - */ - public function exists() { - return is_file( $this->path ); - } - - /** - * Get the file size in bytes - * - * @return int|bool - */ - public function getSize() { - return filesize( $this->path ); - } - - /** - * Get the file's last-modified timestamp - * - * @return string|bool TS_MW timestamp or false on failure - */ - public function getTimestamp() { - MediaWiki\suppressWarnings(); - $timestamp = filemtime( $this->path ); - MediaWiki\restoreWarnings(); - if ( $timestamp !== false ) { - $timestamp = wfTimestamp( TS_MW, $timestamp ); - } - - return $timestamp; - } - - /** - * Get an associative array containing information about - * a file with the given storage path. - * - * Resulting array fields include: - * - fileExists - * - size (filesize in bytes) - * - mime (as major/minor) - * - file-mime (as major/minor) - * - sha1 (in base 36) - * - major_mime - * - minor_mime - * - * @param string|bool $ext The file extension, or true to extract it from the filename. - * Set it to false to ignore the extension. Currently unused. - * @return array - */ - public function getProps( $ext = true ) { - $info = self::placeholderProps(); - $info['fileExists'] = $this->exists(); - - if ( $info['fileExists'] ) { - $info['size'] = $this->getSize(); // bytes - $info['sha1'] = $this->getSha1Base36(); - - $mime = mime_content_type( $this->path ); - # MIME type according to file contents - $info['file-mime'] = ( $mime === false ) ? 'unknown/unknown' : $mime; - # logical MIME type - $info['mime'] = $mime; - - if ( strpos( $mime, '/' ) !== false ) { - list( $info['major_mime'], $info['minor_mime'] ) = explode( '/', $mime, 2 ); - } else { - list( $info['major_mime'], $info['minor_mime'] ) = [ $mime, 'unknown' ]; - } - } - - return $info; - } - - /** - * Placeholder file properties to use for files that don't exist - * - * Resulting array fields include: - * - fileExists - * - size (filesize in bytes) - * - mime (as major/minor) - * - file-mime (as major/minor) - * - sha1 (in base 36) - * - major_mime - * - minor_mime - * - * @return array - */ - public static function placeholderProps() { - $info = []; - $info['fileExists'] = false; - $info['size'] = 0; - $info['file-mime'] = null; - $info['major_mime'] = null; - $info['minor_mime'] = null; - $info['mime'] = null; - $info['sha1'] = ''; - - return $info; - } - - /** - * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case - * encoding, zero padded to 31 digits. - * - * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36 - * fairly neatly. - * - * @param bool $recache - * @return bool|string False on failure - */ - public function getSha1Base36( $recache = false ) { - if ( $this->sha1Base36 !== null && !$recache ) { - return $this->sha1Base36; - } - - MediaWiki\suppressWarnings(); - $this->sha1Base36 = sha1_file( $this->path ); - MediaWiki\restoreWarnings(); - - if ( $this->sha1Base36 !== false ) { - $this->sha1Base36 = Wikimedia\base_convert( $this->sha1Base36, 16, 36, 31 ); - } - - return $this->sha1Base36; - } - - /** - * Get the final file extension from a file system path - * - * @param string $path - * @return string - */ - public static function extensionFromPath( $path ) { - $i = strrpos( $path, '.' ); - - return strtolower( $i ? substr( $path, $i + 1 ) : '' ); - } - - /** - * Get an associative array containing information about a file in the local filesystem. - * - * @param string $path Absolute local filesystem path - * @param string|bool $ext The file extension, or true to extract it from the filename. - * Set it to false to ignore the extension. - * @return array - */ - public static function getPropsFromPath( $path, $ext = true ) { - $fsFile = new self( $path ); - - return $fsFile->getProps( $ext ); - } - - /** - * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case - * encoding, zero padded to 31 digits. - * - * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36 - * fairly neatly. - * - * @param string $path - * @return bool|string False on failure - */ - public static function getSha1Base36FromPath( $path ) { - $fsFile = new self( $path ); - - return $fsFile->getSha1Base36(); - } -} diff --git a/includes/libs/filebackend/TempFSFile.php b/includes/libs/filebackend/TempFSFile.php deleted file mode 100644 index fed6812f5b..0000000000 --- a/includes/libs/filebackend/TempFSFile.php +++ /dev/null @@ -1,196 +0,0 @@ - 1) for paths to delete on shutdown */ - protected static $pathsCollect = null; - - public function __construct( $path ) { - parent::__construct( $path ); - - if ( self::$pathsCollect === null ) { - self::$pathsCollect = []; - register_shutdown_function( [ __CLASS__, 'purgeAllOnShutdown' ] ); - } - } - - /** - * Make a new temporary file on the file system. - * Temporary files may be purged when the file object falls out of scope. - * - * @param string $prefix - * @param string $extension Optional file extension - * @param string|null $tmpDirectory Optional parent directory - * @return TempFSFile|null - */ - public static function factory( $prefix, $extension = '', $tmpDirectory = null ) { - $ext = ( $extension != '' ) ? ".{$extension}" : ''; - - $attempts = 5; - while ( $attempts-- ) { - $hex = sprintf( '%06x%06x', mt_rand( 0, 0xffffff ), mt_rand( 0, 0xffffff ) ); - if ( !is_string( $tmpDirectory ) ) { - $tmpDirectory = self::getUsableTempDirectory(); - } - $path = wfTempDir() . '/' . $prefix . $hex . $ext; - MediaWiki\suppressWarnings(); - $newFileHandle = fopen( $path, 'x' ); - MediaWiki\restoreWarnings(); - if ( $newFileHandle ) { - fclose( $newFileHandle ); - $tmpFile = new self( $path ); - $tmpFile->autocollect(); - // Safely instantiated, end loop. - return $tmpFile; - } - } - - // Give up - return null; - } - - /** - * @return string Filesystem path to a temporary directory - * @throws RuntimeException - */ - public static function getUsableTempDirectory() { - $tmpDir = array_map( 'getenv', [ 'TMPDIR', 'TMP', 'TEMP' ] ); - $tmpDir[] = sys_get_temp_dir(); - $tmpDir[] = ini_get( 'upload_tmp_dir' ); - foreach ( $tmpDir as $tmp ) { - if ( $tmp != '' && is_dir( $tmp ) && is_writable( $tmp ) ) { - return $tmp; - } - } - - // PHP on Windows will detect C:\Windows\Temp as not writable even though PHP can write to - // it so create a directory within that called 'mwtmp' with a suffix of the user running - // the current process. - // The user is included as if various scripts are run by different users they will likely - // not be able to access each others temporary files. - if ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) { - $tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mwtmp-' . get_current_user(); - if ( !file_exists( $tmp ) ) { - mkdir( $tmp ); - } - if ( is_dir( $tmp ) && is_writable( $tmp ) ) { - return $tmp; - } - } - - throw new RuntimeException( - 'No writable temporary directory could be found. ' . - 'Please explicitly specify a writable directory in configuration.' ); - } - - /** - * Purge this file off the file system - * - * @return bool Success - */ - public function purge() { - $this->canDelete = false; // done - MediaWiki\suppressWarnings(); - $ok = unlink( $this->path ); - MediaWiki\restoreWarnings(); - - unset( self::$pathsCollect[$this->path] ); - - return $ok; - } - - /** - * Clean up the temporary file only after an object goes out of scope - * - * @param object $object - * @return TempFSFile This object - */ - public function bind( $object ) { - if ( is_object( $object ) ) { - if ( !isset( $object->tempFSFileReferences ) ) { - // Init first since $object might use __get() and return only a copy variable - $object->tempFSFileReferences = []; - } - $object->tempFSFileReferences[] = $this; - } - - return $this; - } - - /** - * Set flag to not clean up after the temporary file - * - * @return TempFSFile This object - */ - public function preserve() { - $this->canDelete = false; - - unset( self::$pathsCollect[$this->path] ); - - return $this; - } - - /** - * Set flag clean up after the temporary file - * - * @return TempFSFile This object - */ - public function autocollect() { - $this->canDelete = true; - - self::$pathsCollect[$this->path] = 1; - - return $this; - } - - /** - * Try to make sure that all files are purged on error - * - * This method should only be called internally - */ - public static function purgeAllOnShutdown() { - foreach ( self::$pathsCollect as $path ) { - MediaWiki\suppressWarnings(); - unlink( $path ); - MediaWiki\restoreWarnings(); - } - } - - /** - * Cleans up after the temporary file by deleting it - */ - function __destruct() { - if ( $this->canDelete ) { - $this->purge(); - } - } -} diff --git a/includes/libs/filebackend/fsfile/FSFile.php b/includes/libs/filebackend/fsfile/FSFile.php new file mode 100644 index 0000000000..dacad1cb26 --- /dev/null +++ b/includes/libs/filebackend/fsfile/FSFile.php @@ -0,0 +1,223 @@ +path = $path; + } + + /** + * Returns the file system path + * + * @return string + */ + public function getPath() { + return $this->path; + } + + /** + * Checks if the file exists + * + * @return bool + */ + public function exists() { + return is_file( $this->path ); + } + + /** + * Get the file size in bytes + * + * @return int|bool + */ + public function getSize() { + return filesize( $this->path ); + } + + /** + * Get the file's last-modified timestamp + * + * @return string|bool TS_MW timestamp or false on failure + */ + public function getTimestamp() { + MediaWiki\suppressWarnings(); + $timestamp = filemtime( $this->path ); + MediaWiki\restoreWarnings(); + if ( $timestamp !== false ) { + $timestamp = wfTimestamp( TS_MW, $timestamp ); + } + + return $timestamp; + } + + /** + * Get an associative array containing information about + * a file with the given storage path. + * + * Resulting array fields include: + * - fileExists + * - size (filesize in bytes) + * - mime (as major/minor) + * - file-mime (as major/minor) + * - sha1 (in base 36) + * - major_mime + * - minor_mime + * + * @param string|bool $ext The file extension, or true to extract it from the filename. + * Set it to false to ignore the extension. Currently unused. + * @return array + */ + public function getProps( $ext = true ) { + $info = self::placeholderProps(); + $info['fileExists'] = $this->exists(); + + if ( $info['fileExists'] ) { + $info['size'] = $this->getSize(); // bytes + $info['sha1'] = $this->getSha1Base36(); + + $mime = mime_content_type( $this->path ); + # MIME type according to file contents + $info['file-mime'] = ( $mime === false ) ? 'unknown/unknown' : $mime; + # logical MIME type + $info['mime'] = $mime; + + if ( strpos( $mime, '/' ) !== false ) { + list( $info['major_mime'], $info['minor_mime'] ) = explode( '/', $mime, 2 ); + } else { + list( $info['major_mime'], $info['minor_mime'] ) = [ $mime, 'unknown' ]; + } + } + + return $info; + } + + /** + * Placeholder file properties to use for files that don't exist + * + * Resulting array fields include: + * - fileExists + * - size (filesize in bytes) + * - mime (as major/minor) + * - file-mime (as major/minor) + * - sha1 (in base 36) + * - major_mime + * - minor_mime + * + * @return array + */ + public static function placeholderProps() { + $info = []; + $info['fileExists'] = false; + $info['size'] = 0; + $info['file-mime'] = null; + $info['major_mime'] = null; + $info['minor_mime'] = null; + $info['mime'] = null; + $info['sha1'] = ''; + + return $info; + } + + /** + * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case + * encoding, zero padded to 31 digits. + * + * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36 + * fairly neatly. + * + * @param bool $recache + * @return bool|string False on failure + */ + public function getSha1Base36( $recache = false ) { + if ( $this->sha1Base36 !== null && !$recache ) { + return $this->sha1Base36; + } + + MediaWiki\suppressWarnings(); + $this->sha1Base36 = sha1_file( $this->path ); + MediaWiki\restoreWarnings(); + + if ( $this->sha1Base36 !== false ) { + $this->sha1Base36 = Wikimedia\base_convert( $this->sha1Base36, 16, 36, 31 ); + } + + return $this->sha1Base36; + } + + /** + * Get the final file extension from a file system path + * + * @param string $path + * @return string + */ + public static function extensionFromPath( $path ) { + $i = strrpos( $path, '.' ); + + return strtolower( $i ? substr( $path, $i + 1 ) : '' ); + } + + /** + * Get an associative array containing information about a file in the local filesystem. + * + * @param string $path Absolute local filesystem path + * @param string|bool $ext The file extension, or true to extract it from the filename. + * Set it to false to ignore the extension. + * @return array + */ + public static function getPropsFromPath( $path, $ext = true ) { + $fsFile = new self( $path ); + + return $fsFile->getProps( $ext ); + } + + /** + * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case + * encoding, zero padded to 31 digits. + * + * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36 + * fairly neatly. + * + * @param string $path + * @return bool|string False on failure + */ + public static function getSha1Base36FromPath( $path ) { + $fsFile = new self( $path ); + + return $fsFile->getSha1Base36(); + } +} diff --git a/includes/libs/filebackend/fsfile/TempFSFile.php b/includes/libs/filebackend/fsfile/TempFSFile.php new file mode 100644 index 0000000000..fed6812f5b --- /dev/null +++ b/includes/libs/filebackend/fsfile/TempFSFile.php @@ -0,0 +1,196 @@ + 1) for paths to delete on shutdown */ + protected static $pathsCollect = null; + + public function __construct( $path ) { + parent::__construct( $path ); + + if ( self::$pathsCollect === null ) { + self::$pathsCollect = []; + register_shutdown_function( [ __CLASS__, 'purgeAllOnShutdown' ] ); + } + } + + /** + * Make a new temporary file on the file system. + * Temporary files may be purged when the file object falls out of scope. + * + * @param string $prefix + * @param string $extension Optional file extension + * @param string|null $tmpDirectory Optional parent directory + * @return TempFSFile|null + */ + public static function factory( $prefix, $extension = '', $tmpDirectory = null ) { + $ext = ( $extension != '' ) ? ".{$extension}" : ''; + + $attempts = 5; + while ( $attempts-- ) { + $hex = sprintf( '%06x%06x', mt_rand( 0, 0xffffff ), mt_rand( 0, 0xffffff ) ); + if ( !is_string( $tmpDirectory ) ) { + $tmpDirectory = self::getUsableTempDirectory(); + } + $path = wfTempDir() . '/' . $prefix . $hex . $ext; + MediaWiki\suppressWarnings(); + $newFileHandle = fopen( $path, 'x' ); + MediaWiki\restoreWarnings(); + if ( $newFileHandle ) { + fclose( $newFileHandle ); + $tmpFile = new self( $path ); + $tmpFile->autocollect(); + // Safely instantiated, end loop. + return $tmpFile; + } + } + + // Give up + return null; + } + + /** + * @return string Filesystem path to a temporary directory + * @throws RuntimeException + */ + public static function getUsableTempDirectory() { + $tmpDir = array_map( 'getenv', [ 'TMPDIR', 'TMP', 'TEMP' ] ); + $tmpDir[] = sys_get_temp_dir(); + $tmpDir[] = ini_get( 'upload_tmp_dir' ); + foreach ( $tmpDir as $tmp ) { + if ( $tmp != '' && is_dir( $tmp ) && is_writable( $tmp ) ) { + return $tmp; + } + } + + // PHP on Windows will detect C:\Windows\Temp as not writable even though PHP can write to + // it so create a directory within that called 'mwtmp' with a suffix of the user running + // the current process. + // The user is included as if various scripts are run by different users they will likely + // not be able to access each others temporary files. + if ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) { + $tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mwtmp-' . get_current_user(); + if ( !file_exists( $tmp ) ) { + mkdir( $tmp ); + } + if ( is_dir( $tmp ) && is_writable( $tmp ) ) { + return $tmp; + } + } + + throw new RuntimeException( + 'No writable temporary directory could be found. ' . + 'Please explicitly specify a writable directory in configuration.' ); + } + + /** + * Purge this file off the file system + * + * @return bool Success + */ + public function purge() { + $this->canDelete = false; // done + MediaWiki\suppressWarnings(); + $ok = unlink( $this->path ); + MediaWiki\restoreWarnings(); + + unset( self::$pathsCollect[$this->path] ); + + return $ok; + } + + /** + * Clean up the temporary file only after an object goes out of scope + * + * @param object $object + * @return TempFSFile This object + */ + public function bind( $object ) { + if ( is_object( $object ) ) { + if ( !isset( $object->tempFSFileReferences ) ) { + // Init first since $object might use __get() and return only a copy variable + $object->tempFSFileReferences = []; + } + $object->tempFSFileReferences[] = $this; + } + + return $this; + } + + /** + * Set flag to not clean up after the temporary file + * + * @return TempFSFile This object + */ + public function preserve() { + $this->canDelete = false; + + unset( self::$pathsCollect[$this->path] ); + + return $this; + } + + /** + * Set flag clean up after the temporary file + * + * @return TempFSFile This object + */ + public function autocollect() { + $this->canDelete = true; + + self::$pathsCollect[$this->path] = 1; + + return $this; + } + + /** + * Try to make sure that all files are purged on error + * + * This method should only be called internally + */ + public static function purgeAllOnShutdown() { + foreach ( self::$pathsCollect as $path ) { + MediaWiki\suppressWarnings(); + unlink( $path ); + MediaWiki\restoreWarnings(); + } + } + + /** + * Cleans up after the temporary file by deleting it + */ + function __destruct() { + if ( $this->canDelete ) { + $this->purge(); + } + } +}