Completed
Push — master ( 73fd93...7205b8 )
by mw
38:01 queued 04:56
created

SMWSql3SmwIds::getIDFor()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 47
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 29
c 1
b 0
f 0
nc 7
nop 1
dl 0
loc 47
ccs 28
cts 28
cp 1
crap 6
rs 8.5125
1
<?php
2
3
use SMW\DIWikiPage;
4
use SMW\DIProperty;
5
use SMW\HashBuilder;
6
use SMW\ApplicationFactory;
7
use SMW\SQLStore\IdToDataItemMatchFinder;
8
use SMW\SQLStore\PropertyStatisticsTable;
9
use SMW\SQLStore\RedirectInfoStore;
10
11
/**
12
 * @ingroup SMWStore
13
 * @since 1.8
14
 * @author Markus Krötzsch
15
 */
16
17
/**
18
 * Class to access the SMW IDs table in SQLStore3.
19
 * Provides transparent in-memory caching facilities.
20
 *
21
 * Documentation for the SMW IDs table: This table is a dictionary that
22
 * assigns integer IDs to pages, properties, and other objects used by SMW.
23
 * All tables that refer to such objects store these IDs instead. If the ID
24
 * information is lost (e.g., table gets deleted), then the data stored in SMW
25
 * is no longer meaningful: all tables need to be dropped, recreated, and
26
 * refreshed to get back to a working database.
27
 *
28
 * The table has a column for storing interwiki prefixes, used to refer to
29
 * pages on external sites (like in MediaWiki). This column is also used to
30
 * mark some special objects in the table, using "interwiki prefixes" that
31
 * cannot occur in MediaWiki:
32
 *
33
 * - Rows with iw SMW_SQL3_SMWREDIIW are similar to normal entries for
34
 * (internal) wiki pages, but the iw indicates that the page is a redirect, the
35
 * (target of which should be sought using the smw_fpt_redi table.
36
 *
37
 * - The (unique) row with iw SMW_SQL3_SMWBORDERIW just marks the border
38
 * between predefined ids (rows that are reserved for hardcoded ids built into
39
 * SMW) and normal entries. It is no object, but makes sure that SQL's auto
40
 * increment counter is high enough to not add any objects before that marked
41
 * "border".
42
 *
43
 * @note Do not call the constructor of SMWDIWikiPage using data from the SMW
44
 * IDs table; use SMWDIHandlerWikiPage::dataItemFromDBKeys() instead. The table
45
 * does not always contain data as required wiki pages. Especially predefined
46
 * properties are represented by language-independent keys rather than proper
47
 * titles. SMWDIHandlerWikiPage takes care of this.
48
 *
49
 * @since 1.8
50
 *
51
 * @ingroup SMWStore
52
 */
