summaryrefslogtreecommitdiff
path: root/www/wiki/includes/libs/filebackend/fsfile/TempFSFile.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/includes/libs/filebackend/fsfile/TempFSFile.php')
-rw-r--r--www/wiki/includes/libs/filebackend/fsfile/TempFSFile.php196
1 files changed, 196 insertions, 0 deletions
diff --git a/www/wiki/includes/libs/filebackend/fsfile/TempFSFile.php b/www/wiki/includes/libs/filebackend/fsfile/TempFSFile.php
new file mode 100644
index 00000000..00d20287
--- /dev/null
+++ b/www/wiki/includes/libs/filebackend/fsfile/TempFSFile.php
@@ -0,0 +1,196 @@
+<?php
+/**
+ * Location holder of files stored temporarily
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileBackend
+ */
+
+/**
+ * This class is used to hold the location and do limited manipulation
+ * of files stored temporarily (this will be whatever wfTempDir() returns)
+ *
+ * @ingroup FileBackend
+ */
+class TempFSFile extends FSFile {
+ /** @var bool Garbage collect the temp file */
+ protected $canDelete = false;
+
+ /** @var array Map of (path => 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;
+ Wikimedia\suppressWarnings();
+ $newFileHandle = fopen( $path, 'x' );
+ Wikimedia\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
+ Wikimedia\suppressWarnings();
+ $ok = unlink( $this->path );
+ Wikimedia\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 ) {
+ Wikimedia\suppressWarnings();
+ unlink( $path );
+ Wikimedia\restoreWarnings();
+ }
+ }
+
+ /**
+ * Cleans up after the temporary file by deleting it
+ */
+ function __destruct() {
+ if ( $this->canDelete ) {
+ $this->purge();
+ }
+ }
+}