Completed
Branch master (e2eefa)
by
unknown
25:58
created

TextPassDumper   F

Complexity

Total Complexity 143

Size/Duplication

Total Lines 951
Duplicated Lines 2.42 %

Coupling/Cohesion

Components 1
Dependencies 12
Metric Value
dl 23
loc 951
rs 3.4285
wmc 143
lcom 1
cbo 12

23 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 35 2
A execute() 0 4 1
F processOptions() 0 43 10
C rotateDb() 0 39 7
A initProgress() 0 4 1
B dump() 0 40 4
C processFileOpt() 0 31 7
B showReport() 0 59 7
A setTimeExceeded() 0 3 1
A checkIfTimeExceeded() 0 9 3
C finalOptionCheck() 0 23 9
A exportTransform() 0 14 2
A getTextSpawned() 0 11 2
B closeSpawn() 0 20 5
C getTextSpawnedOnce() 0 61 10
B endElement() 5 59 8
C characterData() 0 24 8
A clearOpenElement() 0 6 2
C readDump() 5 75 10
F getText() 13 161 29
A getTextDb() 0 18 3
B openSpawn() 0 39 3
D startElement() 0 36 9

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like TextPassDumper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TextPassDumper, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 33 and the first side effect is on line 27.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * BackupDumper that postprocesses XML dumps from dumpBackup.php to add page text
4
 *
5
 * Copyright (C) 2005 Brion Vibber <[email protected]>
6
 * https://www.mediawiki.org/
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License along
19
 * with this program; if not, write to the Free Software Foundation, Inc.,
20
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
 * http://www.gnu.org/copyleft/gpl.html
22
 *
23
 * @file
24
 * @ingroup Maintenance
25
 */
26
27
require_once __DIR__ . '/backup.inc';
28
require_once __DIR__ . '/../includes/export/WikiExporter.php';
29
30
/**
31
 * @ingroup Maintenance
32
 */
