Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.51% |
37 / 49 |
|
63.64% |
7 / 11 |
CRAP | |
0.00% |
0 / 1 |
FSFile | |
77.08% |
37 / 48 |
|
63.64% |
7 / 11 |
23.34 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
exists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSize | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getTimestamp | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getProps | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
4.01 | |||
placeholderProps | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
getSha1Base36 | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
extensionFromPath | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
getPropsFromPath | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getSha1Base36FromPath | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Non-directory file on the file system. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @ingroup FileBackend |
22 | */ |
23 | |
24 | namespace Wikimedia\FileBackend\FSFile; |
25 | |
26 | use Wikimedia\AtEase\AtEase; |
27 | use Wikimedia\Timestamp\ConvertibleTimestamp; |
28 | |
29 | /** |
30 | * Class representing a non-directory file on the file system |
31 | * |
32 | * @ingroup FileBackend |
33 | */ |
34 | class FSFile { |
35 | /** @var string Path to file */ |
36 | protected $path; |
37 | |
38 | /** @var string File SHA-1 in base 36 */ |
39 | protected $sha1Base36; |
40 | |
41 | /** |
42 | * Sets up the file object |
43 | * |
44 | * @param string $path Path to temporary file on local disk |
45 | */ |
46 | public function __construct( $path ) { |
47 | $this->path = $path; |
48 | } |
49 | |
50 | /** |
51 | * Returns the file system path |
52 | * |
53 | * @return string |
54 | */ |
55 | public function getPath() { |
56 | return $this->path; |
57 | } |
58 | |
59 | /** |
60 | * Checks if the file exists |
61 | * |
62 | * @return bool |
63 | */ |
64 | public function exists() { |
65 | return is_file( $this->path ); |
66 | } |
67 | |
68 | /** |
69 | * Get the file size in bytes |
70 | * |
71 | * @return int|bool |
72 | */ |
73 | public function getSize() { |
74 | AtEase::suppressWarnings(); |
75 | $size = filesize( $this->path ); |
76 | AtEase::restoreWarnings(); |
77 | |
78 | return $size; |
79 | } |
80 | |
81 | /** |
82 | * Get the file's last-modified timestamp |
83 | * |
84 | * @return string|bool TS_MW timestamp or false on failure |
85 | */ |
86 | public function getTimestamp() { |
87 | AtEase::suppressWarnings(); |
88 | $timestamp = filemtime( $this->path ); |
89 | AtEase::restoreWarnings(); |
90 | if ( $timestamp !== false ) { |
91 | $timestamp = ConvertibleTimestamp::convert( TS_MW, $timestamp ); |
92 | } |
93 | |
94 | return $timestamp; |
95 | } |
96 | |
97 | /** |
98 | * Get an associative array containing information about |
99 | * a file with the given storage path. |
100 | * |
101 | * Resulting array fields include: |
102 | * - fileExists |
103 | * - size (filesize in bytes) |
104 | * - mime (as major/minor) |
105 | * - file-mime (as major/minor) |
106 | * - sha1 (in base 36) |
107 | * - major_mime |
108 | * - minor_mime |
109 | * |
110 | * @param string|bool $ext The file extension, or true to extract it from the filename. |
111 | * Set it to false to ignore the extension. Currently unused. |
112 | * @return array |
113 | */ |
114 | public function getProps( $ext = true ) { |
115 | $info = self::placeholderProps(); |
116 | $info['fileExists'] = $this->exists(); |
117 | |
118 | if ( $info['fileExists'] ) { |
119 | $info['size'] = $this->getSize(); // bytes |
120 | $info['sha1'] = $this->getSha1Base36(); |
121 | |
122 | $mime = mime_content_type( $this->path ); |
123 | # MIME type according to file contents |
124 | $info['file-mime'] = ( $mime === false ) ? 'unknown/unknown' : $mime; |
125 | # logical MIME type |
126 | $info['mime'] = $mime; |
127 | |
128 | if ( strpos( $mime, '/' ) !== false ) { |
129 | [ $info['major_mime'], $info['minor_mime'] ] = explode( '/', $mime, 2 ); |
130 | } else { |
131 | [ $info['major_mime'], $info['minor_mime'] ] = [ $mime, 'unknown' ]; |
132 | } |
133 | } |
134 | |
135 | return $info; |
136 | } |
137 | |
138 | /** |
139 | * Placeholder file properties to use for files that don't exist |
140 | * |
141 | * Resulting array fields include: |
142 | * - fileExists |
143 | * - size (filesize in bytes) |
144 | * - mime (as major/minor) |
145 | * - file-mime (as major/minor) |
146 | * - sha1 (in base 36) |
147 | * - major_mime |
148 | * - minor_mime |
149 | * |
150 | * @return array |
151 | */ |
152 | public static function placeholderProps() { |
153 | $info = []; |
154 | $info['fileExists'] = false; |
155 | $info['size'] = 0; |
156 | $info['file-mime'] = null; |
157 | $info['major_mime'] = null; |
158 | $info['minor_mime'] = null; |
159 | $info['mime'] = null; |
160 | $info['sha1'] = ''; |
161 | |
162 | return $info; |
163 | } |
164 | |
165 | /** |
166 | * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case |
167 | * encoding, zero padded to 31 digits. |
168 | * |
169 | * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36 |
170 | * fairly neatly. |
171 | * |
172 | * @param bool $recache |
173 | * @return bool|string False on failure |
174 | */ |
175 | public function getSha1Base36( $recache = false ) { |
176 | if ( $this->sha1Base36 !== null && !$recache ) { |
177 | return $this->sha1Base36; |
178 | } |
179 | |
180 | AtEase::suppressWarnings(); |
181 | $this->sha1Base36 = sha1_file( $this->path ); |
182 | AtEase::restoreWarnings(); |
183 | |
184 | if ( $this->sha1Base36 !== false ) { |
185 | $this->sha1Base36 = \Wikimedia\base_convert( $this->sha1Base36, 16, 36, 31 ); |
186 | } |
187 | |
188 | return $this->sha1Base36; |
189 | } |
190 | |
191 | /** |
192 | * Get the final file extension from a file system path |
193 | * |
194 | * @param string $path |
195 | * @return string |
196 | */ |
197 | public static function extensionFromPath( $path ) { |
198 | $i = strrpos( $path, '.' ); |
199 | |
200 | return strtolower( $i ? substr( $path, $i + 1 ) : '' ); |
201 | } |
202 | |
203 | /** |
204 | * Get an associative array containing information about a file in the local filesystem. |
205 | * |
206 | * @param string $path Absolute local filesystem path |
207 | * @param string|bool $ext The file extension, or true to extract it from the filename. |
208 | * Set it to false to ignore the extension. |
209 | * @return array |
210 | */ |
211 | public static function getPropsFromPath( $path, $ext = true ) { |
212 | $fsFile = new self( $path ); |
213 | |
214 | return $fsFile->getProps( $ext ); |
215 | } |
216 | |
217 | /** |
218 | * Get a SHA-1 hash of a file in the local filesystem, in base-36 lower case |
219 | * encoding, zero padded to 31 digits. |
220 | * |
221 | * 160 log 2 / log 36 = 30.95, so the 160-bit hash fills 31 digits in base 36 |
222 | * fairly neatly. |
223 | * |
224 | * @param string $path |
225 | * @return false|string False on failure |
226 | */ |
227 | public static function getSha1Base36FromPath( $path ) { |
228 | $fsFile = new self( $path ); |
229 | |
230 | return $fsFile->getSha1Base36(); |
231 | } |
232 | } |
233 | |
234 | /** @deprecated class alias since 1.43 */ |
235 | class_alias( FSFile::class, 'FSFile' ); |