53
class SMWSql3SmwIds {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
54
55
	/**
56
	 * Specifies the border limit for pre-defined properties declared
57
	 * in SMWSql3SmwIds::special_ids
58
	 */
59
	const FXD_PROP_BORDER_ID = SMWSQLStore3::FIXED_PROPERTY_ID_UPPERBOUND;
60
61
	/**
62
	 * Name of the table to store IDs in.
63
	 *
64
	 * @note This should never change. Existing wikis will have to drop and
65
	 * rebuild their SMW tables completely to recover from any change here.
66
	 */
67
	const TABLE_NAME = SMWSQLStore3::ID_TABLE;
68
69
	const POOLCACHE_ID = 'sql.store.id.cache';
70
71
	/**
72
	 * Id for which property table hashes are cached, if any.
73
	 *
74
	 * @since 1.8
75
	 * @var integer
76
	 */
77
	protected $hashCacheId = 0;
78
79
	/**
80
	 * Cached property table hashes for $hashCacheId.
81
	 *
82
	 * @since 1.8
83
	 * @var string
84
	 */
85
	protected $hashCacheContents = '';
86
87
	/**
88
	 * Maximal number of cached property IDs.
89
	 *
90
	 * @since 1.8
91
	 * @var integer
92
	 */
93
	public static $PROP_CACHE_MAX_SIZE = 250;
94
95
	/**
96
	 * Maximal number of cached non-property IDs.
97
	 *
98
	 * @since 1.8
99
	 * @var integer
100
	 */
101
	public static $PAGE_CACHE_MAX_SIZE = 500;
102
103
	protected $selectrow_sort_debug = 0;
104
	protected $selectrow_redi_debug = 0;
105
	protected $prophit_debug = 0;
106
	protected $propmiss_debug = 0;
107
	protected $reghit_debug = 0;
108
	protected $regmiss_debug = 0;
109
110
	static protected $singleton_debug = null;
111
112
	/**
113
	 * Parent SMWSQLStore3.
114
	 *
115
	 * @since 1.8
116
	 * @var SMWSQLStore3
117
	 */
118
	public $store;
119
120
	/**
121
	 * Cache for property IDs.
122
	 *
123
	 * @note Tests indicate that it is more memory efficient to have two
124
	 * arrays (IDs and sortkeys) than to have one array that stores both
125
	 * values in some data structure (other than a single string).
126
	 *
127
	 * @since 1.8
128
	 * @var array
129
	 */
130
	protected $prop_ids = array();
131
132
	/**
133
	 * @var IdToDataItemMatchFinder
134
	 */
135
	private $idToDataItemMatchFinder;
136
137
	/**
138
	 * @var RedirectInfoStore
139
	 */
140
	private $redirectInfoStore;
141
142
	/**
143
	 * Cache for property sortkeys.
144
	 *
145
	 * @since 1.8
146
	 * @var array
147
	 */
148
	protected $prop_sortkeys = array();
149
150
	/**
151
	 * Cache for non-property IDs.
152
	 *
153
	 * @since 1.8
154
	 * @var array
155
	 */
156
	protected $regular_ids = array();
157
158
	/**
159
	 * Cache for non-property sortkeys.
160
	 *
161
	 * @since 1.8
162
	 * @var array
163
	 */
164
	protected $regular_sortkeys = array();
165
166
	/**
167
	 * Use pre-defined ids for Very Important Properties, avoiding frequent
168
	 * ID lookups for those.
169
	 *
170
	 * @note These constants also occur in the store. Changing them will
171
	 * require to run setup.php again. They can also not be larger than 50.
172
	 *
173
	 * @since 1.8
174
	 * @var array
175
	 */
176
	public static $special_ids = array(
177
		'_TYPE' => 1,
178
		'_URI'  => 2,
179
		'_INST' => 4,
180
		'_UNIT' => 7,
181
		'_IMPO' => 8,
182
		'_PDESC' => 10,
183
		'_PREC' => 11,
184
		'_CONV' => 12,
185
		'_SERV' => 13,
186
		'_PVAL' => 14,
187
		'_REDI' => 15,
188
		'_DTITLE' => 16,
189
		'_SUBP' => 17,
190
		'_SUBC' => 18,
191
		'_CONC' => 19,
192
//		'_SF_DF' => 20, // Semantic Form's default form property
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
193
//		'_SF_AF' => 21,  // Semantic Form's alternate form property
194
		'_ERRP' => 22,
195
// 		'_1' => 23, // properties for encoding (short) lists
0 ignored issues
show
Unused Code Comprehensibility introduced by
49% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
196
// 		'_2' => 24,
197
// 		'_3' => 25,
198
// 		'_4' => 26,
199
// 		'_5' => 27,
200
// 		'_SOBJ' => 27
201
		'_LIST' => 28,
202
		'_MDAT' => 29,
203
		'_CDAT' => 30,
204
		'_NEWP' => 31,
205
		'_LEDT' => 32,
206
		// properties related to query management
207
		'_ASK'   =>  33,
208
		'_ASKST' =>  34,
209
		'_ASKFO' =>  35,
210
		'_ASKSI' =>  36,
211
		'_ASKDE' =>  37,
212
//		'_ASKDU' =>  38,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
213
		'_LCODE' =>  40,
214
		'_TEXT'  =>  41,
215
	);
216
217
	/**
218
	 * Constructor.
219
	 *
220
	 * @since 1.8
221
	 * @param SMWSQLStore3 $store
222
	 */
223 33
	public function __construct( SMWSQLStore3 $store ) {
224 33
		$this->store = $store;
225
		// Yes, this is a hack, but we only use it for convenient debugging:
226 33
		self::$singleton_debug = $this;
227
228 33
		$this->idToDataItemMatchFinder = new IdToDataItemMatchFinder(
229 33
			$this->store->getConnection( 'mw.db' )
230
		);
231
232 33
		$this->redirectInfoStore = new RedirectInfoStore(
233 33
			$this->store->getConnection( 'mw.db' )
234
		);
235
236 33
		$this->intermediaryIdCache = ApplicationFactory::getInstance()->getInMemoryPoolCache()->getPoolCacheFor( self::POOLCACHE_ID );
0 ignored issues
show
Bug introduced by
The property intermediaryIdCache 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...
237 33
	}
238
239
	/**
240
	 * @since  2.1
241
	 *
242
	 * @param DIWikiPage $subject
243
	 *
244
	 * @return boolean
245
	 */
246 99
	public function checkIsRedirect( DIWikiPage $subject ) {
247
248 99
		$redirectId = $this->findRedirectIdFor(
249 99
			$subject->getDBKey(),
250 99
			$subject->getNamespace()
251
		);
252
253 99
		return $redirectId != 0;
254
	}
255
256
	/**
257
	 * @see RedirectInfoStore::findRedirectIdFor
258
	 *
259
	 * @since 2.1
260
	 *
261
	 * @param string $title DB key
262
	 * @param integer $namespace
263
	 *
264
	 * @return integer
265
	 */
266 241
	public function findRedirectIdFor( $title, $namespace ) {
267 241
		return $this->redirectInfoStore->findRedirectIdFor( $title, $namespace );
268
	}
269
270
	/**
271
	 * @see RedirectInfoStore::addRedirectForId
272
	 *
273
	 * @since 2.1
274
	 *
275
	 * @param integer $id
276
	 * @param string $title
277
	 * @param integer $namespace
278
	 */
279 24
	public function addRedirectForId( $id, $title, $namespace ) {
280 24
		return $this->redirectInfoStore->addRedirectForId( $id, $title, $namespace );
281
	}
282
283
	/**
284
	 * @see RedirectInfoStore::deleteRedirectEntry
285
	 *
286
	 * @since 2.1
287
	 *
288
	 * @param string $title
289
	 * @param integer $namespace
290
	 */
291 24
	public function deleteRedirectEntry( $title, $namespace ) {
292 24
		return $this->redirectInfoStore->deleteRedirectEntry( $title, $namespace );
293
	}
294
295
	/**
296
	 * Find the numeric ID used for the page of the given title,
297
	 * namespace, interwiki, and subobject. If $canonical is set to true,
298
	 * redirects are taken into account to find the canonical alias ID for
299
	 * the given page. If no such ID exists, 0 is returned. The Call-By-Ref
300
	 * parameter $sortkey is set to the current sortkey, or to '' if no ID
301
	 * exists.
302
	 *
303
	 * If $fetchhashes is true, the property table hash blob will be
304
	 * retrieved in passing if the opportunity arises, and cached
305
	 * internally. This will speed up a subsequent call to
306
	 * getPropertyTableHashes() for this id. This should only be done
307
	 * if such a call is intended, both to safe the previous cache and
308
	 * to avoid extra work (even if only a little) to fill it.
309
	 *
310
	 * @since 1.8
311
	 * @param string $title DB key
312
	 * @param integer $namespace namespace
313
	 * @param string $iw interwiki prefix
314
	 * @param string $subobjectName name of subobject
315
	 * @param string $sortkey call-by-ref will be set to sortkey
316
	 * @param boolean $canonical should redirects be resolved?
317
	 * @param boolean $fetchHashes should the property hashes be obtained and cached?
318
	 * @return integer SMW id or 0 if there is none
319
	 */
320 251
	public function getSMWPageIDandSort( $title, $namespace, $iw, $subobjectName, &$sortkey, $canonical, $fetchHashes = false ) {
321 251
		$id = $this->getPredefinedData( $title, $namespace, $iw, $subobjectName, $sortkey );
322 251
		if ( $id != 0 ) {
323 47
			return (int)$id;
324
		} else {
325 248
			return (int)$this->getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, $sortkey, $canonical, $fetchHashes );
326
		}
327
	}
328
329
	/**
330
	 * Find the numeric ID used for the page of the given normalized title,
331
	 * namespace, interwiki, and subobjectName. Predefined IDs are not
332
	 * taken into account (however, they would still be found correctly by
333
	 * an avoidable database read if they are stored correctly in the
334
	 * database; this should always be the case). In all other aspects, the
335
	 * method works just like getSMWPageIDandSort().
336
	 *
337
	 * @since 1.8
338
	 * @param string $title DB key
339
	 * @param integer $namespace namespace
340
	 * @param string $iw interwiki prefix
341
	 * @param string $subobjectName name of subobject
342
	 * @param string $sortkey call-by-ref will be set to sortkey
343
	 * @param boolean $canonical should redirects be resolved?
344
	 * @param boolean $fetchHashes should the property hashes be obtained and cached?
345
	 * @return integer SMW id or 0 if there is none
346
	 */