33
class TextPassDumper extends BackupDumper {
34
	public $prefetch = null;
35
36
	// when we spend more than maxTimeAllowed seconds on this run, we continue
37
	// processing until we write out the next complete page, then save output file(s),
38
	// rename it/them and open new one(s)
39
	public $maxTimeAllowed = 0; // 0 = no limit
40
41
	protected $input = "php://stdin";
42
	protected $history = WikiExporter::FULL;
43
	protected $fetchCount = 0;
44
	protected $prefetchCount = 0;
45
	protected $prefetchCountLast = 0;
46
	protected $fetchCountLast = 0;
47
48
	protected $maxFailures = 5;
49
	protected $maxConsecutiveFailedTextRetrievals = 200;
50
	protected $failureTimeout = 5; // Seconds to sleep after db failure
51
52
	protected $bufferSize = 524288; // In bytes. Maximum size to read from the stub in on go.
53
54
	protected $php = "php";
55
	protected $spawn = false;
56
57
	/**
58
	 * @var bool|resource
59
	 */
60
	protected $spawnProc = false;
61
62
	/**
63
	 * @var bool|resource
64
	 */
65
	protected $spawnWrite = false;
66
67
	/**
68
	 * @var bool|resource
69
	 */
70
	protected $spawnRead = false;
71
72
	/**
73
	 * @var bool|resource
74
	 */
75
	protected $spawnErr = false;
76
77
	protected $xmlwriterobj = false;
78
79
	protected $timeExceeded = false;
80
	protected $firstPageWritten = false;
81
	protected $lastPageWritten = false;
82
	protected $checkpointJustWritten = false;
83
	protected $checkpointFiles = [];
84
85
	/**
86
	 * @var DatabaseBase
87
	 */
88
	protected $db;
89
90
	/**
91
	 * @param array $args For backward compatibility
92
	 */
93
	function __construct( $args = null ) {
94
		parent::__construct();
95
96
		$this->addDescription( <<<TEXT
97
This script postprocesses XML dumps from dumpBackup.php to add
98
page text which was stubbed out (using --stub).
99
100
XML input is accepted on stdin.
101
XML output is sent to stdout; progress reports are sent to stderr.
102
TEXT
103
		);
104
		$this->stderr = fopen( "php://stderr", "wt" );
0 ignored issues
show
Bug introduced by
The property stderr does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
105
106
		$this->addOption( 'stub', 'To load a compressed stub dump instead of stdin. ' .
107
			'Specify as --stub=<type>:<file>.', false, true );
108
		$this->addOption( 'prefetch', 'Use a prior dump file as a text source, to savepressure on the ' .
109
			'database. (Requires the XMLReader extension). Specify as --prefetch=<type>:<file>',
110
			false, true );
111
		$this->addOption( 'maxtime', 'Write out checkpoint file after this many minutes (writing' .
112
			'out complete page, closing xml file properly, and opening new one' .
113
			'with header).  This option requires the checkpointfile option.', false, true );
114
		$this->addOption( 'checkpointfile', 'Use this string for checkpoint filenames,substituting ' .
115
			'first pageid written for the first %s (required) and the last pageid written for the ' .
116
			'second %s if it exists.', false, true, false, true ); // This can be specified multiple times
117
		$this->addOption( 'quiet', 'Don\'t dump status reports to stderr.' );
118
		$this->addOption( 'current', 'Base ETA on number of pages in database instead of all revisions' );
119
		$this->addOption( 'spawn', 'Spawn a subprocess for loading text records' );
120
		$this->addOption( 'buffersize', 'Buffer size in bytes to use for reading the stub. ' .
121
			'(Default: 512KB, Minimum: 4KB)', false, true );
122
123
		if ( $args ) {
124
			$this->loadWithArgv( $args );
125
			$this->processOptions();
126
		}
127
	}
128
129
	function execute() {
130
		$this->processOptions();
131
		$this->dump( true );
132
	}
133
134
	function processOptions() {
135
		global $IP;
136
137
		parent::processOptions();
138
139
		if ( $this->hasOption( 'buffersize' ) ) {
140
			$this->bufferSize = max( intval( $this->getOption( 'buffersize' ) ), 4 * 1024 );
141
		}
142
143
		if ( $this->hasOption( 'prefetch' ) ) {
144
			require_once "$IP/maintenance/backupPrefetch.inc";
145
			$url = $this->processFileOpt( $this->getOption( 'prefetch' ) );
146
			$this->prefetch = new BaseDump( $url );
147
		}
148
149
		if ( $this->hasOption( 'stub' ) ) {
150
			$this->input = $this->processFileOpt( $this->getOption( 'stub' ) );
151
		}
152
153
		if ( $this->hasOption( 'maxtime' ) ) {
154
			$this->maxTimeAllowed = intval( $this->getOption( 'maxtime' ) ) * 60;
155
		}
156
157
		if ( $this->hasOption( 'checkpointfile' ) ) {
158
			$this->checkpointFiles = $this->getOption( 'checkpointfile' );
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getOption('checkpointfile') of type * is incompatible with the declared type array of property $checkpointFiles.

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...
159
		}
160
161
		if ( $this->hasOption( 'current' ) ) {
162
			$this->history = WikiExporter::CURRENT;
163
		}
164
165
		if ( $this->hasOption( 'full' ) ) {
166
			$this->history = WikiExporter::FULL;
167
		}
168
169
		if ( $this->hasOption( 'spawn' ) ) {
170
			$this->spawn = true;
171
			$val = $this->getOption( 'spawn' );
172
			if ( $val !== 1 ) {
173
				$this->php = $val;
174
			}
175
		}
176
	}
177
178
	/**
179
	 * Drop the database connection $this->db and try to get a new one.
180
	 *
181
	 * This function tries to get a /different/ connection if this is
182
	 * possible. Hence, (if this is possible) it switches to a different
183
	 * failover upon each call.
184
	 *
185
	 * This function resets $this->lb and closes all connections on it.
186
	 *
187
	 * @throws MWException
188
	 */
189
	function rotateDb() {
190
		// Cleaning up old connections
191
		if ( isset( $this->lb ) ) {
192
			$this->lb->closeAll();
193
			unset( $this->lb );
194
		}
195
196
		if ( $this->forcedDb !== null ) {
197
			$this->db = $this->forcedDb;
198
199
			return;
200
		}
201
202
		if ( isset( $this->db ) && $this->db->isOpen() ) {
203
			throw new MWException( 'DB is set and has not been closed by the Load Balancer' );
204
		}
205
206
		unset( $this->db );
207
208
		// Trying to set up new connection.
209
		// We do /not/ retry upon failure, but delegate to encapsulating logic, to avoid
210
		// individually retrying at different layers of code.
211
212
		// 1. The LoadBalancer.
213
		try {
214
			$this->lb = wfGetLBFactory()->newMainLB();
0 ignored issues
show
Deprecated Code introduced by
The function wfGetLBFactory() has been deprecated with message: since 1.27, use MediaWikiServices::getDBLoadBalancerFactory() instead.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
215
		} catch ( Exception $e ) {
216
			throw new MWException( __METHOD__
217
				. " rotating DB failed to obtain new load balancer (" . $e->getMessage() . ")" );
218
		}
219
220
		// 2. The Connection, through the load balancer.
221
		try {
222
			$this->db = $this->lb->getConnection( DB_SLAVE, 'dump' );
223
		} catch ( Exception $e ) {
224
			throw new MWException( __METHOD__
225
				. " rotating DB failed to obtain new database (" . $e->getMessage() . ")" );
226
		}
227
	}
228
229
	function initProgress( $history = WikiExporter::FULL ) {
230
		parent::initProgress();
231
		$this->timeOfCheckpoint = $this->startTime;
0 ignored issues
show
Bug introduced by
The property timeOfCheckpoint does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property startTime does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
232
	}
233
234
	function dump( $history, $text = WikiExporter::TEXT ) {
235
		// Notice messages will foul up your XML output even if they're
236
		// relatively harmless.
237
		if ( ini_get( 'display_errors' ) ) {
238
			ini_set( 'display_errors', 'stderr' );
239
		}
240
241
		$this->initProgress( $this->history );
242
243
		// We are trying to get an initial database connection to avoid that the
244
		// first try of this request's first call to getText fails. However, if
245
		// obtaining a good DB connection fails it's not a serious issue, as
246
		// getText does retry upon failure and can start without having a working
247
		// DB connection.
248
		try {
249
			$this->rotateDb();
250
		} catch ( Exception $e ) {
251
			// We do not even count this as failure. Just let eventual
252
			// watchdogs know.
253
			$this->progress( "Getting initial DB connection failed (" .
254
				$e->getMessage() . ")" );
255
		}
256
257
		$this->egress = new ExportProgressFilter( $this->sink, $this );
0 ignored issues
show
Bug introduced by
The property egress does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
258
259
		// it would be nice to do it in the constructor, oh well. need egress set
260
		$this->finalOptionCheck();
261
262
		// we only want this so we know how to close a stream :-P
263
		$this->xmlwriterobj = new XmlDumpWriter();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \XmlDumpWriter() of type object<XmlDumpWriter> is incompatible with the declared type boolean of property $xmlwriterobj.

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...
264
265
		$input = fopen( $this->input, "rt" );
266
		$this->readDump( $input );
267
268
		if ( $this->spawnProc ) {
269
			$this->closeSpawn();
270
		}
271
272
		$this->report( true );
273
	}
274
275
	function processFileOpt( $opt ) {
276
		$split = explode( ':', $opt, 2 );
277
		$val = $split[0];
278
		$param = '';
279
		if ( count( $split ) === 2 ) {
280
			$param = $split[1];
281
		}
282
		$fileURIs = explode( ';', $param );
283
		foreach ( $fileURIs as $URI ) {
284
			switch ( $val ) {
285
				case "file":
286
					$newURI = $URI;
287
					break;
288
				case "gzip":
289
					$newURI = "compress.zlib://$URI";
290
					break;
291
				case "bzip2":
292
					$newURI = "compress.bzip2://$URI";
293
					break;
294
				case "7zip":
295
					$newURI = "mediawiki.compress.7z://$URI";
296
					break;
297
				default:
298
					$newURI = $URI;
299
			}
300
			$newFileURIs[] = $newURI;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$newFileURIs was never initialized. Although not strictly required by PHP, it is generally a good practice to add $newFileURIs = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
301
		}
302
		$val = implode( ';', $newFileURIs );
0 ignored issues
show
Bug introduced by
The variable $newFileURIs does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
303
304
		return $val;
305
	}
306
307
	/**
308
	 * Overridden to include prefetch ratio if enabled.
309
	 */
310
	function showReport() {
311
		if ( !$this->prefetch ) {
312
			parent::showReport();
313
314
			return;
315
		}
316
317
		if ( $this->reporting ) {
318
			$now = wfTimestamp( TS_DB );
319
			$nowts = microtime( true );
320
			$deltaAll = $nowts - $this->startTime;
321
			$deltaPart = $nowts - $this->lastTime;
322
			$this->pageCountPart = $this->pageCount - $this->pageCountLast;
0 ignored issues
show
Bug introduced by
The property pageCountPart does not seem to exist. Did you mean pageCount?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
323
			$this->revCountPart = $this->revCount - $this->revCountLast;
0 ignored issues
show
Bug introduced by
The property revCountPart does not seem to exist. Did you mean revCount?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
324
325
			if ( $deltaAll ) {
326
				$portion = $this->revCount / $this->maxCount;
0 ignored issues
show
Bug introduced by
The property maxCount does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
327
				$eta = $this->startTime + $deltaAll / $portion;
328
				$etats = wfTimestamp( TS_DB, intval( $eta ) );
329
				if ( $this->fetchCount ) {
330
					$fetchRate = 100.0 * $this->prefetchCount / $this->fetchCount;
331
				} else {
332
					$fetchRate = '-';
333
				}
334
				$pageRate = $this->pageCount / $deltaAll;
335
				$revRate = $this->revCount / $deltaAll;
336
			} else {
337
				$pageRate = '-';
338
				$revRate = '-';
339
				$etats = '-';
340
				$fetchRate = '-';
341
			}
342
			if ( $deltaPart ) {
343
				if ( $this->fetchCountLast ) {
344
					$fetchRatePart = 100.0 * $this->prefetchCountLast / $this->fetchCountLast;
345
				} else {
346
					$fetchRatePart = '-';
347
				}
348
				$pageRatePart = $this->pageCountPart / $deltaPart;
0 ignored issues
show
Bug introduced by
The property pageCountPart does not seem to exist. Did you mean pageCount?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
349
				$revRatePart = $this->revCountPart / $deltaPart;
0 ignored issues
show
Bug introduced by
The property revCountPart does not seem to exist. Did you mean revCount?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
350
			} else {
351
				$fetchRatePart = '-';
352
				$pageRatePart = '-';
353
				$revRatePart = '-';
354
			}
355
			$this->progress( sprintf(
356
				"%s: %s (ID %d) %d pages (%0.1f|%0.1f/sec all|curr), "
357
					. "%d revs (%0.1f|%0.1f/sec all|curr), %0.1f%%|%0.1f%% "
358
					. "prefetched (all|curr), ETA %s [max %d]",
359
				$now, wfWikiID(), $this->ID, $this->pageCount, $pageRate,
360
				$pageRatePart, $this->revCount, $revRate, $revRatePart,
361
				$fetchRate, $fetchRatePart, $etats, $this->maxCount
362
			) );
363
			$this->lastTime = $nowts;
0 ignored issues
show
Documentation Bug introduced by
The property $lastTime was declared of type integer, but $nowts is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
364
			$this->revCountLast = $this->revCount;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->revCount can also be of type double. However, the property $revCountLast is declared as type integer. 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...
365
			$this->prefetchCountLast = $this->prefetchCount;
366
			$this->fetchCountLast = $this->fetchCount;
367
		}
368
	}
369
370
	function setTimeExceeded() {
371
		$this->timeExceeded = true;
372
	}
373
374
	function checkIfTimeExceeded() {
375
		if ( $this->maxTimeAllowed
376
			&& ( $this->lastTime - $this->timeOfCheckpoint > $this->maxTimeAllowed )
377
		) {
378
			return true;
379
		}
380
381
		return false;
382
	}
383
384
	function finalOptionCheck() {
385
		if ( ( $this->checkpointFiles && !$this->maxTimeAllowed )
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->checkpointFiles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
386
			|| ( $this->maxTimeAllowed && !$this->checkpointFiles )
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->checkpointFiles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
387
		) {
388
			throw new MWException( "Options checkpointfile and maxtime must be specified together.\n" );
389
		}
390
		foreach ( $this->checkpointFiles as $checkpointFile ) {
391
			$count = substr_count( $checkpointFile, "%s" );
392
			if ( $count != 2 ) {
393
				throw new MWException( "Option checkpointfile must contain two '%s' "
394
					. "for substitution of first and last pageids, count is $count instead, "
395
					. "file is $checkpointFile.\n" );
396
			}
397
		}
398
399
		if ( $this->checkpointFiles ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->checkpointFiles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
400
			$filenameList = (array)$this->egress->getFilenames();
401
			if ( count( $filenameList ) != count( $this->checkpointFiles ) ) {
402
				throw new MWException( "One checkpointfile must be specified "
403
					. "for each output option, if maxtime is used.\n" );
404
			}
405
		}
406
	}
407
408
	/**
409
	 * @throws MWException Failure to parse XML input
410
	 * @param string $input
411
	 * @return bool
412
	 */
413
	function readDump( $input ) {
414
		$this->buffer = "";
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
415
		$this->openElement = false;
0 ignored issues
show
Bug introduced by
The property openElement does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
416
		$this->atStart = true;
0 ignored issues
show
Bug introduced by
The property atStart does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
417
		$this->state = "";
0 ignored issues
show
Bug introduced by
The property state does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
418
		$this->lastName = "";
0 ignored issues
show
Bug introduced by
The property lastName does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
419
		$this->thisPage = 0;
0 ignored issues
show
Bug introduced by
The property thisPage does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
420
		$this->thisRev = 0;
0 ignored issues
show
Bug introduced by
The property thisRev does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
421
		$this->thisRevModel = null;
0 ignored issues
show
Bug introduced by
The property thisRevModel does not seem to exist. Did you mean thisRev?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
422
		$this->thisRevFormat = null;
0 ignored issues
show
Bug introduced by
The property thisRevFormat does not seem to exist. Did you mean thisRev?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
423
424
		$parser = xml_parser_create( "UTF-8" );
425
		xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
426
427
		xml_set_element_handler(
428
			$parser,
429
			[ $this, 'startElement' ],
430
			[ $this, 'endElement' ]
431
		);
432
		xml_set_character_data_handler( $parser, [ $this, 'characterData' ] );
433
434
		$offset = 0; // for context extraction on error reporting
435
		do {
436
			if ( $this->checkIfTimeExceeded() ) {
437
				$this->setTimeExceeded();
438
			}
439
			$chunk = fread( $input, $this->bufferSize );
440
			if ( !xml_parse( $parser, $chunk, feof( $input ) ) ) {
441
				wfDebug( "TextDumpPass::readDump encountered XML parsing error\n" );
442
443
				$byte = xml_get_current_byte_index( $parser );
444
				$msg = wfMessage( 'xml-error-string',
445
					'XML import parse failure',
446
					xml_get_current_line_number( $parser ),
447
					xml_get_current_column_number( $parser ),
448
					$byte . ( is_null( $chunk ) ? null : ( '; "' . substr( $chunk, $byte - $offset, 16 ) . '"' ) ),
449
					xml_error_string( xml_get_error_code( $parser ) ) )->escaped();
450
451
				xml_parser_free( $parser );
452
453
				throw new MWException( $msg );
454
			}
455
			$offset += strlen( $chunk );
456
		} while ( $chunk !== false && !feof( $input ) );
457
		if ( $this->maxTimeAllowed ) {
458
			$filenameList = (array)$this->egress->getFilenames();
459
			// we wrote some stuff after last checkpoint that needs renamed
460
			if ( file_exists( $filenameList[0] ) ) {
461
				$newFilenames = [];
462
				# we might have just written the header and footer and had no
463
				# pages or revisions written... perhaps they were all deleted
464
				# there's no pageID 0 so we use that. the caller is responsible
465
				# for deciding what to do with a file containing only the
466
				# siteinfo information and the mw tags.
467
				if ( !$this->firstPageWritten ) {
468
					$firstPageID = str_pad( 0, 9, "0", STR_PAD_LEFT );
469
					$lastPageID = str_pad( 0, 9, "0", STR_PAD_LEFT );
470
				} else {
471
					$firstPageID = str_pad( $this->firstPageWritten, 9, "0", STR_PAD_LEFT );
472
					$lastPageID = str_pad( $this->lastPageWritten, 9, "0", STR_PAD_LEFT );
473
				}
474
475
				$filenameCount = count( $filenameList );
476 View Code Duplication
				for ( $i = 0; $i < $filenameCount; $i++ ) {
477
					$checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID );
478
					$fileinfo = pathinfo( $filenameList[$i] );
479
					$newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn;
480
				}
481
				$this->egress->closeAndRename( $newFilenames );
482
			}
483
		}
484
		xml_parser_free( $parser );
485
486
		return true;
487
	}
