Issues (4122)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/logging/LogEntry.php (5 issues)

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
 * Contain classes for dealing with individual log entries
4
 *
5
 * This is how I see the log system history:
6
 * - appending to plain wiki pages
7
 * - formatting log entries based on database fields
8
 * - user is now part of the action message
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License along
21
 * with this program; if not, write to the Free Software Foundation, Inc.,
22
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
 * http://www.gnu.org/copyleft/gpl.html
24
 *
25
 * @file
26
 * @author Niklas Laxström
27
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
28
 * @since 1.19
29
 */
30
31
/**
32
 * Interface for log entries. Every log entry has these methods.
33
 *
34
 * @since 1.19
35
 */
36
interface LogEntry {
37
38
	/**
39
	 * The main log type.
40
	 *
41
	 * @return string
42
	 */
43
	public function getType();
44
45
	/**
46
	 * The log subtype.
47
	 *
48
	 * @return string
49
	 */
50
	public function getSubtype();
51
52
	/**
53
	 * The full logtype in format maintype/subtype.
54
	 *
55
	 * @return string
56
	 */
57
	public function getFullType();
58
59
	/**
60
	 * Get the extra parameters stored for this message.
61
	 *
62
	 * @return array
63
	 */
64
	public function getParameters();
65
66
	/**
67
	 * Get the user for performed this action.
68
	 *
69
	 * @return User
70
	 */
71
	public function getPerformer();
72
73
	/**
74
	 * Get the target page of this action.
75
	 *
76
	 * @return Title
77
	 */
78
	public function getTarget();
79
80
	/**
81
	 * Get the timestamp when the action was executed.
82
	 *
83
	 * @return string
84
	 */
85
	public function getTimestamp();
86
87
	/**
88
	 * Get the user provided comment.
89
	 *
90
	 * @return string
91
	 */
92
	public function getComment();
93
94
	/**
95
	 * Get the access restriction.
96
	 *
97
	 * @return string
98
	 */
99
	public function getDeleted();
100
101
	/**
102
	 * @param int $field One of LogPage::DELETED_* bitfield constants
103
	 * @return bool
104
	 */
105
	public function isDeleted( $field );
106
}
107
108
/**
109
 * Extends the LogEntryInterface with some basic functionality
110
 *
111
 * @since 1.19
112
 */
113
abstract class LogEntryBase implements LogEntry {
114
115
	public function getFullType() {
116
		return $this->getType() . '/' . $this->getSubtype();
117
	}
118
119
	public function isDeleted( $field ) {
120
		return ( $this->getDeleted() & $field ) === $field;
121
	}
122
123
	/**
124
	 * Whether the parameters for this log are stored in new or
125
	 * old format.
126
	 *
127
	 * @return bool
128
	 */
129
	public function isLegacy() {
130
		return false;
131
	}
132
133
	/**
134
	 * Create a blob from a parameter array
135
	 *
136
	 * @since 1.26
137
	 * @param array $params
138
	 * @return string
139
	 */
140
	public static function makeParamBlob( $params ) {
141
		return serialize( (array)$params );
142
	}
143
144
	/**
145
	 * Extract a parameter array from a blob
146
	 *
147
	 * @since 1.26
148
	 * @param string $blob
149
	 * @return array
150
	 */
151
	public static function extractParams( $blob ) {
152
		return unserialize( $blob );
153
	}
154
}
155
156
/**
157
 * This class wraps around database result row.
158
 *
159
 * @since 1.19
160
 */
