Completed
Branch master (939199)
by
unknown
39:35
created

includes/filerepo/file/OldLocalFile.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Old file in the oldimage table.
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 FileAbstraction
22
 */
23
24
/**
25
 * Class to represent a file in the oldimage table
26
 *
27
 * @ingroup FileAbstraction
28
 */
29
class OldLocalFile extends LocalFile {
30
	/** @var string Timestamp */
31
	protected $requestedTime;
32
33
	/** @var string Archive name */
34
	protected $archive_name;
35
36
	const CACHE_VERSION = 1;
37
	const MAX_CACHE_ROWS = 20;
38
39
	/**
40
	 * @param Title $title
41
	 * @param FileRepo $repo
42
	 * @param null|int $time Timestamp or null
43
	 * @return OldLocalFile
44
	 * @throws MWException
45
	 */
46
	static function newFromTitle( $title, $repo, $time = null ) {
47
		# The null default value is only here to avoid an E_STRICT
48
		if ( $time === null ) {
49
			throw new MWException( __METHOD__ . ' got null for $time parameter' );
50
		}
51
52
		return new self( $title, $repo, $time, null );
53
	}
54
55
	/**
56
	 * @param Title $title
57
	 * @param FileRepo $repo
58
	 * @param string $archiveName
59
	 * @return OldLocalFile
60
	 */
61
	static function newFromArchiveName( $title, $repo, $archiveName ) {
62
		return new self( $title, $repo, null, $archiveName );
63
	}
64
65
	/**
66
	 * @param stdClass $row
67
	 * @param FileRepo $repo
68
	 * @return OldLocalFile
69
	 */
70
	static function newFromRow( $row, $repo ) {
71
		$title = Title::makeTitle( NS_FILE, $row->oi_name );
72
		$file = new self( $title, $repo, null, $row->oi_archive_name );
73
		$file->loadFromRow( $row, 'oi_' );
74
75
		return $file;
76
	}
77
78
	/**
79
	 * Create a OldLocalFile from a SHA-1 key
80
	 * Do not call this except from inside a repo class.
81
	 *
82
	 * @param string $sha1 Base-36 SHA-1
83
	 * @param LocalRepo $repo
84
	 * @param string|bool $timestamp MW_timestamp (optional)
85
	 *
86
	 * @return bool|OldLocalFile
87
	 */
88 View Code Duplication
	static function newFromKey( $sha1, $repo, $timestamp = false ) {
89
		$dbr = $repo->getSlaveDB();
90
91
		$conds = [ 'oi_sha1' => $sha1 ];
92
		if ( $timestamp ) {
93
			$conds['oi_timestamp'] = $dbr->timestamp( $timestamp );
94
		}
95
96
		$row = $dbr->selectRow( 'oldimage', self::selectFields(), $conds, __METHOD__ );
97
		if ( $row ) {
98
			return self::newFromRow( $row, $repo );
99
		} else {
100
			return false;
101
		}
102
	}
103
104
	/**
105
	 * Fields in the oldimage table
106
	 * @return array
107
	 */
108
	static function selectFields() {
109
		return [
110
			'oi_name',
111
			'oi_archive_name',
112
			'oi_size',
113
			'oi_width',
114
			'oi_height',
115
			'oi_metadata',
116
			'oi_bits',
117
			'oi_media_type',
118
			'oi_major_mime',
119
			'oi_minor_mime',
120
			'oi_description',
121
			'oi_user',
122
			'oi_user_text',
123
			'oi_timestamp',
124
			'oi_deleted',
125
			'oi_sha1',
126
		];
127
	}
128
129
	/**
130
	 * @param Title $title
131
	 * @param FileRepo $repo
132
	 * @param string $time Timestamp or null to load by archive name
133
	 * @param string $archiveName Archive name or null to load by timestamp
134
	 * @throws MWException
135
	 */
136
	function __construct( $title, $repo, $time, $archiveName ) {
137
		parent::__construct( $title, $repo );
138
		$this->requestedTime = $time;
139
		$this->archive_name = $archiveName;
140
		if ( is_null( $time ) && is_null( $archiveName ) ) {
141
			throw new MWException( __METHOD__ . ': must specify at least one of $time or $archiveName' );
142
		}
143
	}
144
145
	/**
146
	 * @return bool
147
	 */
148
	function getCacheKey() {
149
		return false;
150
	}
151
152
	/**
153
	 * @return string
154
	 */
155
	function getArchiveName() {
156
		if ( !isset( $this->archive_name ) ) {
157
			$this->load();
158
		}
159
160
		return $this->archive_name;
161
	}
162
163
	/**
164
	 * @return bool
165
	 */
166
	function isOld() {
167
		return true;
168
	}
169
170
	/**
171
	 * @return bool
172
	 */
173
	function isVisible() {
174
		return $this->exists() && !$this->isDeleted( File::DELETED_FILE );
175
	}
176
177
	function loadFromDB( $flags = 0 ) {
178
		$this->dataLoaded = true;
179
180
		$dbr = ( $flags & self::READ_LATEST )
181
			? $this->repo->getMasterDB()
182
			: $this->repo->getSlaveDB();
183
184
		$conds = [ 'oi_name' => $this->getName() ];
185 View Code Duplication
		if ( is_null( $this->requestedTime ) ) {
186
			$conds['oi_archive_name'] = $this->archive_name;
187
		} else {
188
			$conds['oi_timestamp'] = $dbr->timestamp( $this->requestedTime );
189
		}
190
		$row = $dbr->selectRow( 'oldimage', $this->getCacheFields( 'oi_' ),
191
			$conds, __METHOD__, [ 'ORDER BY' => 'oi_timestamp DESC' ] );
192
		if ( $row ) {
193
			$this->loadFromRow( $row, 'oi_' );
194
		} else {
195
			$this->fileExists = false;
196
		}
197
198
	}
199
200
	/**
201
	 * Load lazy file metadata from the DB
202
	 */
203
	protected function loadExtraFromDB() {
204
205
		$this->extraDataLoaded = true;
206
		$dbr = $this->repo->getSlaveDB();
207
		$conds = [ 'oi_name' => $this->getName() ];
208 View Code Duplication
		if ( is_null( $this->requestedTime ) ) {
209
			$conds['oi_archive_name'] = $this->archive_name;
210
		} else {
211
			$conds['oi_timestamp'] = $dbr->timestamp( $this->requestedTime );
212
		}
213
		// In theory the file could have just been renamed/deleted...oh well
214
		$row = $dbr->selectRow( 'oldimage', $this->getLazyCacheFields( 'oi_' ),
215
			$conds, __METHOD__, [ 'ORDER BY' => 'oi_timestamp DESC' ] );
216
217
		if ( !$row ) { // fallback to master
218
			$dbr = $this->repo->getMasterDB();
219
			$row = $dbr->selectRow( 'oldimage', $this->getLazyCacheFields( 'oi_' ),
220
				$conds, __METHOD__, [ 'ORDER BY' => 'oi_timestamp DESC' ] );
221
		}
222
223
		if ( $row ) {
224
			foreach ( $this->unprefixRow( $row, 'oi_' ) as $name => $value ) {
225
				$this->$name = $value;
226
			}
227
		} else {
228
			throw new MWException( "Could not find data for image '{$this->archive_name}'." );
229
		}
230
231
	}
232
233
	/**
234
	 * @param string $prefix
235
	 * @return array
236
	 */
237
	function getCacheFields( $prefix = 'img_' ) {
238
		$fields = parent::getCacheFields( $prefix );
239
		$fields[] = $prefix . 'archive_name';
240
		$fields[] = $prefix . 'deleted';
241
242
		return $fields;
243
	}
244
245
	/**
246
	 * @return string
247
	 */
248
	function getRel() {
249
		return 'archive/' . $this->getHashPath() . $this->getArchiveName();
250
	}
251
252
	/**
253
	 * @return string
254
	 */
255
	function getUrlRel() {
256
		return 'archive/' . $this->getHashPath() . rawurlencode( $this->getArchiveName() );
257
	}
258
259
	function upgradeRow() {
260
		$this->loadFromFile();
261
262
		# Don't destroy file info of missing files
263
		if ( !$this->fileExists ) {
264
			wfDebug( __METHOD__ . ": file does not exist, aborting\n" );
265
266
			return;
267
		}
268
269
		$dbw = $this->repo->getMasterDB();
270
		list( $major, $minor ) = self::splitMime( $this->mime );
271
272
		wfDebug( __METHOD__ . ': upgrading ' . $this->archive_name . " to the current schema\n" );
273
		$dbw->update( 'oldimage',
274
			[
275
				'oi_size' => $this->size, // sanity
276
				'oi_width' => $this->width,
277
				'oi_height' => $this->height,
278
				'oi_bits' => $this->bits,
279
				'oi_media_type' => $this->media_type,
280
				'oi_major_mime' => $major,
281
				'oi_minor_mime' => $minor,
282
				'oi_metadata' => $this->metadata,
283
				'oi_sha1' => $this->sha1,
284
			], [
285
				'oi_name' => $this->getName(),
286
				'oi_archive_name' => $this->archive_name ],
287
			__METHOD__
288
		);
289
	}
290
291
	/**
292
	 * @param int $field One of DELETED_* bitfield constants for file or
293
	 *   revision rows
294
	 * @return bool
295
	 */
296
	function isDeleted( $field ) {
297
		$this->load();
298
299
		return ( $this->deleted & $field ) == $field;
300
	}
301
302
	/**
303
	 * Returns bitfield value
304
	 * @return int
305
	 */
306
	function getVisibility() {
307
		$this->load();
308
309
		return (int)$this->deleted;
310
	}
311
312
	/**
313
	 * Determine if the current user is allowed to view a particular
314
	 * field of this image file, if it's marked as deleted.
315
	 *
316
	 * @param int $field
317
	 * @param User|null $user User object to check, or null to use $wgUser
318
	 * @return bool
319
	 */
320
	function userCan( $field, User $user = null ) {
321
		$this->load();
322
323
		return Revision::userCanBitfield( $this->deleted, $field, $user );
324
	}
325
326
	/**
327
	 * Upload a file directly into archive. Generally for Special:Import.
328
	 *
329
	 * @param string $srcPath File system path of the source file
330
	 * @param string $archiveName Full archive name of the file, in the form
331
	 *   $timestamp!$filename, where $filename must match $this->getName()
332
	 * @param string $timestamp
333
	 * @param string $comment
334
	 * @param User $user
335
	 * @return Status
336
	 */
337
	function uploadOld( $srcPath, $archiveName, $timestamp, $comment, $user ) {
338
		$this->lock();
339
340
		$dstRel = 'archive/' . $this->getHashPath() . $archiveName;
341
		$status = $this->publishTo( $srcPath, $dstRel );
342
343
		if ( $status->isGood() ) {
344
			if ( !$this->recordOldUpload( $srcPath, $archiveName, $timestamp, $comment, $user ) ) {
345
				$status->fatal( 'filenotfound', $srcPath );
346
			}
347
		}
348
349
		$this->unlock();
350
351
		return $status;
352
	}
353
354
	/**
355
	 * Record a file upload in the oldimage table, without adding log entries.
356
	 *
357
	 * @param string $srcPath File system path to the source file
358
	 * @param string $archiveName The archive name of the file
359
	 * @param string $timestamp
360
	 * @param string $comment Upload comment
361
	 * @param User $user User who did this upload
362
	 * @return bool
363
	 */
364
	protected function recordOldUpload( $srcPath, $archiveName, $timestamp, $comment, $user ) {
365
		$dbw = $this->repo->getMasterDB();
0 ignored issues
show
The method getMasterDB does only exist in LocalRepo, but not in FileRepo and ForeignAPIRepo.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
366
367
		$dstPath = $this->repo->getZonePath( 'public' ) . '/' . $this->getRel();
368
		$props = $this->repo->getFileProps( $dstPath );
369
		if ( !$props['fileExists'] ) {
370
			return false;
371
		}
372
373
		$dbw->insert( 'oldimage',
374
			[
375
				'oi_name' => $this->getName(),
376
				'oi_archive_name' => $archiveName,
377
				'oi_size' => $props['size'],
378
				'oi_width' => intval( $props['width'] ),
379
				'oi_height' => intval( $props['height'] ),
380
				'oi_bits' => $props['bits'],
381
				'oi_timestamp' => $dbw->timestamp( $timestamp ),
382
				'oi_description' => $comment,
383
				'oi_user' => $user->getId(),
384
				'oi_user_text' => $user->getName(),
385
				'oi_metadata' => $props['metadata'],
386
				'oi_media_type' => $props['media_type'],
387
				'oi_major_mime' => $props['major_mime'],
388
				'oi_minor_mime' => $props['minor_mime'],
389
				'oi_sha1' => $props['sha1'],
390
			], __METHOD__
391
		);
392
393
		return true;
394
	}
395
396
	/**
397
	 * If archive name is an empty string, then file does not "exist"
398
	 *
399
	 * This is the case for a couple files on Wikimedia servers where
400
	 * the old version is "lost".
401
	 */
402
	public function exists() {
403
		$archiveName = $this->getArchiveName();
404
		if ( $archiveName === '' || !is_string( $archiveName ) ) {
405
			return false;
406
		}
407
		return parent::exists();
408
	}
409
}
410