488
489
	/**
490
	 * Applies applicable export transformations to $text.
491
	 *
492
	 * @param string $text
493
	 * @param string $model
494
	 * @param string|null $format
495
	 *
496
	 * @return string
497
	 */
498
	private function exportTransform( $text, $model, $format = null ) {
499
		try {
500
			$handler = ContentHandler::getForModelID( $model );
501
			$text = $handler->exportTransform( $text, $format );
502
		}
503
		catch ( MWException $ex ) {
504
			$this->progress(
505
				"Unable to apply export transformation for content model '$model': " .
506
				$ex->getMessage()
507
			);
508
		}
509
510
		return $text;
511
	}
512
513
	/**
514
	 * Tries to get the revision text for a revision id.
515
	 * Export transformations are applied if the content model can is given or can be
516
	 * determined from the database.
517
	 *
518
	 * Upon errors, retries (Up to $this->maxFailures tries each call).
519
	 * If still no good revision get could be found even after this retrying, "" is returned.
520
	 * If no good revision text could be returned for
521
	 * $this->maxConsecutiveFailedTextRetrievals consecutive calls to getText, MWException
522
	 * is thrown.
523
	 *
524
	 * @param string $id The revision id to get the text for
525
	 * @param string|bool|null $model The content model used to determine
526
	 *  applicable export transformations.
527
	 *  If $model is null, it will be determined from the database.
528
	 * @param string|null $format The content format used when applying export transformations.
529
	 *
530
	 * @throws MWException
531
	 * @return string The revision text for $id, or ""
532
	 */