347 251
	protected function getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, &$sortkey, $canonical, $fetchHashes ) {
348 251
		global $smwgQEqualitySupport;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
349
350 251
		$db = $this->store->getConnection();
351
352
		// Integration test "query-04-02-subproperty-dc-import-marc21.json"
353
		// showed a deterministic failure (due to a wrong cache id during querying
354
		// for redirects) hence we force to read directly from the RedirectInfoStore
355
		// for objects marked as redirect
356 251
		if ( $iw === SMW_SQL3_SMWREDIIW && $canonical &&
357 251
			$smwgQEqualitySupport !== SMW_EQ_NONE && $subobjectName === '' ) {
358 240
			$id = $this->findRedirectIdFor( $title, $namespace );
359
		} else {
360 251
			$id = $this->getCachedId(
361
				$title,
362
				$namespace,
363
				$iw,
364
				$subobjectName
365
			);
366
		}
367
368 251
		if ( $id !== false ) { // cache hit
369 240
			$sortkey = $this->getCachedSortKey( $title, $namespace, $iw, $subobjectName );
370 251
		} elseif ( $iw == SMW_SQL3_SMWREDIIW && $canonical &&
371 251
			$smwgQEqualitySupport != SMW_EQ_NONE && $subobjectName === '' ) {
372
			$id = $this->findRedirectIdFor( $title, $namespace );
373
			if ( $id != 0 ) {
374
375
				if ( $fetchHashes ) {
376
					$select = array( 'smw_sortkey', 'smw_proptable_hash' );
377
				} else {
378
					$select = array( 'smw_sortkey' );
379
				}
380
381
				$row = $db->selectRow(
382
					self::TABLE_NAME,
383
					$select,
384
					array( 'smw_id' => $id ),
385
					__METHOD__
386
				);
387
388
				if ( $row !== false ) {
389
					$sortkey = $row->smw_sortkey;
390
					if ( $fetchHashes ) {
391
						$this->setPropertyTableHashesCache( $id, $row->smw_proptable_hash );
392
					}
393
				} else { // inconsistent DB; just recover somehow
394
					$sortkey = str_replace( '_', ' ', $title );
395
				}
396
			} else {
397
				$sortkey = '';
398
			}
399
			$this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
400
		} else {
401
402 251
			if ( $fetchHashes ) {
403 221
				$select = array( 'smw_id', 'smw_sortkey', 'smw_proptable_hash' );
404
			} else {
405 244
				$select = array( 'smw_id', 'smw_sortkey' );
406
			}
407
408 251
			$row = $db->selectRow(
409 251
				self::TABLE_NAME,
410
				$select,
411
				array(
412 251
					'smw_title' => $title,
413 251
					'smw_namespace' => $namespace,
414 251
					'smw_iw' => $iw,
415 251
					'smw_subobject' => $subobjectName
416
				),
417 251
				__METHOD__
418
			);
419
420 251
			$this->selectrow_sort_debug++;
421
422 251
			if ( $row !== false ) {
423 197
				$id = $row->smw_id;
424 197
				$sortkey = $row->smw_sortkey;
425 197
				if ( $fetchHashes ) {
426 197
					$this->setPropertyTableHashesCache( $id, $row->smw_proptable_hash);
427
				}
428
			} else {
429 234
				$id = 0;
430 234
				$sortkey = '';
431
			}
432
433 251
			$this->setCache(
434
				$title,
435
				$namespace,
436
				$iw,
437
				$subobjectName,
438
				$id,
439
				$sortkey
440
			);
441
		}
442
443 251
		if ( $id == 0 && $subobjectName === '' && $iw === '' ) { // could be a redirect; check
444 240
			$id = $this->getSMWPageIDandSort(
445
				$title,
446
				$namespace,
447 240
				SMW_SQL3_SMWREDIIW,
448
				$subobjectName,
449
				$sortkey,
450
				$canonical,
451
				$fetchHashes
452
			);
453
		}
454
455 251
		return $id;
456
	}
457
458
	/**
459
	 * @since 2.3
460
	 *
461
	 * @param string $title DB key
462
	 * @param integer $namespace namespace
463
	 * @param string $iw interwiki prefix
464
	 * @param string $subobjectName name of subobject
465
	 *
466
	 * @param array
467
	 */
468 201
	public function getListOfIdMatchesFor( $title, $namespace, $iw, $subobjectName = '' ) {
469
470 201
		$matches = array();
471
472 201
		$rows = $this->store->getConnection( 'mw.db' )->select(
473 201
			self::TABLE_NAME,
474 201
			$select = array( 'smw_id' ),
475
			array(
476 201
				'smw_title' => $title,
477 201
				'smw_namespace' => $namespace,
478 201
				'smw_iw' => $iw,
479 201
				'smw_subobject' => $subobjectName
480
			),
481 201
			__METHOD__
482
		);
483
484 201
		if ( $rows === false ) {
485
			return $matches;
486
		}
487
488 201
		foreach ( $rows as $row ) {
489 194
			$matches[] = $row->smw_id;
490
		}
491
492 201
		return $matches;
493
	}
494
495
	/**
496
	 * @since 2.4
497
	 *
498
	 * @param DIWikiPage $subject
499
	 *
500
	 * @param boolean
501
	 */
502 32
	public function hasIDFor( DIWikiPage $subject ) {
503 32
		return $this->getIDFor( $subject ) > 0;
504
	}
505
506
	/**
507
	 * @note SMWSql3SmwIds::getSMWPageID has some issues with the cache as it returned
508
	 * 0 even though an object was matchable but since I'm too busy to investigate, using
509
	 * this method is safer then trying to encipher getSMWPageID related methods.
510
	 *
511
	 * It uses the PoolCache which means Lru is in place to avoid memory leakage.
512
	 *
513
	 * @since 2.4
514
	 *
515
	 * @param DIWikiPage $subject
516
	 *
517
	 * @param integer
518
	 */