161
class DatabaseLogEntry extends LogEntryBase {
162
163
	/**
164
	 * Returns array of information that is needed for querying
165
	 * log entries. Array contains the following keys:
166
	 * tables, fields, conds, options and join_conds
167
	 *
168
	 * @return array
169
	 */
170
	public static function getSelectQueryData() {
171
		$tables = [ 'logging', 'user' ];
172
		$fields = [
173
			'log_id', 'log_type', 'log_action', 'log_timestamp',
174
			'log_user', 'log_user_text',
175
			'log_namespace', 'log_title', // unused log_page
176
			'log_comment', 'log_params', 'log_deleted',
177
			'user_id', 'user_name', 'user_editcount',
178
		];
179
180
		$joins = [
181
			// IPs don't have an entry in user table
182
			'user' => [ 'LEFT JOIN', 'log_user=user_id' ],
183
		];
184
185
		return [
186
			'tables' => $tables,
187
			'fields' => $fields,
188
			'conds' => [],
189
			'options' => [],
190
			'join_conds' => $joins,
191
		];
192
	}
193
194
	/**
195
	 * Constructs new LogEntry from database result row.
196
	 * Supports rows from both logging and recentchanges table.
197
	 *
198
	 * @param stdClass|array $row
199
	 * @return DatabaseLogEntry
200
	 */
201
	public static function newFromRow( $row ) {
202
		$row = (object)$row;
203
		if ( isset( $row->rc_logid ) ) {
204
			return new RCDatabaseLogEntry( $row );
205
		} else {
206
			return new self( $row );
207
		}
208
	}
209
210
	/** @var stdClass Database result row. */
211
	protected $row;
212
213
	/** @var User */
214
	protected $performer;
215
216
	/** @var array Parameters for log entry */
217
	protected $params;
218
219
	/** @var int A rev id associated to the log entry */
220
	protected $revId = null;
221
222
	/** @var bool Whether the parameters for this log entry are stored in new or old format. */
223
	protected $legacy;
224
225
	protected function __construct( $row ) {
226
		$this->row = $row;
227
	}
228
229
	/**
230
	 * Returns the unique database id.
231
	 *
232
	 * @return int
233
	 */
234
	public function getId() {
235
		return (int)$this->row->log_id;
236
	}
237
238
	/**
239
	 * Returns whatever is stored in the database field.
240
	 *
241
	 * @return string
242
	 */
243
	protected function getRawParameters() {
244
		return $this->row->log_params;
245
	}
246
247
	public function isLegacy() {
248
		// This extracts the property
249
		$this->getParameters();
250
		return $this->legacy;
251
	}
252
253
	public function getType() {
254
		return $this->row->log_type;
255
	}
256
257
	public function getSubtype() {
258
		return $this->row->log_action;
259
	}
260
261
	public function getParameters() {
262
		if ( !isset( $this->params ) ) {
263
			$blob = $this->getRawParameters();
264
			MediaWiki\suppressWarnings();
265
			$params = LogEntryBase::extractParams( $blob );
266
			MediaWiki\restoreWarnings();
267
			if ( $params !== false ) {
268
				$this->params = $params;
269
				$this->legacy = false;
270
			} else {
271
				$this->params = LogPage::extractParams( $blob );
272
				$this->legacy = true;
273
			}
274
275
			if ( isset( $this->params['associated_rev_id'] ) ) {
276
				$this->revId = $this->params['associated_rev_id'];
277
				unset( $this->params['associated_rev_id'] );
278
			}
279
		}
280
281
		return $this->params;
282
	}
283
284
	public function getAssociatedRevId() {
285
		// This extracts the property
286
		$this->getParameters();
287
		return $this->revId;
288
	}
289
290
	public function getPerformer() {
291
		if ( !$this->performer ) {
292
			$userId = (int)$this->row->log_user;
293 View Code Duplication
			if ( $userId !== 0 ) {
294
				// logged-in users
295
				if ( isset( $this->row->user_name ) ) {
296
					$this->performer = User::newFromRow( $this->row );
297
				} else {
298
					$this->performer = User::newFromId( $userId );
299
				}
300
			} else {
301
				// IP users
302
				$userText = $this->row->log_user_text;
303
				$this->performer = User::newFromName( $userText, false );
0 ignored issues
show
Documentation Bug introduced by
It seems like \User::newFromName($userText, false) can also be of type false. However, the property $performer is declared as type object<User>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
304
			}
305
		}
306
307
		return $this->performer;
308
	}
309
310
	public function getTarget() {
311
		$namespace = $this->row->log_namespace;
312
		$page = $this->row->log_title;
313
		$title = Title::makeTitle( $namespace, $page );
314
315
		return $title;
316
	}
317
318
	public function getTimestamp() {
319
		return wfTimestamp( TS_MW, $this->row->log_timestamp );
320
	}
321
322
	public function getComment() {
323
		return $this->row->log_comment;
324
	}
325
326
	public function getDeleted() {
327
		return $this->row->log_deleted;
328
	}
329
}
330
331
class RCDatabaseLogEntry extends DatabaseLogEntry {
332
333
	public function getId() {
334
		return $this->row->rc_logid;
335
	}
336
337
	protected function getRawParameters() {
338
		return $this->row->rc_params;
339
	}
340
341
	public function getAssociatedRevId() {
342
		return $this->row->rc_this_oldid;
343
	}
344
345
	public function getType() {
346
		return $this->row->rc_log_type;
347
	}
348
349
	public function getSubtype() {
350
		return $this->row->rc_log_action;
351
	}
352
353
	public function getPerformer() {
354 View Code Duplication
		if ( !$this->performer ) {
355
			$userId = (int)$this->row->rc_user;
356
			if ( $userId !== 0 ) {
357
				$this->performer = User::newFromId( $userId );
358
			} else {
359
				$userText = $this->row->rc_user_text;
360
				// Might be an IP, don't validate the username
361
				$this->performer = User::newFromName( $userText, false );
0 ignored issues
show
Documentation Bug introduced by
It seems like \User::newFromName($userText, false) can also be of type false. However, the property $performer is declared as type object<User>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
362
			}
363
		}
364
365
		return $this->performer;
366
	}
367
368
	public function getTarget() {
369
		$namespace = $this->row->rc_namespace;
370
		$page = $this->row->rc_title;
371
		$title = Title::makeTitle( $namespace, $page );
372
373
		return $title;
374
	}
375
376
	public function getTimestamp() {
377
		return wfTimestamp( TS_MW, $this->row->rc_timestamp );
378
	}
379
380
	public function getComment() {
381
		return $this->row->rc_comment;
382
	}
383
384
	public function getDeleted() {
385
		return $this->row->rc_deleted;
386
	}
387
}
388
389
/**
390
 * Class for creating log entries manually, to inject them into the database.
391
 *
392
 * @since 1.19
393
 */