533
	function getText( $id, $model = null, $format = null ) {
534
		global $wgContentHandlerUseDB;
535
536
		$prefetchNotTried = true; // Whether or not we already tried to get the text via prefetch.
537
		$text = false; // The candidate for a good text. false if no proper value.
538
		$failures = 0; // The number of times, this invocation of getText already failed.
539
540
		// The number of times getText failed without yielding a good text in between.
541
		static $consecutiveFailedTextRetrievals = 0;
542
543
		$this->fetchCount++;
544
545
		// To allow to simply return on success and do not have to worry about book keeping,
546
		// we assume, this fetch works (possible after some retries). Nevertheless, we koop
547
		// the old value, so we can restore it, if problems occur (See after the while loop).
548
		$oldConsecutiveFailedTextRetrievals = $consecutiveFailedTextRetrievals;
549
		$consecutiveFailedTextRetrievals = 0;
550
551
		if ( $model === null && $wgContentHandlerUseDB ) {
552
			$row = $this->db->selectRow(
553
				'revision',
554
				[ 'rev_content_model', 'rev_content_format' ],
555
				[ 'rev_id' => $this->thisRev ],
556
				__METHOD__
557
			);
558
559
			if ( $row ) {
560
				$model = $row->rev_content_model;
561
				$format = $row->rev_content_format;
562
			}
563
		}
564
565
		if ( $model === null || $model === '' ) {
566
			$model = false;
567
		}
568
569
		while ( $failures < $this->maxFailures ) {
570
571
			// As soon as we found a good text for the $id, we will return immediately.
572
			// Hence, if we make it past the try catch block, we know that we did not
573
			// find a good text.
574
575
			try {
576
				// Step 1: Get some text (or reuse from previous iteratuon if checking
577
				//         for plausibility failed)
578
579
				// Trying to get prefetch, if it has not been tried before
580
				if ( $text === false && isset( $this->prefetch ) && $prefetchNotTried ) {
581
					$prefetchNotTried = false;
582
					$tryIsPrefetch = true;
583
					$text = $this->prefetch->prefetch( intval( $this->thisPage ),
584
						intval( $this->thisRev ) );
585
586
					if ( $text === null ) {
587
						$text = false;
588
					}
589
590 View Code Duplication
					if ( is_string( $text ) && $model !== false ) {
591
						// Apply export transformation to text coming from an old dump.
592
						// The purpose of this transformation is to convert up from legacy
593
						// formats, which may still be used in the older dump that is used
594
						// for pre-fetching. Applying the transformation again should not
595
						// interfere with content that is already in the correct form.
596
						$text = $this->exportTransform( $text, $model, $format );
597
					}
598
				}
599
600
				if ( $text === false ) {
601
					// Fallback to asking the database
602
					$tryIsPrefetch = false;
603
					if ( $this->spawn ) {
604
						$text = $this->getTextSpawned( $id );
605
					} else {
606
						$text = $this->getTextDb( $id );
607
					}
608
609 View Code Duplication
					if ( $text !== false && $model !== false ) {
610
						// Apply export transformation to text coming from the database.
611
						// Prefetched text should already have transformations applied.
612
						$text = $this->exportTransform( $text, $model, $format );
613
					}
614
615
					// No more checks for texts from DB for now.
616
					// If we received something that is not false,
617
					// We treat it as good text, regardless of whether it actually is or is not
618
					if ( $text !== false ) {
619
						return $text;
620
					}
621
				}
622
623
				if ( $text === false ) {
624
					throw new MWException( "Generic error while obtaining text for id " . $id );
625
				}
626
627
				// We received a good candidate for the text of $id via some method
628
629
				// Step 2: Checking for plausibility and return the text if it is
630
				//         plausible
631
				$revID = intval( $this->thisRev );
632
				if ( !isset( $this->db ) ) {
633
					throw new MWException( "No database available" );
634
				}
635
636
				if ( $model !== CONTENT_MODEL_WIKITEXT ) {
637
					$revLength = strlen( $text );
638
				} else {
639
					$revLength = $this->db->selectField( 'revision', 'rev_len', [ 'rev_id' => $revID ] );
640
				}
641
642
				if ( strlen( $text ) == $revLength ) {
643
					if ( $tryIsPrefetch ) {
0 ignored issues
show
Bug introduced by
The variable $tryIsPrefetch does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
644
						$this->prefetchCount++;
645
					}
646
647
					return $text;
648
				}
649
650
				$text = false;
651
				throw new MWException( "Received text is unplausible for id " . $id );
652
			} catch ( Exception $e ) {
653
				$msg = "getting/checking text " . $id . " failed (" . $e->getMessage() . ")";
654
				if ( $failures + 1 < $this->maxFailures ) {
655
					$msg .= " (Will retry " . ( $this->maxFailures - $failures - 1 ) . " more times)";
656
				}
657
				$this->progress( $msg );
658
			}
659
660
			// Something went wrong; we did not a text that was plausible :(
661
			$failures++;
662
663
			// A failure in a prefetch hit does not warrant resetting db connection etc.
664
			if ( !$tryIsPrefetch ) {
665
				// After backing off for some time, we try to reboot the whole process as
666
				// much as possible to not carry over failures from one part to the other
667
				// parts
668
				sleep( $this->failureTimeout );
669
				try {
670
					$this->rotateDb();
671
					if ( $this->spawn ) {
672
						$this->closeSpawn();
673
						$this->openSpawn();
674
					}
675
				} catch ( Exception $e ) {
676
					$this->progress( "Rebooting getText infrastructure failed (" . $e->getMessage() . ")" .
677
						" Trying to continue anyways" );
678
				}
679
			}
680
		}
681
682
		// Retirieving a good text for $id failed (at least) maxFailures times.
683
		// We abort for this $id.
684
685
		// Restoring the consecutive failures, and maybe aborting, if the dump
686
		// is too broken.
687
		$consecutiveFailedTextRetrievals = $oldConsecutiveFailedTextRetrievals + 1;
688
		if ( $consecutiveFailedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals ) {
689
			throw new MWException( "Graceful storage failure" );
690
		}
691
692
		return "";
693
	}