519 66
	public function getIDFor( DIWikiPage $subject ) {
520
521 66
		$hash = HashBuilder::getHashIdForDiWikiPage( $subject );
522
523 66
		if ( ( $id = $this->intermediaryIdCache->fetch( $hash ) ) > 0 ) {
524 55
			return $id;
525
		}
526
527 35
		$row = $this->store->getConnection( 'mw.db' )->selectRow(
528 35
			self::TABLE_NAME,
529 35
			array( 'smw_id' ),
530
			array(
531 35
				'smw_title' => $subject->getDBKey(),
532 35
				'smw_namespace' => $subject->getNamespace(),
533 35
				'smw_iw' => $subject->getInterWiki(),
534 35
				'smw_subobject' => $subject->getSubobjectName()
535
			),
536 35
			__METHOD__
537
		);
538
539
		// Try to match a predefined property
540 35
		if ( $subject->getNamespace() === SMW_NS_PROPERTY && $subject->getInterWiki() === '' ) {
541 3
			$property = DIProperty::newFromUserLabel( $subject->getDBKey() );
542
543 3
			if ( isset( self::$special_ids[$property->getKey()] ) ) {
544 3
				$row = new \stdClass;
545 3
				$row->smw_id = self::$special_ids[$property->getKey()];
546
			}
547
		}
548
549 35
		if ( $row === false ) {
550 32
			wfDebugLog( 'smw', __METHOD__  . " $hash was a cache miss" );
551 32
			return 0;
552
		}
553
554
		// Legacy
555 4
		$this->setCache(
556 4
			$subject->getDBKey(),
557 4
			$subject->getNamespace(),
558 4
			$subject->getInterWiki(),
559 4
			$subject->getSubobjectName(),
560 4
			$row->smw_id,
561 4
			$subject->getSortKey()
562
		);
563
564 4
		return $row->smw_id;
565
	}
566
567
	/**
568
	 * Convenience method for calling getSMWPageIDandSort without
569
	 * specifying a sortkey (if not asked for).
570
	 *
571
	 * @since 1.8
572
	 * @param string $title DB key
573
	 * @param integer $namespace namespace
574
	 * @param string $iw interwiki prefix
575
	 * @param string $subobjectName name of subobject
576
	 * @param boolean $canonical should redirects be resolved?
577
	 * @param boolean $fetchHashes should the property hashes be obtained and cached?
578
	 * @return integer SMW id or 0 if there is none
579
	 */
580 193
	public function getSMWPageID( $title, $namespace, $iw, $subobjectName, $canonical = true, $fetchHashes = false ) {
581 193
		$sort = '';
582 193
		return $this->getSMWPageIDandSort( $title, $namespace, $iw, $subobjectName, $sort, $canonical, $fetchHashes );
583
	}
584
585
	/**
586
	 * Find the numeric ID used for the page of the given title, namespace,
587
	 * interwiki, and subobjectName. If $canonical is set to true,
588
	 * redirects are taken into account to find the canonical alias ID for
589
	 * the given page. If no such ID exists, a new ID is created and
590
	 * returned. In any case, the current sortkey is set to the given one
591
	 * unless $sortkey is empty.
592
	 *
593
	 * @note Using this with $canonical==false can make sense, especially when
594
	 * the title is a redirect target (we do not want chains of redirects).
595
	 * But it is of no relevance if the title does not have an id yet.
596
	 *
597
	 * @since 1.8
598
	 * @param string $title DB key
599
	 * @param integer $namespace namespace
600
	 * @param string $iw interwiki prefix
601
	 * @param string $subobjectName name of subobject
602
	 * @param boolean $canonical should redirects be resolved?
603
	 * @param string $sortkey call-by-ref will be set to sortkey
604
	 * @param boolean $fetchHashes should the property hashes be obtained and cached?
605
	 * @return integer SMW id or 0 if there is none
606
	 */
607 229
	public function makeSMWPageID( $title, $namespace, $iw, $subobjectName, $canonical = true, $sortkey = '', $fetchHashes = false ) {
608 229
		$id = $this->getPredefinedData( $title, $namespace, $iw, $subobjectName, $sortkey );
609 229
		if ( $id != 0 ) {
610 10
			return (int)$id;
611
		} else {
612 229
			return (int)$this->makeDatabaseId( $title, $namespace, $iw, $subobjectName, $canonical, $sortkey, $fetchHashes );
613
		}
614
	}
615
616
	/**
617
	 * Find the numeric ID used for the page of the given normalized title,
618
	 * namespace, interwiki, and subobjectName. Predefined IDs are not
619
	 * taken into account (however, they would still be found correctly by
620
	 * an avoidable database read if they are stored correctly in the
621
	 * database; this should always be the case). In all other aspects, the
622
	 * method works just like makeSMWPageID(). Especially, if no ID exists,
623
	 * a new ID is created and returned.
624
	 *
625
	 * @since 1.8
626
	 * @param string $title DB key
627
	 * @param integer $namespace namespace
628
	 * @param string $iw interwiki prefix
629
	 * @param string $subobjectName name of subobject
630
	 * @param boolean $canonical should redirects be resolved?
631
	 * @param string $sortkey call-by-ref will be set to sortkey
632
	 * @param boolean $fetchHashes should the property hashes be obtained and cached?
633
	 * @return integer SMW id or 0 if there is none
634
	 */
635 229
	protected function makeDatabaseId( $title, $namespace, $iw, $subobjectName, $canonical, $sortkey, $fetchHashes ) {
636
637 229
		$oldsort = '';
638 229
		$id = $this->getDatabaseIdAndSort( $title, $namespace, $iw, $subobjectName, $oldsort, $canonical, $fetchHashes );
639 229
		$db = $this->store->getConnection( 'mw.db' );
640
641 229
		$db->beginAtomicTransaction( __METHOD__ );
642
643 229
		if ( $id == 0 ) {
644 224
			$sortkey = $sortkey ? $sortkey : ( str_replace( '_', ' ', $title ) );
645 224
			$sequenceValue = $db->nextSequenceValue( $this->getIdTable() . '_smw_id_seq' ); // Bug 42659
646
647 224
			$db->insert(
648 224
				self::TABLE_NAME,
649
				array(
650 224
					'smw_id' => $sequenceValue,
651 224
					'smw_title' => $title,
652 224
					'smw_namespace' => $namespace,
653 224
					'smw_iw' => $iw,
654 224
					'smw_subobject' => $subobjectName,
655 224
					'smw_sortkey' => $sortkey
656
				),
657 224
				__METHOD__
658
			);
659
660 224
			$id = (int)$db->insertId();
661
662
			// Properties also need to be in the property statistics table
663 224
			if( $namespace === SMW_NS_PROPERTY ) {
664
665 172
				$statsStore = new PropertyStatisticsTable(
666
					$db,
667 172
					SMWSQLStore3::PROPERTY_STATISTICS_TABLE
668
				);
669
670 172
				$statsStore->insertUsageCount( $id, 0 );
671
			}
672
673 224
			$this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
674
675 224
			if ( $fetchHashes ) {
676 224
				$this->setPropertyTableHashesCache( $id, null );
677
			}
678
679 207
		} elseif ( $sortkey !== '' && $sortkey != $oldsort ) {
680 18
			$db->update(
681 18
				self::TABLE_NAME,
682 18
				array( 'smw_sortkey' => $sortkey ),
683 18
				array( 'smw_id' => $id ),
684 18
				__METHOD__
685
			);
686
687 18
			$this->setCache( $title, $namespace, $iw, $subobjectName, $id, $sortkey );
688
		}
689
690 229
		$db->endAtomicTransaction( __METHOD__ );
691
692 229
		return $id;
693
	}
