Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.67% covered (warning)
86.67%
26 / 30
87.50% covered (warning)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
TempFSFile
89.66% covered (warning)
89.66%
26 / 29
87.50% covered (warning)
87.50%
7 / 8
18.36
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 factory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUsableTempDirectory
66.67% covered (warning)
66.67%
6 / 9
0.00% covered (danger)
0.00%
0 / 1
5.93
 purge
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 bind
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 preserve
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 autocollect
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 purgeAllOnShutdown
n/a
0 / 0
n/a
0 / 0
2
 __destruct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * Location holder of files stored temporarily
5 *
6 * @license GPL-2.0-or-later
7 * @file
8 * @ingroup FileBackend
9 */
10
11namespace Wikimedia\FileBackend\FSFile;
12
13use RuntimeException;
14use WeakMap;
15
16/**
17 * This class is used to hold the location and do limited manipulation
18 * of files stored temporarily (this will be whatever wfTempDir() returns)
19 *
20 * @ingroup FileBackend
21 */
22class TempFSFile extends FSFile {
23    /** @var bool Garbage collect the temp file */
24    protected $canDelete = false;
25
26    /** @var array Map of (path => 1) for paths to delete on shutdown */
27    protected static $pathsCollect = null;
28
29    /**
30     * A WeakMap where the key is an object which depends on the file, and the
31     * value is a TempFSFile responsible for deleting the file. This keeps each
32     * TempFSFile alive until all associated objects have been destroyed.
33     * @var WeakMap|null
34     */
35    private static $references;
36
37    /**
38     * Do not call directly. Use TempFSFileFactory
39     *
40     * @param string $path
41     */
42    public function __construct( $path ) {
43        parent::__construct( $path );
44
45        if ( self::$pathsCollect === null ) {
46            // @codeCoverageIgnoreStart
47            self::$pathsCollect = [];
48            register_shutdown_function( self::purgeAllOnShutdown( ... ) );
49            // @codeCoverageIgnoreEnd
50        }
51    }
52
53    /**
54     * Make a new temporary file on the file system.
55     * Temporary files may be purged when the file object falls out of scope.
56     *
57     * @deprecated since 1.34, use TempFSFileFactory directly
58     *
59     * @param string $prefix
60     * @param string $extension Optional file extension
61     * @param string|null $tmpDirectory Optional parent directory
62     * @return TempFSFile|null
63     */
64    public static function factory( $prefix, $extension = '', $tmpDirectory = null ) {
65        return ( new TempFSFileFactory( $tmpDirectory ) )->newTempFSFile( $prefix, $extension );
66    }
67
68    /**
69     * @todo Is there any useful way to test this? Would it be useful to make this non-static on
70     * TempFSFileFactory?
71     *
72     * @return string Filesystem path to a temporary directory
73     * @throws RuntimeException if no writable temporary directory can be found
74     */
75    public static function getUsableTempDirectory() {
76        $tmpDir = array_map( 'getenv', [ 'TMPDIR', 'TMP', 'TEMP' ] );
77        $tmpDir[] = sys_get_temp_dir();
78        $tmpDir[] = ini_get( 'upload_tmp_dir' );
79        foreach ( $tmpDir as $tmp ) {
80            if ( $tmp != '' && is_dir( $tmp ) && is_writable( $tmp ) ) {
81                return $tmp;
82            }
83        }
84
85        throw new RuntimeException(
86            'No writable temporary directory could be found. ' .
87            'Please explicitly specify a writable directory in configuration.' );
88    }
89
90    /**
91     * Purge this file off the file system
92     *
93     * @return bool Success
94     */
95    public function purge() {
96        $this->canDelete = false; // done
97        // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
98        $ok = @unlink( $this->path );
99
100        unset( self::$pathsCollect[$this->path] );
101
102        return $ok;
103    }
104
105    /**
106     * Clean up the temporary file only after an object goes out of scope
107     *
108     * @param mixed $object
109     * @return TempFSFile This object
110     */
111    public function bind( $object ) {
112        if ( is_object( $object ) ) {
113            // Use a WeakMap to avoid dynamic property creation (T324894)
114            if ( self::$references === null ) {
115                self::$references = new WeakMap;
116            }
117            self::$references[$object] = $this;
118        }
119
120        return $this;
121    }
122
123    /**
124     * Set flag to not clean up after the temporary file
125     *
126     * @return TempFSFile This object
127     */
128    public function preserve() {
129        $this->canDelete = false;
130
131        unset( self::$pathsCollect[$this->path] );
132
133        return $this;
134    }
135
136    /**
137     * Set flag clean up after the temporary file
138     *
139     * @return TempFSFile This object
140     */
141    public function autocollect() {
142        $this->canDelete = true;
143
144        self::$pathsCollect[$this->path] = 1;
145
146        return $this;
147    }
148
149    /**
150     * Try to make sure that all files are purged on error
151     *
152     * This method should only be called internally
153     *
154     * @codeCoverageIgnore
155     */
156    public static function purgeAllOnShutdown() {
157        foreach ( self::$pathsCollect as $path => $unused ) {
158            // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
159            @unlink( $path );
160        }
161    }
162
163    /**
164     * Cleans up after the temporary file by deleting it
165     */
166    public function __destruct() {
167        if ( $this->canDelete ) {
168            $this->purge();
169        }
170    }
171}
172
173/** @deprecated class alias since 1.43 */
174class_alias( TempFSFile::class, 'TempFSFile' );