694
695
	/**
696
	 * May throw a database error if, say, the server dies during query.
697
	 * @param int $id
698
	 * @return bool|string
699
	 * @throws MWException
700
	 */
701
	private function getTextDb( $id ) {
702
		global $wgContLang;
703
		if ( !isset( $this->db ) ) {
704
			throw new MWException( __METHOD__ . "No database available" );
705
		}
706
		$row = $this->db->selectRow( 'text',
707
			[ 'old_text', 'old_flags' ],
708
			[ 'old_id' => $id ],
709
			__METHOD__ );
710
		$text = Revision::getRevisionText( $row );
0 ignored issues
show
Bug introduced by
It seems like $row defined by $this->db->selectRow('te...d' => $id), __METHOD__) on line 706 can also be of type boolean; however, Revision::getRevisionText() does only seem to accept object<stdClass>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
711
		if ( $text === false ) {
712
			return false;
713
		}
714
		$stripped = str_replace( "\r", "", $text );
715
		$normalized = $wgContLang->normalize( $stripped );
716
717
		return $normalized;
718
	}
719
720
	private function getTextSpawned( $id ) {
721
		MediaWiki\suppressWarnings();
722
		if ( !$this->spawnProc ) {
723
			// First time?
724
			$this->openSpawn();
725
		}
726
		$text = $this->getTextSpawnedOnce( $id );
727
		MediaWiki\restoreWarnings();
728
729
		return $text;
730
	}