694
695
	/**
696
	 * Properties have a mechanisms for being predefined (i.e. in PHP instead
697
	 * of in wiki). Special "interwiki" prefixes separate the ids of such
698
	 * predefined properties from the ids for the current pages (which may,
699
	 * e.g., be moved, while the predefined object is not movable).
700
	 *
701
	 * @todo This documentation is out of date. Right now, the special
702
	 * interwiki is used only for special properties without a label, i.e.,
703
	 * which cannot be shown to a user. This allows us to filter such cases
704
	 * from all queries that retrieve lists of properties. It should be
705
	 * checked that this is really the only use that this has throughout
706
	 * the code.
707
	 *
708
	 * @since 1.8
709
	 * @param SMWDIProperty $property
710
	 * @return string
711
	 */
712 222
	public function getPropertyInterwiki( SMWDIProperty $property ) {
713 222
		return ( $property->getLabel() !== '' ) ? '' : SMW_SQL3_SMWINTDEFIW;
714
	}
715
716
	/**
717
	 * @since  2.1
718
	 *
719
	 * @param integer $sid
720
	 * @param DIWikiPage $subject
721
	 * @param integer|string|null $interWiki
722
	 */
723 196
	public function updateInterwikiField( $sid, DIWikiPage $subject, $interWiki = null ) {
724
725 196
		$this->store->getConnection()->update(
726 196
			self::TABLE_NAME,
727 196
			array( 'smw_iw' => $interWiki !== null ? $interWiki : $subject->getInterWiki() ),
728 196
			array( 'smw_id' => $sid ),
729 196
			__METHOD__
730
		);
731
732 196
		$this->setCache(
733 196
			$subject->getDBKey(),
734 196
			$subject->getNamespace(),
735 196
			$subject->getInterWiki(),
736 196
			$subject->getSubobjectName(),
737
			$sid,
738 196
			$subject->getSortKey()
739
		);
740 196
	}
741
742
	/**
743
	 * Fetch the ID for an SMWDIProperty object. This method achieves the
744
	 * same as getSMWPageID(), but avoids additional normalization steps
745
	 * that have already been performed when creating an SMWDIProperty
746
	 * object.
747
	 *
748
	 * @note There is no distinction between properties and inverse
749
	 * properties here. A property and its inverse have the same ID in SMW.
750
	 *
751
	 * @param SMWDIProperty $property
752
	 * @return integer
753
	 */
754 224
	public function getSMWPropertyID( SMWDIProperty $property ) {
755 224
		if ( array_key_exists( $property->getKey(), self::$special_ids ) ) {
756 223
			return self::$special_ids[$property->getKey()];
757
		} else {
758 222
			$sortkey = '';
759 222
			return $this->getDatabaseIdAndSort( $property->getKey(), SMW_NS_PROPERTY, $this->getPropertyInterwiki( $property ), '', $sortkey, true, false );
760
		}
761
	}
762
763
	/**
764
	 * Fetch and possibly create the ID for an SMWDIProperty object. The
765
	 * method achieves the same as getSMWPageID() but avoids additional
766
	 * normalization steps that have already been performed when creating
767
	 * an SMWDIProperty object.
768
	 *
769
	 * @see getSMWPropertyID
770
	 * @param SMWDIProperty $property
771
	 * @return integer
772
	 */
773 206
	public function makeSMWPropertyID( SMWDIProperty $property ) {
774 206
		if ( array_key_exists( $property->getKey(), self::$special_ids ) ) {
775 176
			return (int)self::$special_ids[$property->getKey()];
776
		} else {
777 181
			return (int)$this->makeDatabaseId(
778 181
				$property->getKey(),
779 181
				SMW_NS_PROPERTY,
780 181
				$this->getPropertyInterwiki( $property ),
781 181
				'',
782 181
				true,
783 181
				$property->getLabel(),
784 181
				false
785
			);
786
		}
787
	}
788
789
	/**
790
	 * Normalize the information for an SMW object (page etc.) and return
791
	 * the predefined ID if any. All parameters are call-by-reference and
792
	 * will be changed to perform any kind of built-in normalization that
793
	 * SMW requires. This mainly applies to predefined properties that
794
	 * should always use their property key as a title, have fixed
795
	 * sortkeys, etc. Some very special properties also have fixed IDs that
796
	 * do not require any DB lookups. In such cases, the method returns
797
	 * this ID; otherwise it returns 0.
798
	 *
799
	 * @note This function could be extended to account for further kinds
800
	 * of normalization and predefined ID. However, both getSMWPropertyID
801
	 * and makeSMWPropertyID must then also be adjusted to do the same.
802
	 *
803
	 * @since 1.8
804
	 * @param string $title DB key
805
	 * @param integer $namespace namespace
806
	 * @param string $iw interwiki prefix
807
	 * @param string $subobjectName
808
	 * @param string $sortkey
809
	 * @return integer predefined id or 0 if none
810
	 */
