Completed
Push — master ( 043426...79cf54 )
by mw
138:09 queued 103:23
created

PageUpdater::doPurgeParserCache()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 14
nc 5
nop 0
dl 0
loc 25
ccs 0
cts 0
cp 0
crap 30
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
namespace SMW\MediaWiki;
4
5
use Title;
6
use Psr\Log\LoggerInterface;
7
use Psr\Log\LoggerAwareInterface;
8
use SMW\Utils\Timer;
9
use DeferrableUpdate;
10
use DeferredUpdates;
11
12
/**
13
 * @license GNU GPL v2+
14
 * @since 2.1
15
 *
16
 * @author mwjames
17
 */
18
class PageUpdater implements LoggerAwareInterface, DeferrableUpdate {
19
20
	/**
21
	 * @var Database
22
	 */
23
	private $connection;
24
25 268
	/**
26 268
	 * @var Title[]
27 268
	 */
28
	private $titles = array();
29
30
	/**
31
	 * LoggerInterface
32
	 */
33
	private $logger;
34
35
	/**
36
	 * @var string
37
	 */
38
	private $origin = '';
39
40
	/**
41 264
	 * @var boolean
42 264
	 */
43
	private $isCommandLineMode = false;
44
45
	/**
46
	 * @var boolean
47
	 */
48 266
	private $isHtmlCacheUpdate = true;
49 266
50 266
	/**
51
	 * @var boolean
52 266
	 */
53
	private $onTransactionIdle = false;
54
55
	/**
56
	 * @var boolean
57 264
	 */
58 264
	private $asPoolPurge = false;
59 264
60
	/**
61
	 * @var boolean
62 264
	 */
63
	private $isPending = false;
64 264
65
	/**
66
	 * @var array
67
	 */
68
	private $pendingUpdates = array();
69 1
70 1
	/**
71 1
	 * @since 2.5
72
	 *
73 1
	 * @param Database|null $connection
74
	 */
75
	public function __construct( Database $connection = null ) {
76
		$this->connection = $connection;
77
	}
78
79
	/**
80
	 * @see LoggerAwareInterface::setLogger
81
	 *
82
	 * @since 2.5
83
	 *
84
	 * @param LoggerInterface $logger
85
	 */
86
	public function setLogger( LoggerInterface $logger ) {
87
		$this->logger = $logger;
88
	}
89
90
	/**
91
	 * @since 3.0
92
	 *
93
	 * @param string $origin
94
	 */
95
	public function setOrigin( $origin ) {
96
		$this->origin = $origin;
97
	}
98
99
	/**
100
	 * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode
101
	 * Indicates whether MW is running in command-line mode or not.
102
	 *
103
	 * @since 2.5
104
	 *
105
	 * @param boolean $isCommandLineMode
106
	 */
107
	public function isCommandLineMode( $isCommandLineMode ) {
108
		$this->isCommandLineMode = $isCommandLineMode;
109
	}
110
111
	/**
112
	 * @since 3.0
113
	 *
114
	 * @param boolean $isHtmlCacheUpdate
115
	 */
116
	public function isHtmlCacheUpdate( $isHtmlCacheUpdate ) {
117
		$this->isHtmlCacheUpdate = $isHtmlCacheUpdate;
118
	}
119
120
	/**
121
	 * @since 3.0
122
	 *
123
	 * @param booloan $isPending
0 ignored issues
show
Bug introduced by
There is no parameter named $isPending. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
124
	 */
125
	public function markAsPending() {
126
		$this->isPending = true;
127
	}
128
129
	/**
130
	 * @since 2.1
131
	 *
132
	 * @param Title|null $title
133
	 */
134
	public function addPage( Title $title = null ) {
135
136
		if ( $title === null ) {
137
			return;
138
		}
139
140
		$this->titles[$title->getDBKey()] = $title;
141
	}
142
143
	/**
144
	 * @note MW 1.29+ runs Title::invalidateCache in AutoCommitUpdate which has
145
	 * been shown to cause transaction issues when executed while a transaction
146
	 * hasn't finished therefore use 'onTransactionIdle' to isolate the
147
	 * execution.
148
	 *
149
	 * @since 2.5
150
	 */
151
	public function waitOnTransactionIdle() {
152
153
		if ( $this->connection === null ) {
154
			$this->log( __METHOD__ . ' is missing an active connection therefore `onTransactionIdle` cannot be used.' );
155
			return $this->onTransactionIdle = false;
156
		}
157
158
		$this->onTransactionIdle = !$this->isCommandLineMode;
159
	}
160
161
	/**
162
	 * Controls the purge to use a direct DB access to make changes to avoid
163
	 * racing conditions for a large number of title entities.
164
	 *
165
	 * @since 3.0
166
	 */
167
	public function asPoolPurge() {
168
169
		if ( $this->connection === null ) {
170
			return;
171
		}
172
173
		$this->onTransactionIdle = true;
174
		$this->asPoolPurge = true;
175
	}
176
177
	/**
178
	 * @since 2.1
179
	 */
180
	public function clear() {
181
		$this->titles = array();
182
	}
183
184
	/**
185
	 * @since 2.1
186
	 *
187
	 * @return boolean
188
	 */
189
	public function canUpdate() {
190
		return !wfReadOnly();
191
	}
192
193
	/**
194
	 * Push updates to be either deferred or direct pending the setting invoked
195
	 * by PageUPdater::markAsPending.
196
	 *
197
	 * @since 3.0
198
	 */
199
	public function pushUpdate() {
200
201
		if ( !$this->isPending || $this->isCommandLineMode === true ) {
202
			return $this->doUpdate();
203
		}
204
205
		$this->log( __METHOD__ . " $this->origin (as DeferrableUpdate)" );
206
207
		if ( $this->onTransactionIdle ) {
208
			$this->connection->onTransactionIdle( function () {
209
				DeferredUpdates::addUpdate( $this );
210
			} );
211
		} else{
212
			DeferredUpdates::addUpdate( $this );
213
		}
214
	}
215
216
	/**
217
	 * @see DeferrableUpdate::doUpdate
218
	 *
219
	 * @since 3.0
220
	 */
221
	public function doUpdate() {
222
		$this->isPending = false;
223
224
		foreach ( array_keys( $this->pendingUpdates ) as $update ) {
225
			call_user_func( [ $this, $update ] );
226
		}
227
228
		$this->pendingUpdates = array();
229
	}
230
231
	/**
232
	 * @since 2.1
233
	 */
234
	public function doPurgeParserCache() {
235
236
		$method = __METHOD__;
237
238
		if ( $this->isPending ) {
239
			return $this->pendingUpdates['doPurgeParserCache'] = true;
240
		}
241
242
		if ( $this->onTransactionIdle ) {
243
			return $this->connection->onTransactionIdle( function () use( $method ) {
244
				$this->log( $method . ' (onTransactionIdle)' );
245
				$this->onTransactionIdle = false;
246
				$this->doPurgeParserCache();
247
				$this->onTransactionIdle = true;
248
			} );
249
		}
250
251
		if ( $this->asPoolPurge ) {
252
			return $this->doPoolPurge();
253
		}
254
255
		foreach ( $this->titles as $title ) {
256
			$title->invalidateCache();
257
		}
258
	}
259
260
	/**
261
	 * @since 2.1
262
	 */
263
	public function doPurgeHtmlCache() {
264
265
		if ( $this->isHtmlCacheUpdate === false ) {
266
			return;
267
		}
268
269
		if ( $this->isPending ) {
270
			return $this->pendingUpdates['doPurgeHtmlCache'] = true;
271
		}
272
273
		$method = __METHOD__;
274
275
		if ( $this->onTransactionIdle ) {
276
			return $this->connection->onTransactionIdle( function () use ( $method ) {
277
				$this->log( $method . ' (onTransactionIdle)' );
278
				$this->onTransactionIdle = false;
279
				$this->doPurgeHtmlCache();
280
				$this->onTransactionIdle = true;
281
			} );
282
		}
283
284
		// Calls HTMLCacheUpdate, HTMLCacheUpdateJob including HTMLFileCache,
285
		// CdnCacheUpdate
286
		foreach ( $this->titles as $title ) {
287
			$title->touchLinks();
288
		}
289
	}
290
291
	/**
292
	 * @since 2.1
293
	 */
294
	public function doPurgeWebCache() {
295
296
		$method = __METHOD__;
297
298
		if ( $this->isPending ) {
299
			return $this->pendingUpdates['doPurgeWebCache'] = true;
300
		}
301
302
		if ( $this->onTransactionIdle ) {
303
			return $this->connection->onTransactionIdle( function () use ( $method ) {
304
				$this->log( $method . ' (onTransactionIdle)' );
305
				$this->onTransactionIdle = false;
306
				$this->doPurgeWebCache();
307
				$this->onTransactionIdle = true;
308
			} );
309
		}
310
311
		foreach ( $this->titles as $title ) {
312
			$title->purgeSquid();
313
		}
314
	}
315
316
	/**
317
	 * Copied from PurgeJobUtils to avoid the AutoCommitUpdate from
318
	 * Title::invalidateCache introduced with MW 1.28/1.29 on a large update pool
319
	 */
320
	private function doPoolPurge() {
321
322
		Timer::start( __METHOD__ );
323
324
		// Required due to postgres and "Error: 22007 ERROR:  invalid input
325
		// syntax for type timestamp with time zone: "20170408113703""
326
		$now = $this->connection->timestamp();
327
		$res = $this->connection->select(
328
			'page',
329
			'page_id',
330
			[
0 ignored issues
show
Documentation introduced by
array('page_title' => ar...ction->addQuotes($now)) is of type array<string|integer,arr...nteger>","0":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
331
				'page_title' => array_keys( $this->titles ),
332
				'page_touched < ' . $this->connection->addQuotes( $now )
333
			],
334
			__METHOD__
335
		);
336
337
		if ( $res === false ) {
338
			return;
339
		}
340
341
		$ids = [];
342
343
		foreach ( $res as $row ) {
344
			$ids[] = $row->page_id;
345
		}
346
347
		if ( $ids === array() ) {
348
			return;
349
		}
350
351
		$this->connection->update(
352
			'page',
353
			[ 'page_touched' => $now ],
354
			[
355
				'page_id' => $ids,
356
				'page_touched < ' . $this->connection->addQuotes( $now )
357
			],
358
			__METHOD__
359
		);
360
361
		$this->log( __METHOD__ . ' (procTime in sec: ' . Timer::getElapsedTime( __METHOD__, 7 ) . ')' );
362
	}
363
364
	private function log( $message, $context = array() ) {
365
366
		if ( $this->logger === null ) {
367
			return;
368
		}
369
370
		$this->logger->info( $message, $context );
371
	}
372
373
}
374