731
732
	function openSpawn() {
733
		global $IP;
734
735
		if ( file_exists( "$IP/../multiversion/MWScript.php" ) ) {
736
			$cmd = implode( " ",
737
				array_map( 'wfEscapeShellArg',
738
					[
739
						$this->php,
740
						"$IP/../multiversion/MWScript.php",
741
						"fetchText.php",
742
						'--wiki', wfWikiID() ] ) );
743
		} else {
744
			$cmd = implode( " ",
745
				array_map( 'wfEscapeShellArg',
746
					[
747
						$this->php,
748
						"$IP/maintenance/fetchText.php",
749
						'--wiki', wfWikiID() ] ) );
750
		}
751
		$spec = [
752
			0 => [ "pipe", "r" ],
753
			1 => [ "pipe", "w" ],
754
			2 => [ "file", "/dev/null", "a" ] ];
755
		$pipes = [];
756
757
		$this->progress( "Spawning database subprocess: $cmd" );
758
		$this->spawnProc = proc_open( $cmd, $spec, $pipes );
759
		if ( !$this->spawnProc ) {
760
			$this->progress( "Subprocess spawn failed." );
761
762
			return false;
763
		}
764
		list(
765
			$this->spawnWrite, // -> stdin
766
			$this->spawnRead, // <- stdout
767
		) = $pipes;
768
769
		return true;
770
	}