811 253
	protected function getPredefinedData( &$title, &$namespace, &$iw, &$subobjectName, &$sortkey ) {
812 253
		if ( $namespace == SMW_NS_PROPERTY &&
813 253
			( $iw === '' || $iw == SMW_SQL3_SMWINTDEFIW ) && $title != '' ) {
814
815
			// Check if this is a predefined property:
816 183
			if ( $title{0} != '_' ) {
817
				// This normalization also applies to
818
				// subobjects of predefined properties.
819 183
				$newTitle = SMW\DIProperty::findPropertyID( str_replace( '_', ' ', $title ) );
0 ignored issues
show
Deprecated Code introduced by
The method SMW\DIProperty::findPropertyID() has been deprecated with message: since 2.1, use PropertyRegistry::findPropertyIdByLabel

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
820 183
				if ( $newTitle ) {
821 61
					$title = $newTitle;
822 61
					$sortkey = SMW\DIProperty::findPropertyLabel( $title );
0 ignored issues
show
Deprecated Code introduced by
The method SMW\DIProperty::findPropertyLabel() has been deprecated with message: since 2.1, use PropertyRegistry::findPropertyLabelById

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
823 61
					if ( $sortkey === '' ) {
824
						$iw = SMW_SQL3_SMWINTDEFIW;
825
					}
826
				}
827
			}
828
829
			// Check if this is a property with a fixed SMW ID:
830 183
			if ( $subobjectName === '' && array_key_exists( $title, self::$special_ids ) ) {
831 51
				return self::$special_ids[$title];
832
			}
833
		}
834
835 250
		return 0;
836
	}
837
838
	/**
839
	 * Change an internal id to another value. If no target value is given, the
840
	 * value is changed to become the last id entry (based on the automatic id
841
	 * increment of the database). Whatever currently occupies this id will be
842
	 * moved consistently in all relevant tables. Whatever currently occupies
843
	 * the target id will be ignored (it should be ensured that nothing is
844
	 * moved to an id that is still in use somewhere).
845
	 *
846
	 * @since 1.8
847
	 * @param integer $curid
848
	 * @param integer $targetid
849
	 */
850 1
	public function moveSMWPageID( $curid, $targetid = 0 ) {
851 1
		$db = $this->store->getConnection();
852
853 1
		$row = $db->selectRow(
854 1
			self::TABLE_NAME,
855 1
			'*',
856 1
			array( 'smw_id' => $curid ),
857 1
			__METHOD__
858
		);
859
860 1
		if ( $row === false ) {
861 1
			return; // no id at current position, ignore
862
		}
863
864
		$db->beginAtomicTransaction( __METHOD__ );
865
866
		if ( $targetid == 0 ) { // append new id
867
			$sequenceValue = $db->nextSequenceValue( $this->getIdTable() . '_smw_id_seq' ); // Bug 42659
868
869
			$db->insert(
870
				self::TABLE_NAME,
871
				array(
872
					'smw_id' => $sequenceValue,
873
					'smw_title' => $row->smw_title,
874
					'smw_namespace' => $row->smw_namespace,
875
					'smw_iw' => $row->smw_iw,
876
					'smw_subobject' => $row->smw_subobject,
877
					'smw_sortkey' => $row->smw_sortkey
878
				),
879
				__METHOD__
880
			);
881
882
			$targetid = $db->insertId();
883
		} else { // change to given id
884
			$db->insert(
885
				self::TABLE_NAME,
886
				array( 'smw_id' => $targetid,
887
					'smw_title' => $row->smw_title,
888
					'smw_namespace' => $row->smw_namespace,
889
					'smw_iw' => $row->smw_iw,
890
					'smw_subobject' => $row->smw_subobject,
891
					'smw_sortkey' => $row->smw_sortkey
892
				),
893
				__METHOD__
894
			);
895
		}
896
897
		$db->delete(
898
			self::TABLE_NAME,
899
			array(
900
				'smw_id' => $curid
901
			),
902
			__METHOD__
903
		);
904
905
		$this->setCache(
906
			$row->smw_title,
907
			$row->smw_namespace,
908
			$row->smw_iw,
909
			$row->smw_subobject,
910
			$targetid,
911
			$row->smw_sortkey
912
		);
913
914
		$this->store->changeSMWPageID(
915
			$curid,
916
			$targetid,
917
			$row->smw_namespace,
918
			$row->smw_namespace
919
		);
920
921
		$db->endAtomicTransaction( __METHOD__ );
922
	}
923
924
	/**
925
	 * Add or modify a cache entry. The key consists of the
926
	 * parameters $title, $namespace, $interwiki, and $subobject. The
927
	 * cached data is $id and $sortkey.
928
	 *
929
	 * @since 1.8
930
	 * @param string $title
931
	 * @param integer $namespace
932
	 * @param string $interwiki
933
	 * @param string $subobject
934
	 * @param integer $id
935
	 * @param string $sortkey
936
	 */
937 254
	public function setCache( $title, $namespace, $interwiki, $subobject, $id, $sortkey ) {
938 254
		if ( strpos( $title, ' ' ) !== false ) {
939
			throw new MWException("Somebody tried to use spaces in a cache title! ($title)");
940
		}
941
942 254
		$hashKey = HashBuilder::createHashIdFromSegments( $title, $namespace, $interwiki, $subobject );
0 ignored issues
show
Deprecated Code introduced by
The method SMW\HashBuilder::createHashIdFromSegments() has been deprecated with message: since 2.4, use Hash::createFromSegments

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
943
944 254
		if ( $namespace == SMW_NS_PROPERTY && $interwiki === '' && $subobject === '' ) {
945 230
			$this->checkPropertySizeLimit();
946 230
			$this->prop_ids[$title] = $id;
947 230
			$this->prop_sortkeys[$title] = $sortkey;
948
		} else {
949 241
			$this->checkRegularSizeLimit();
950 241
			$this->regular_ids[$hashKey] = $id;
951 241
			$this->regular_sortkeys[$hashKey] = $sortkey;
952
		}
953
954 254
		$this->idToDataItemMatchFinder->saveToCache( $id, $hashKey );
955 254
		$this->intermediaryIdCache->save( $hashKey, $id );
956
957 254
		if ( $interwiki == SMW_SQL3_SMWREDIIW ) { // speed up detection of redirects when fetching IDs
958 216
			$this->setCache(  $title, $namespace, '', $subobject, 0, '' );
959
		}
960 254
	}
961
962
	/**
963
	 * @since 2.1
964
	 *
965
	 * @param integer $id
966
	 *
967
	 * @return DIWikiPage|null
968
	 */
969 222
	public function getDataItemForId( $id ) {
970 222
		return $this->idToDataItemMatchFinder->getDataItemForId( $id );
971
	}
972
973
	/**
974
	 * @since 2.3
975
	 *
976
	 * @param integer $id
0 ignored issues
show
Bug introduced by
There is no parameter named $id. 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...
977
	 *
978
	 * @return string[]
979
	 */
980
	public function getDataItemPoolHashListFor( array $idlist ) {
981
		return $this->idToDataItemMatchFinder->getDataItemPoolHashListFor( $idlist );
982
	}
