/** @var bool Garbage collect the temp file */
protected $canDelete = false;
- /** @var array Active temp files to purge on shutdown */
- protected static $instances = array();
-
/** @var array Map of (path => 1) for paths to delete on shutdown */
protected static $pathsCollect = null;
parent::__construct( $path );
if ( self::$pathsCollect === null ) {
- self::$pathsCollect = array();
- register_shutdown_function( array( __CLASS__, 'purgeAllOnShutdown' ) );
+ self::$pathsCollect = [];
+ register_shutdown_function( [ __CLASS__, 'purgeAllOnShutdown' ] );
}
}
* Temporary files may be purged when the file object falls out of scope.
*
* @param string $prefix
- * @param string $extension
+ * @param string $extension Optional file extension
+ * @param string|null $tmpDirectory Optional parent directory
* @return TempFSFile|null
*/
- public static function factory( $prefix, $extension = '' ) {
- $base = wfTempDir() . '/' . $prefix . wfRandomString( 12 );
- $ext = ( $extension != '' ) ? ".{$extension}" : "";
- for ( $attempt = 1; true; $attempt++ ) {
- $path = "{$base}-{$attempt}{$ext}";
+ 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 );
- break; // got it
+ $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 ( $attempt >= 5 ) {
- return null; // give up
+ if ( is_dir( $tmp ) && is_writable( $tmp ) ) {
+ return $tmp;
}
}
- $tmpFile = new self( $path );
- $tmpFile->autocollect(); // safely instantiated
- return $tmpFile;
+ throw new RuntimeException(
+ 'No writable temporary directory could be found. ' .
+ 'Please explicitly specify a writable directory in configuration.' );
}
/**
if ( is_object( $object ) ) {
if ( !isset( $object->tempFSFileReferences ) ) {
// Init first since $object might use __get() and return only a copy variable
- $object->tempFSFileReferences = array();
+ $object->tempFSFileReferences = [];
}
$object->tempFSFileReferences[] = $this;
}