771
772
	private function closeSpawn() {
773
		MediaWiki\suppressWarnings();
774
		if ( $this->spawnRead ) {
775
			fclose( $this->spawnRead );
776
		}
777
		$this->spawnRead = false;
778
		if ( $this->spawnWrite ) {
779
			fclose( $this->spawnWrite );
780
		}
781
		$this->spawnWrite = false;
782
		if ( $this->spawnErr ) {
783
			fclose( $this->spawnErr );
784
		}
785
		$this->spawnErr = false;
786
		if ( $this->spawnProc ) {
787
			pclose( $this->spawnProc );
788
		}
789
		$this->spawnProc = false;
790
		MediaWiki\restoreWarnings();
791
	}
792
793
	private function getTextSpawnedOnce( $id ) {
794
		global $wgContLang;
795
796
		$ok = fwrite( $this->spawnWrite, "$id\n" );
797
		// $this->progress( ">> $id" );
798
		if ( !$ok ) {
799
			return false;
800
		}
801
802
		$ok = fflush( $this->spawnWrite );
803
		// $this->progress( ">> [flush]" );
804
		if ( !$ok ) {
805
			return false;
806
		}
807
808
		// check that the text id they are sending is the one we asked for
809
		// this avoids out of sync revision text errors we have encountered in the past
810
		$newId = fgets( $this->spawnRead );
811
		if ( $newId === false ) {
812
			return false;
813
		}
814
		if ( $id != intval( $newId ) ) {
815
			return false;
816
		}
817
818
		$len = fgets( $this->spawnRead );
819
		// $this->progress( "<< " . trim( $len ) );
820
		if ( $len === false ) {
821
			return false;
822
		}
823
824
		$nbytes = intval( $len );
825
		// actual error, not zero-length text
826
		if ( $nbytes < 0 ) {
827
			return false;
828
		}
829
830
		$text = "";
831
832
		// Subprocess may not send everything at once, we have to loop.
833
		while ( $nbytes > strlen( $text ) ) {
834
			$buffer = fread( $this->spawnRead, $nbytes - strlen( $text ) );
835
			if ( $buffer === false ) {
836
				break;
837
			}
838
			$text .= $buffer;
839
		}
840
841
		$gotbytes = strlen( $text );
842
		if ( $gotbytes != $nbytes ) {
843
			$this->progress( "Expected $nbytes bytes from database subprocess, got $gotbytes " );
844
845
			return false;
846
		}
847
848
		// Do normalization in the dump thread...
849
		$stripped = str_replace( "\r", "", $text );
850
		$normalized = $wgContLang->normalize( $stripped );
851
852
		return $normalized;
853
	}
854
855
	function startElement( $parser, $name, $attribs ) {
856
		$this->checkpointJustWritten = false;
857
858
		$this->clearOpenElement( null );
859
		$this->lastName = $name;
860
861
		if ( $name == 'revision' ) {
862
			$this->state = $name;
863
			$this->egress->writeOpenPage( null, $this->buffer );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
864
			$this->buffer = "";
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
865
		} elseif ( $name == 'page' ) {
866
			$this->state = $name;
867
			if ( $this->atStart ) {
868
				$this->egress->writeOpenStream( $this->buffer );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
869
				$this->buffer = "";
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
870
				$this->atStart = false;
871
			}
872
		}
873
874
		if ( $name == "text" && isset( $attribs['id'] ) ) {
875
			$id = $attribs['id'];
876
			$model = trim( $this->thisRevModel );
0 ignored issues
show
Bug introduced by
The property thisRevModel does not seem to exist. Did you mean thisRev?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
877
			$format = trim( $this->thisRevFormat );
0 ignored issues
show
Bug introduced by
The property thisRevFormat does not seem to exist. Did you mean thisRev?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
878
879
			$model = $model === '' ? null : $model;
880
			$format = $format === '' ? null : $format;
881
882
			$text = $this->getText( $id, $model, $format );
883
			$this->openElement = [ $name, [ 'xml:space' => 'preserve' ] ];
884
			if ( strlen( $text ) > 0 ) {
885
				$this->characterData( $parser, $text );
886
			}
887
		} else {
888
			$this->openElement = [ $name, $attribs ];
889
		}
890
	}