983
984
	/**
985
	 * Get a cached SMW ID, or false if no cache entry is found.
986
	 *
987
	 * @since 1.8
988
	 * @param string $title
989
	 * @param integer $namespace
990
	 * @param string $interwiki
991
	 * @param string $subobject
992
	 * @return integer|boolean
993
	 */
994 251
	protected function getCachedId( $title, $namespace, $interwiki, $subobject ) {
995 251
		if ( $namespace == SMW_NS_PROPERTY && $interwiki === '' && $subobject === '' ) {
996 234
			if ( array_key_exists( $title, $this->prop_ids ) ) {
997 213
				$this->prophit_debug++;
998 213
				return (int)$this->prop_ids[$title];
999
			} else {
1000 230
				$this->propmiss_debug++;
1001 230
				return false;
1002
			}
1003
		} else {
1004 238
			$hashKey = HashBuilder::createHashIdFromSegments( $title, $namespace, $interwiki, $subobject );
0 ignored issues
show
Deprecated Code introduced by
The method SMW\HashBuilder::createHashIdFromSegments() has been deprecated with message: since 2.4, use Hash::createFromSegments

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
1005 238
			if ( array_key_exists( $hashKey, $this->regular_ids ) ) {
1006 219
				$this->reghit_debug++;
1007 219
				return (int)$this->regular_ids[$hashKey];
1008
			} else {
1009 238
				$this->regmiss_debug++;
1010 238
				return false;
1011
			}
1012
		}
1013
	}
1014
1015
	/**
1016
	 * Get a cached SMW sortkey, or false if no cache entry is found.
1017
	 *
1018
	 * @since 1.8
1019
	 * @param string $title
1020
	 * @param integer $namespace
1021
	 * @param string $interwiki
1022
	 * @param string $subobject
1023
	 * @return string|boolean
1024
	 */
1025 240
	protected function getCachedSortKey( $title, $namespace, $interwiki, $subobject ) {
1026 240
		if ( $namespace == SMW_NS_PROPERTY && $interwiki === '' && $subobject === '' ) {
1027 213
			if ( array_key_exists( $title, $this->prop_sortkeys ) ) {
1028 213
				return $this->prop_sortkeys[$title];
1029
			} else {
1030
				return false;
1031
			}
1032
		} else {
1033 240
			$hashKey = HashBuilder::createHashIdFromSegments( $title, $namespace, $interwiki, $subobject );
0 ignored issues
show
Deprecated Code introduced by
The method SMW\HashBuilder::createHashIdFromSegments() has been deprecated with message: since 2.4, use Hash::createFromSegments

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
1034 240
			if ( array_key_exists( $hashKey, $this->regular_sortkeys ) ) {
1035 221
				return $this->regular_sortkeys[$hashKey];
1036
			} else {
1037 240
				return false;
1038
			}
1039
		}
1040
	}
1041
1042
	/**
1043
	 * Remove any cache entry for the given data. The key consists of the
1044
	 * parameters $title, $namespace, $interwiki, and $subobject. The
1045
	 * cached data is $id and $sortkey.
1046
	 *
1047
	 * @since 1.8
1048
	 * @param string $title
1049
	 * @param integer $namespace
1050
	 * @param string $interwiki
1051
	 * @param string $subobject
1052
	 */
1053 9
	public function deleteCache( $title, $namespace, $interwiki, $subobject ) {
1054
1055 9
		$hashKey = HashBuilder::createHashIdFromSegments( $title, $namespace, $interwiki, $subobject );
0 ignored issues
show
Deprecated Code introduced by
The method SMW\HashBuilder::createHashIdFromSegments() has been deprecated with message: since 2.4, use Hash::createFromSegments

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
1056
1057 9
		if ( $namespace == SMW_NS_PROPERTY && $interwiki === '' && $subobject === '' ) {
1058 1
			$id =  isset( $this->prop_ids[$title] ) ?  $this->prop_ids[$title] : 0;
1059 1
			unset( $this->prop_ids[$title] );
1060 1
			unset( $this->prop_sortkeys[$title] );
1061
		} else {
1062 9
			$id = isset( $this->regular_ids[$hashKey] ) ? $this->regular_ids[$hashKey] : 0;
1063 9
			unset( $this->regular_ids[$hashKey] );
1064 9
			unset( $this->regular_sortkeys[$hashKey] );
1065
		}
1066
1067 9
		$this->intermediaryIdCache->delete( $hashKey );
1068 9
		$this->idToDataItemMatchFinder->deleteFromCache( $id );
1069 9
	}
1070
1071
	/**
1072
	 * Move all cached information about subobjects.
1073
	 *
1074
	 * @todo This method is neither efficient nor very convincing
1075
	 * architecturally; it should be redesigned.
1076
	 *
1077
	 * @since 1.8
1078
	 * @param string $oldtitle
1079
	 * @param integer $oldnamespace
1080
	 * @param string $newtitle
1081
	 * @param integer $newnamespace
1082
	 */
1083 21
	public function moveSubobjects( $oldtitle, $oldnamespace, $newtitle, $newnamespace ) {
0 ignored issues
show
Unused Code introduced by
The parameter $oldtitle is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $newtitle is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1084
		// Currently we have no way to change title and namespace across all entries.
1085
		// Best we can do is clear the cache to avoid wrong hits:
1086 21
		if ( $oldnamespace == SMW_NS_PROPERTY || $newnamespace == SMW_NS_PROPERTY ) {
1087 6
			$this->prop_ids = array();
1088 6
			$this->prop_sortkeys = array();
1089
		}
1090 21
		if ( $oldnamespace != SMW_NS_PROPERTY || $newnamespace != SMW_NS_PROPERTY ) {
1091 16
			$this->regular_ids = array();
1092 16
			$this->regular_sortkeys = array();
1093
		}
1094 21
	}
1095
1096
	/**
1097
	 * Delete all cached information.
1098
	 *
1099
	 * @since 1.8
1100
	 */
1101 20
	public function clearCaches() {
1102 20
		$this->prop_ids = array();
1103 20
		$this->prop_sortkeys = array();
1104 20
		$this->regular_ids = array();
1105 20
		$this->regular_sortkeys = array();
1106 20
		$this->idToDataItemMatchFinder->clear();
1107 20
	}
1108
1109
	/**
1110
	 * Ensure that the property ID and sortkey caches have space to insert
1111
	 * at least one more element. If not, some other entries will be unset.
1112
	 *
1113
	 * @since 1.8
1114
	 */