394
class ManualLogEntry extends LogEntryBase {
395
	/** @var string Type of log entry */
396
	protected $type;
397
398
	/** @var string Sub type of log entry */
399
	protected $subtype;
400
401
	/** @var array Parameters for log entry */
402
	protected $parameters = [];
403
404
	/** @var array */
405
	protected $relations = [];
406
407
	/** @var User Performer of the action for the log entry */
408
	protected $performer;
409
410
	/** @var Title Target title for the log entry */
411
	protected $target;
412
413
	/** @var string Timestamp of creation of the log entry */
414
	protected $timestamp;
415
416
	/** @var string Comment for the log entry */
417
	protected $comment = '';
418
419
	/** @var int A rev id associated to the log entry */
420
	protected $revId = 0;
421
422
	/** @var array Change tags add to the log entry */
423
	protected $tags = null;
424
425
	/** @var int Deletion state of the log entry */
426
	protected $deleted;
427
428
	/** @var int ID of the log entry */
429
	protected $id;
430
431
	/** @var Can this log entry be patrolled? */
432
	protected $isPatrollable = false;
433
434
	/** @var bool Whether this is a legacy log entry */
435
	protected $legacy = false;
436
437
	/**
438
	 * Constructor.
439
	 *
440
	 * @since 1.19
441
	 * @param string $type
442
	 * @param string $subtype
443
	 */
444
	public function __construct( $type, $subtype ) {
445
		$this->type = $type;
446
		$this->subtype = $subtype;
447
	}
448
449
	/**
450
	 * Set extra log parameters.
451
	 *
452
	 * You can pass params to the log action message by prefixing the keys with
453
	 * a number and optional type, using colons to separate the fields. The
454
	 * numbering should start with number 4, the first three parameters are
455
	 * hardcoded for every message.
456
	 *
457
	 * If you want to store stuff that should not be available in messages, don't
458
	 * prefix the array key with a number and don't use the colons.
459
	 *
460
	 * Example:
461
	 *   $entry->setParameters(
462
	 *     '4::color' => 'blue',
463
	 *     '5:number:count' => 3000,
464
	 *     'animal' => 'dog'
465
	 *   );
466
	 *
467
	 * @since 1.19
468
	 * @param array $parameters Associative array
469
	 */
470
	public function setParameters( $parameters ) {
471
		$this->parameters = $parameters;
472
	}
473
474
	/**
475
	 * Declare arbitrary tag/value relations to this log entry.
476
	 * These can be used to filter log entries later on.
477
	 *
478
	 * @param array $relations Map of (tag => (list of values|value))
479
	 * @since 1.22
480
	 */
481
	public function setRelations( array $relations ) {
482
		$this->relations = $relations;
483
	}
484
485
	/**
486
	 * Set the user that performed the action being logged.
487
	 *
488
	 * @since 1.19
489
	 * @param User $performer
490
	 */
491
	public function setPerformer( User $performer ) {
492
		$this->performer = $performer;
493
	}
494
495
	/**
496
	 * Set the title of the object changed.
497
	 *
498
	 * @since 1.19
499
	 * @param Title $target
500
	 */
501
	public function setTarget( Title $target ) {
502
		$this->target = $target;
503
	}
504
505
	/**
506
	 * Set the timestamp of when the logged action took place.
507
	 *
508
	 * @since 1.19
509
	 * @param string $timestamp
510
	 */
511
	public function setTimestamp( $timestamp ) {
512
		$this->timestamp = $timestamp;
513
	}
514
515
	/**
516
	 * Set a comment associated with the action being logged.
517
	 *
518
	 * @since 1.19
519
	 * @param string $comment
520
	 */
521
	public function setComment( $comment ) {
522
		$this->comment = $comment;
523
	}
524
525
	/**
526
	 * Set an associated revision id.
527
	 *
528
	 * For example, the ID of the revision that was inserted to mark a page move
529
	 * or protection, file upload, etc.
530
	 *
531
	 * @since 1.27
532
	 * @param int $revId
533
	 */
534
	public function setAssociatedRevId( $revId ) {
535
		$this->revId = $revId;
536
	}
537
538
	/**
539
	 * Set change tags for the log entry.
540
	 *
541
	 * @since 1.27
542
	 * @param string|string[] $tags
543
	 */
544
	public function setTags( $tags ) {
545
		if ( is_string( $tags ) ) {
546
			$tags = [ $tags ];
547
		}
548
		$this->tags = $tags;
549
	}
550
551
	/**
552
	 * Set whether this log entry should be made patrollable
553
	 * This shouldn't depend on config, only on whether there is full support
554
	 * in the software for patrolling this log entry.
555
	 * False by default
556
	 *
557
	 * @since 1.27
558
	 * @param bool $patrollable
559
	 */
560
	public function setIsPatrollable( $patrollable ) {
561
		$this->isPatrollable = (bool)$patrollable;
0 ignored issues
show
Documentation Bug introduced by
It seems like (bool) $patrollable of type boolean is incompatible with the declared type object<Can> of property $isPatrollable.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
562
	}
563
564
	/**
565
	 * Set the 'legacy' flag
566
	 *
567
	 * @since 1.25
568
	 * @param bool $legacy
569
	 */
570
	public function setLegacy( $legacy ) {
571
		$this->legacy = $legacy;
572
	}
573
574
	/**
575
	 * Set the 'deleted' flag.
576
	 *
577
	 * @since 1.19
578
	 * @param int $deleted One of LogPage::DELETED_* bitfield constants
579
	 */
580
	public function setDeleted( $deleted ) {
581
		$this->deleted = $deleted;
582
	}
583
584
	/**
585
	 * Insert the entry into the `logging` table.
586
	 *
587
	 * @param IDatabase $dbw
588
	 * @return int ID of the log entry
589
	 * @throws MWException
590
	 */
591
	public function insert( IDatabase $dbw = null ) {
592
		global $wgContLang;
593
594
		$dbw = $dbw ?: wfGetDB( DB_MASTER );
595
		$id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
596
597
		if ( $this->timestamp === null ) {
598
			$this->timestamp = wfTimestampNow();
0 ignored issues
show
Documentation Bug introduced by
It seems like wfTimestampNow() can also be of type false. However, the property $timestamp is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
599
		}
600
601
		// Trim spaces on user supplied text
602
		$comment = trim( $this->getComment() );
603
604
		// Truncate for whole multibyte characters.
605
		$comment = $wgContLang->truncate( $comment, 255 );
606
607
		$params = $this->getParameters();
608
		$relations = $this->relations;
609
610
		// Additional fields for which there's no space in the database table schema
611
		$revId = $this->getAssociatedRevId();
612
		if ( $revId ) {
613
			$params['associated_rev_id'] = $revId;
614
			$relations['associated_rev_id'] = $revId;
615
		}
616
617
		$data = [
618
			'log_id' => $id,
619
			'log_type' => $this->getType(),
620
			'log_action' => $this->getSubtype(),
621
			'log_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
622
			'log_user' => $this->getPerformer()->getId(),
623
			'log_user_text' => $this->getPerformer()->getName(),
624
			'log_namespace' => $this->getTarget()->getNamespace(),
625
			'log_title' => $this->getTarget()->getDBkey(),
626
			'log_page' => $this->getTarget()->getArticleID(),
627
			'log_comment' => $comment,
628
			'log_params' => LogEntryBase::makeParamBlob( $params ),
629
		];
630
		if ( isset( $this->deleted ) ) {
631
			$data['log_deleted'] = $this->deleted;
632
		}
633
634
		$dbw->insert( 'logging', $data, __METHOD__ );
635
		$this->id = !is_null( $id ) ? $id : $dbw->insertId();
636
637
		$rows = [];
638
		foreach ( $relations as $tag => $values ) {
639
			if ( !strlen( $tag ) ) {
640
				throw new MWException( "Got empty log search tag." );
641
			}
642
643
			if ( !is_array( $values ) ) {
644
				$values = [ $values ];
645
			}
646
647 View Code Duplication
			foreach ( $values as $value ) {
648
				$rows[] = [
649
					'ls_field' => $tag,
650
					'ls_value' => $value,
651
					'ls_log_id' => $this->id
652
				];
653
			}
654
		}
655
		if ( count( $rows ) ) {
656
			$dbw->insert( 'log_search', $rows, __METHOD__, 'IGNORE' );
657
		}
658
659
		return $this->id;
660
	}
661
662
	/**
663
	 * Get a RecentChanges object for the log entry
664
	 *
665
	 * @param int $newId
666
	 * @return RecentChange
667
	 * @since 1.23
668
	 */
669
	public function getRecentChange( $newId = 0 ) {
670
		$formatter = LogFormatter::newFromEntry( $this );
671
		$context = RequestContext::newExtraneousContext( $this->getTarget() );
672
		$formatter->setContext( $context );
673
674
		$logpage = SpecialPage::getTitleFor( 'Log', $this->getType() );
675
		$user = $this->getPerformer();
676
		$ip = "";
677
		if ( $user->isAnon() ) {
678
			// "MediaWiki default" and friends may have
679
			// no IP address in their name
680
			if ( IP::isIPAddress( $user->getName() ) ) {
681
				$ip = $user->getName();
682
			}
683
		}
684
685
		return RecentChange::newLogEntry(
686
			$this->getTimestamp(),
0 ignored issues
show
It seems like $this->getTimestamp() targeting ManualLogEntry::getTimestamp() can also be of type false; however, RecentChange::newLogEntry() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
687
			$logpage,
688
			$user,
689
			$formatter->getPlainActionText(),
690
			$ip,
691
			$this->getType(),
692
			$this->getSubtype(),
693
			$this->getTarget(),
694
			$this->getComment(),
695
			LogEntryBase::makeParamBlob( $this->getParameters() ),
696
			$newId,
697
			$formatter->getIRCActionComment(), // Used for IRC feeds
698
			$this->getAssociatedRevId(), // Used for e.g. moves and uploads
699
			$this->getIsPatrollable()
700
		);
701
	}
702
703
	/**
704
	 * Publish the log entry.
705
	 *
706
	 * @param int $newId Id of the log entry.
707
	 * @param string $to One of: rcandudp (default), rc, udp
708
	 */
709
	public function publish( $newId, $to = 'rcandudp' ) {
710
		DeferredUpdates::addCallableUpdate(
711
			function () use ( $newId, $to ) {
712
				$log = new LogPage( $this->getType() );
713
				if ( !$log->isRestricted() ) {
714
					$rc = $this->getRecentChange( $newId );
715
716
					if ( $to === 'rc' || $to === 'rcandudp' ) {
717
						// save RC, passing tags so they are applied there
718
						$tags = $this->getTags();
719
						if ( is_null( $tags ) ) {
720
							$tags = [];
721
						}
722
						$rc->addTags( $tags );
723
						$rc->save( 'pleasedontudp' );
724
					}
725
726
					if ( $to === 'udp' || $to === 'rcandudp' ) {
727
						$rc->notifyRCFeeds();
728
					}
729
730
					// Log the autopatrol if the log entry is patrollable
731
					if ( $this->getIsPatrollable() &&
732
						$rc->getAttribute( 'rc_patrolled' ) === 1
733
					) {
734
						PatrolLog::record( $rc, true, $this->getPerformer() );
735
					}
736
				}
737
			},
738
			DeferredUpdates::POSTSEND,
739
			wfGetDB( DB_MASTER )
740
		);
741
	}
742
743
	public function getType() {
744
		return $this->type;
745
	}
746
747
	public function getSubtype() {
748
		return $this->subtype;
749
	}
750
751
	public function getParameters() {
752
		return $this->parameters;
753
	}
754
755
	/**
756
	 * @return User
757
	 */
758
	public function getPerformer() {
759
		return $this->performer;
760
	}
761
762
	/**
763
	 * @return Title
764
	 */
765
	public function getTarget() {
766
		return $this->target;
767
	}
768
769
	public function getTimestamp() {
770
		$ts = $this->timestamp !== null ? $this->timestamp : wfTimestampNow();
771
772
		return wfTimestamp( TS_MW, $ts );
773
	}
774
775
	public function getComment() {
776
		return $this->comment;
777
	}
778
779
	/**
780
	 * @since 1.27
781
	 * @return int
782
	 */
783
	public function getAssociatedRevId() {
784
		return $this->revId;
785
	}
786
787
	/**
788
	 * @since 1.27
789
	 * @return array
790
	 */
791
	public function getTags() {
792
		return $this->tags;
793
	}
794
795
	/**
796
	 * Whether this log entry is patrollable
797
	 *
798
	 * @since 1.27
799
	 * @return bool
800
	 */
801
	public function getIsPatrollable() {
802
		return $this->isPatrollable;
803
	}
804
805
	/**
806
	 * @since 1.25
807
	 * @return bool
808
	 */
809
	public function isLegacy() {
810
		return $this->legacy;
811
	}
812
813
	public function getDeleted() {
814
		return (int)$this->deleted;
815
	}
816
}
817