891
892
	function endElement( $parser, $name ) {
893
		$this->checkpointJustWritten = false;
894
895
		if ( $this->openElement ) {
896
			$this->clearOpenElement( "" );
897
		} else {
898
			$this->buffer .= "</$name>";
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
899
		}
900
901
		if ( $name == 'revision' ) {
902
			$this->egress->writeRevision( null, $this->buffer );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
903
			$this->buffer = "";
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
904
			$this->thisRev = "";
905
			$this->thisRevModel = null;
0 ignored issues
show
Bug introduced by
The property thisRevModel does not seem to exist. Did you mean thisRev?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
906
			$this->thisRevFormat = null;
0 ignored issues
show
Bug introduced by
The property thisRevFormat does not seem to exist. Did you mean thisRev?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
907
		} elseif ( $name == 'page' ) {
908
			if ( !$this->firstPageWritten ) {
909
				$this->firstPageWritten = trim( $this->thisPage );
0 ignored issues
show
Documentation Bug introduced by
The property $firstPageWritten was declared of type boolean, but trim($this->thisPage) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
910
			}
911
			$this->lastPageWritten = trim( $this->thisPage );
0 ignored issues
show
Documentation Bug introduced by
The property $lastPageWritten was declared of type boolean, but trim($this->thisPage) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
912
			if ( $this->timeExceeded ) {
913
				$this->egress->writeClosePage( $this->buffer );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
914
				// nasty hack, we can't just write the chardata after the
915
				// page tag, it will include leading blanks from the next line
916
				$this->egress->sink->write( "\n" );
917
918
				$this->buffer = $this->xmlwriterobj->closeStream();
0 ignored issues
show
Bug introduced by
The method closeStream cannot be called on $this->xmlwriterobj (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
919
				$this->egress->writeCloseStream( $this->buffer );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
920
921
				$this->buffer = "";
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
922
				$this->thisPage = "";
923
				// this could be more than one file if we had more than one output arg
924
925
				$filenameList = (array)$this->egress->getFilenames();
926
				$newFilenames = [];
927
				$firstPageID = str_pad( $this->firstPageWritten, 9, "0", STR_PAD_LEFT );
928
				$lastPageID = str_pad( $this->lastPageWritten, 9, "0", STR_PAD_LEFT );
929
				$filenamesCount = count( $filenameList );
930 View Code Duplication
				for ( $i = 0; $i < $filenamesCount; $i++ ) {
931
					$checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID );
932
					$fileinfo = pathinfo( $filenameList[$i] );
933
					$newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn;
934
				}
935
				$this->egress->closeRenameAndReopen( $newFilenames );
936
				$this->buffer = $this->xmlwriterobj->openStream();
0 ignored issues
show
Bug introduced by
The method openStream cannot be called on $this->xmlwriterobj (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
937
				$this->timeExceeded = false;
938
				$this->timeOfCheckpoint = $this->lastTime;
939
				$this->firstPageWritten = false;
940
				$this->checkpointJustWritten = true;
941
			} else {
942
				$this->egress->writeClosePage( $this->buffer );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
943
				$this->buffer = "";
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
944
				$this->thisPage = "";
945
			}
946
		} elseif ( $name == 'mediawiki' ) {
947
			$this->egress->writeCloseStream( $this->buffer );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
948
			$this->buffer = "";
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
949
		}
950
	}
951
952
	function characterData( $parser, $data ) {
953
		$this->clearOpenElement( null );
954
		if ( $this->lastName == "id" ) {
955
			if ( $this->state == "revision" ) {
956
				$this->thisRev .= $data;
957
			} elseif ( $this->state == "page" ) {
958
				$this->thisPage .= $data;
959
			}
960
		} elseif ( $this->lastName == "model" ) {
961
			$this->thisRevModel .= $data;
0 ignored issues
show
Bug introduced by
The property thisRevModel does not seem to exist. Did you mean thisRev?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
962
		} elseif ( $this->lastName == "format" ) {
963
			$this->thisRevFormat .= $data;
0 ignored issues
show
Bug introduced by
The property thisRevFormat does not seem to exist. Did you mean thisRev?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
964
		}
965
966
		// have to skip the newline left over from closepagetag line of
967
		// end of checkpoint files. nasty hack!!
968
		if ( $this->checkpointJustWritten ) {
969
			if ( $data[0] == "\n" ) {
970
				$data = substr( $data, 1 );
971
			}
972
			$this->checkpointJustWritten = false;
973
		}
974
		$this->buffer .= htmlspecialchars( $data );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
975
	}
976
977
	function clearOpenElement( $style ) {
978
		if ( $this->openElement ) {
979
			$this->buffer .= Xml::element( $this->openElement[0], $this->openElement[1], $style );
0 ignored issues
show
Bug introduced by
The property buffer does not seem to exist. Did you mean bufferSize?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
980
			$this->openElement = false;
981
		}
982
	}
983
}
984
985
$maintClass = 'TextPassDumper';
986
require_once RUN_MAINTENANCE_IF_MAIN;
987