1115 230
	protected function checkPropertySizeLimit() {
1116 230
		if ( count( $this->prop_ids ) >= self::$PROP_CACHE_MAX_SIZE ) {
1117
			$keys = array_rand( $this->prop_ids, 10 );
1118
			foreach ( $keys as $key ) {
1119
				unset( $this->prop_ids[$key] );
1120
				unset( $this->prop_sortkeys[$key] );
1121
			}
1122
		}
1123 230
	}
1124
1125
	/**
1126
	 * Ensure that the non-property ID and sortkey caches have space to
1127
	 * insert at least one more element. If not, some other entries will be
1128
	 * unset.
1129
	 *
1130
	 * @since 1.8
1131
	 */
1132 241
	protected function checkRegularSizeLimit() {
1133 241
		if ( count( $this->regular_ids ) >= self::$PAGE_CACHE_MAX_SIZE ) {
1134
			$keys = array_rand( $this->regular_ids, 10 );
1135
			foreach ( $keys as $key ) {
1136
				unset( $this->regular_ids[$key] );
1137
				unset( $this->regular_sortkeys[$key] );
1138
			}
1139
		}
1140 241
	}
1141
1142
	/**
1143
	 * Return an array of hashes with table names as keys. These
1144
	 * hashes are used to compare new data with old data for each
1145
	 * property-value table when updating data
1146
	 *
1147
	 * @since 1.8
1148
	 *
1149
	 * @param integer $subjectId ID of the page as stored in the SMW IDs table
1150
	 *
1151
	 * @return array
1152
	 */
1153 221
	public function getPropertyTableHashes( $subjectId ) {
0 ignored issues
show
Coding Style introduced by
getPropertyTableHashes uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1154 221
		$hash = null;
1155 221
		$db = $this->store->getConnection();
1156
1157 221
		if ( $this->hashCacheId == $subjectId ) {
1158 221
			$hash = $this->hashCacheContents;
1159 180
		} elseif ( $subjectId !== 0 ) {
1160
1161 180
			$row = $db->selectRow(
1162 180
				self::TABLE_NAME,
1163 180
				array( 'smw_proptable_hash' ),
1164 180
				'smw_id=' . $subjectId,
1165 180
				__METHOD__
1166
			);
1167
1168 180
			if ( $row !== false ) {
1169 180
				$hash = $row->smw_proptable_hash;
1170
			}
1171
		}
1172
1173 221
		if ( $hash !== null && $GLOBALS['wgDBtype'] == 'postgres' ) {
1174
			$hash = pg_unescape_bytea( $hash );
1175
		}
1176
1177 221
		return is_null( $hash ) ? array() : unserialize( $hash );
1178
	}
1179
1180
	/**
1181
	 * Update the proptable_hash for a given page.
1182
	 *
1183
	 * @since 1.8
1184
	 * @param integer $sid ID of the page as stored in SMW IDs table
1185
	 * @param string[] of hash values with table names as keys
1186
	 */
1187 206
	public function setPropertyTableHashes( $sid, array $newTableHashes ) {
1188 206
		$db = $this->store->getConnection();
1189 206
		$propertyTableHash = serialize( $newTableHashes );
1190
1191 206
		$db->update(
1192 206
			self::TABLE_NAME,
1193 206
			array( 'smw_proptable_hash' => $propertyTableHash ),
1194 206
			array( 'smw_id' => $sid ),
1195 206
			__METHOD__
1196
		);
1197
1198 206
		if ( $sid == $this->hashCacheId ) {
1199 206
			$this->setPropertyTableHashesCache( $sid, $propertyTableHash );
1200
		}
1201 206
	}
1202
1203
	/**
1204
	 * Temporarily cache a property tablehash that has been retrieved for
1205
	 * the given SMW ID.
1206
	 *
1207
	 * @since 1.8
1208
	 * @param $id integer
1209
	 * @param $propertyTableHash string
1210
	 */
1211 225
	protected function setPropertyTableHashesCache( $id, $propertyTableHash ) {
1212 225
		if ( $id == 0 ) {
1213 2
			return; // never cache 0
1214
		}
1215
		//print "Cache set for $id.\n";
1216 225
		$this->hashCacheId = $id;
1217 225
		$this->hashCacheContents = $propertyTableHash;
1218 225
	}
1219
1220
	/**
1221
	 * Simple helper method for debugging cache performance. Prints
1222
	 * statistics about the SMWSql3SmwIds object created last.
1223
	 * The following code can be used in LocalSettings.php to enable
1224
	 * this in a wiki:
1225
	 *
1226
	 * $wgHooks['SkinAfterContent'][] = 'showCacheStats';
1227
	 * function showCacheStats() {
1228
	 *   self::debugDumpCacheStats();
1229
	 *   return true;
1230
	 * }
1231
	 *
1232
	 * @note This is a debugging/profiling method that no published code
1233
	 * should rely on.
1234
	 *
1235
	 * @since 1.8
1236
	 */
1237
	public static function debugDumpCacheStats() {
1238
		$that = self::$singleton_debug;
1239
		if ( is_null( $that ) ) {
1240
			return;
1241
		}
1242
1243
		$debugString =
1244
			"Statistics for SMWSql3SmwIds:\n" .
1245
			"- Executed {$that->selectrow_sort_debug} selects for sortkeys.\n" .
1246
			"- Executed {$that->selectrow_redi_debug} selects for redirects.\n" .
1247
			"- Regular cache hits: {$that->reghit_debug} misses: {$that->regmiss_debug}";
1248
		if ( $that->regmiss_debug + $that->reghit_debug > 0 ) {
1249
			$debugString .= " rate: " . round( $that->reghit_debug/( $that->regmiss_debug + $that->reghit_debug ), 3 );
1250
		}
1251
		$debugString .= " cache size: " . count( $that->regular_ids ) . "\n";
1252
		$debugString .= "- Property cache hits: {$that->prophit_debug} misses: {$that->propmiss_debug}";
1253
		if ( $that->propmiss_debug + $that->prophit_debug > 0 ) {
1254
			$debugString .= " rate: " . round( $that->prophit_debug/( $that->propmiss_debug + $that->prophit_debug ), 3 );
1255
		}
1256
		$debugString .= " cache size: " . count( $that->prop_ids ) . "\n";
1257
		wfDebug( $debugString );
1258
	}
1259
1260
	/**
1261
	 * Returns store Id table name
1262
	 *
1263
	 * @return string
1264
	 */
1265 225
	public function getIdTable() {
1266 225
		return self::TABLE_NAME;
1267
	}
1268
1269
}
1270