2 # Cache for article titles (prefixed DB keys) and ids linked from one source
4 # These are used in incrementalSetup()
5 define ('LINKCACHE_GOOD', 0);
6 define ('LINKCACHE_BAD', 1);
7 define ('LINKCACHE_IMAGE', 2);
10 // Increment $mClassVer whenever old serialized versions of this class
11 // becomes incompatible with the new version.
12 /* private */ var $mClassVer = 2;
14 /* private */ var $mGoodLinks, $mBadLinks, $mActive;
15 /* private */ var $mImageLinks, $mCategoryLinks;
16 /* private */ var $mPreFilled, $mOldGoodLinks, $mOldBadLinks;
17 /* private */ var $mForUpdate;
19 /* private */ function getKey( $title ) {
21 return "$wgDBname:lc:title:$title";
26 $this->mActive
= true;
27 $this->mPreFilled
= false;
28 $this->mForUpdate
= false;
29 $this->mGoodLinks
= array();
30 $this->mBadLinks
= array();
31 $this->mImageLinks
= array();
32 $this->mCategoryLinks
= array();
33 $this->mOldGoodLinks
= array();
34 $this->mOldBadLinks
= array();
37 # General accessor to get/set whether SELECT FOR UPDATE should be used
38 function forUpdate( $update = NULL ) {
39 return wfSetVar( $this->mForUpdate
, $update );
42 function getGoodLinkID( $title )
44 if ( array_key_exists( $title, $this->mGoodLinks
) ) {
45 return $this->mGoodLinks
[$title];
51 function isBadLink( $title )
53 return array_key_exists( $title, $this->mBadLinks
);
56 function addGoodLink( $id, $title )
58 if ( $this->mActive
) {
59 $this->mGoodLinks
[$title] = $id;
63 function addBadLink( $title )
65 if ( $this->mActive
&& ( ! $this->isBadLink( $title ) ) ) {
66 $this->mBadLinks
[$title] = 1;
70 function addImageLink( $title )
72 if ( $this->mActive
) { $this->mImageLinks
[$title] = 1; }
75 function addImageLinkObj( $nt )
77 if ( $this->mActive
) { $this->mImageLinks
[$nt->getDBkey()] = 1; }
80 function addCategoryLink( $title, $sortkey ) {
81 if ( $this->mActive
) { $this->mCategoryLinks
[$title] = $sortkey; }
84 function addCategoryLinkObj( &$nt, $sortkey ) {
85 $this->addCategoryLink( $nt->getDBkey(), $sortkey );
88 function clearBadLink( $title )
90 unset( $this->mBadLinks
[$title] );
91 $this->clearLink( $title );
94 function clearLink( $title )
96 global $wgMemc, $wgLinkCacheMemcached;
97 if( $wgLinkCacheMemcached )
98 $wgMemc->delete( $this->getKey( $title ) );
101 function suspend() { $this->mActive
= false; }
102 function resume() { $this->mActive
= true; }
103 function getGoodLinks() { return $this->mGoodLinks
; }
104 function getBadLinks() { return array_keys( $this->mBadLinks
); }
105 function getImageLinks() { return $this->mImageLinks
; }
106 function getCategoryLinks() { return $this->mCategoryLinks
; }
108 function addLink( $title )
110 $nt = Title
::newFromDBkey( $title );
112 return $this->addLinkObj( $nt );
118 function addLinkObj( &$nt )
120 global $wgMemc, $wgLinkCacheMemcached;
121 $title = $nt->getPrefixedDBkey();
122 if ( $this->isBadLink( $title ) ) { return 0; }
123 $id = $this->getGoodLinkID( $title );
124 if ( 0 != $id ) { return $id; }
126 $fname = "LinkCache::addLinkObj";
127 wfProfileIn( $fname );
129 $ns = $nt->getNamespace();
130 $t = $nt->getDBkey();
132 if ( "" == $title ) {
133 wfProfileOut( $fname );
138 if( $wgLinkCacheMemcached )
139 $id = $wgMemc->get( $key = $this->getKey( $title ) );
140 if( ! is_integer( $id ) ) {
141 if ( $this->mForUpdate
) {
142 $db =& wfGetDB( DB_MASTER
);
143 $options = array( 'FOR UPDATE' );
145 $db =& wfGetDB( DB_SLAVE
);
149 $id = $db->getField( 'cur', 'cur_id', array( 'cur_namespace' => $ns, 'cur_title' => $t ), $fname, $options );
153 if( $wgLinkCacheMemcached )
154 $wgMemc->add( $key, $id, 3600*24 );
157 if ( 0 == $id ) { $this->addBadLink( $title ); }
158 else { $this->addGoodLink( $id, $title ); }
159 wfProfileOut( $fname );
163 function preFill( &$fromtitle )
165 global $wgEnablePersistentLC;
167 $fname = "LinkCache::preFill";
168 wfProfileIn( $fname );
169 # Note -- $fromtitle is a Title *object*
172 $id = $fromtitle->getArticleID();
176 wfDebug( "$fname - got id 0 for title '" . $fromtitle->getPrefixedDBkey() . "'\n" );
177 wfProfileOut( $fname );
181 if ( $wgEnablePersistentLC ) {
182 if( $this->fillFromLinkscc( $id ) ){
183 wfProfileOut( $fname );
187 if ( $this->mForUpdate
) {
188 $db =& wfGetDB( DB_MASTER
);
189 $options = 'FOR UPDATE';
191 $db =& wfGetDB( DB_SLAVE
);
195 $cur = $db->tableName( 'cur' );
196 $links = $db->tableName( 'links' );
198 $sql = "SELECT cur_id,cur_namespace,cur_title
200 WHERE cur_id=l_to AND l_from=$id $options";
201 $res = $db->query( $sql, $fname );
202 while( $s = $db->fetchObject( $res ) ) {
203 $this->addGoodLink( $s->cur_id
,
204 Title
::makeName( $s->cur_namespace
, $s->cur_title
)
208 $res = $db->select( 'brokenlinks', array( 'bl_to' ), array( 'bl_from' => $id ), $fname, array( $options ) );
209 while( $s = $db->fetchObject( $res ) ) {
210 $this->addBadLink( $s->bl_to
);
213 $this->mOldBadLinks
= $this->mBadLinks
;
214 $this->mOldGoodLinks
= $this->mGoodLinks
;
215 $this->mPreFilled
= true;
217 if ( $wgEnablePersistentLC ) {
218 $this->saveToLinkscc( $id );
220 wfProfileOut( $fname );
223 function getGoodAdditions()
225 return array_diff( $this->mGoodLinks
, $this->mOldGoodLinks
);
228 function getBadAdditions()
230 #wfDebug( "mOldBadLinks: " . implode( ', ', array_keys( $this->mOldBadLinks ) ) . "\n" );
231 #wfDebug( "mBadLinks: " . implode( ', ', array_keys( $this->mBadLinks ) ) . "\n" );
232 return array_values( array_diff( array_keys( $this->mBadLinks
), array_keys( $this->mOldBadLinks
) ) );
235 function getImageAdditions()
237 return array_diff_assoc( $this->mImageLinks
, $this->mOldImageLinks
);
240 function getGoodDeletions()
242 return array_diff( $this->mOldGoodLinks
, $this->mGoodLinks
);
245 function getBadDeletions()
247 return array_values( array_diff( array_keys( $this->mOldBadLinks
), array_keys( $this->mBadLinks
) ));
250 function getImageDeletions()
252 return array_diff_assoc( $this->mOldImageLinks
, $this->mImageLinks
);
255 # Parameters: $which is one of the LINKCACHE_xxx constants, $del and $add are
256 # the incremental update arrays which will be filled. Returns whether or not it's
257 # worth doing the incremental version. For example, if [[List of mathematical topics]]
258 # was blanked, it would take a long, long time to do incrementally.
259 function incrementalSetup( $which, &$del, &$add )
261 if ( ! $this->mPreFilled
) {
267 $old =& $this->mOldGoodLinks
;
268 $cur =& $this->mGoodLinks
;
269 $del = $this->getGoodDeletions();
270 $add = $this->getGoodAdditions();
273 $old =& $this->mOldBadLinks
;
274 $cur =& $this->mBadLinks
;
275 $del = $this->getBadDeletions();
276 $add = $this->getBadAdditions();
278 default: # LINKCACHE_IMAGE
285 # Clears cache but leaves old preFill copies alone
288 $this->mGoodLinks
= array();
289 $this->mBadLinks
= array();
290 $this->mImageLinks
= array();
293 /* private */ function fillFromLinkscc( $id ){
294 $fname = 'LinkCache::fillFromLinkscc';
297 if ( $this->mForUpdate
) {
298 $db =& wfGetDB( DB_MASTER
);
299 $options = 'FOR UPDATE';
301 $db =& wfGetDB( DB_SLAVE
);
304 $raw = $db->getField( 'linkscc', 'lcc_cacheobj', array( 'lcc_pageid' => $id ), $fname, $options );
305 if ( $raw === false ) {
310 if( function_exists( "gzuncompress" ) )
311 $cacheobj = @gzuncompress
( $raw );
313 if($cacheobj == FALSE){
316 $cc = @unserialize
( $cacheobj );
317 if( isset( $cc->mClassVer
) and ($cc->mClassVer
== $this->mClassVer
) ){
318 $this->mOldGoodLinks
= $this->mGoodLinks
= $cc->mGoodLinks
;
319 $this->mOldBadLinks
= $this->mBadLinks
= $cc->mBadLinks
;
320 $this->mPreFilled
= true;
328 /* private */ function saveToLinkscc( $pid ){
329 global $wgCompressedPersistentLC;
330 if( $wgCompressedPersistentLC and function_exists( "gzcompress" ) ) {
331 $ser = gzcompress( serialize( $this ), 3 );
333 $ser = serialize( $this );
335 $db =& wfGetDB( DB_MASTER
);
336 $db->replace( 'linkscc', array( 'lcc_pageid' ), array( 'lcc_pageid' => $pid, 'lcc_cacheobj' => $ser ) );
339 # Delete linkscc rows which link to here
341 /* static */ function linksccClearLinksTo( $pid ){
342 global $wgEnablePersistentLC;
343 if ( $wgEnablePersistentLC ) {
344 $fname = "LinkCache::linksccClearLinksTo";
345 $pid = intval( $pid );
346 $dbw =& wfGetDB( DB_MASTER
);
347 # Delete linkscc rows which link to here
348 $dbw->deleteJoin( 'linkscc', 'links', 'lcc_pageid', 'l_from', array( 'l_to' => $pid ), $fname );
349 # Delete linkscc row representing this page
350 $dbw->delete( 'linkscc', array( 'lcc_pageid' => $pid ), $fname);
355 # Delete linkscc rows with broken links to here
356 # $title is a prefixed db title, for example like Title->getPrefixedDBkey() returns.
357 /* static */ function linksccClearBrokenLinksTo( $title ){
358 global $wgEnablePersistentLC;
359 $fname = 'LinkCache::linksccClearBrokenLinksTo';
361 if ( $wgEnablePersistentLC ) {
362 $dbw =& wfGetDB( DB_MASTER
);
363 $dbw->deleteJoin( 'linkscc', 'brokenlinks', 'lcc_pageid', 'bl_from', array( 'bl_to' => $title ), $fname );
368 /* static */ function linksccClearPage( $pid ){
369 global $wgEnablePersistentLC;
370 if ( $wgEnablePersistentLC ) {
371 $pid = intval( $pid );
372 $dbw =& wfGetDB( DB_MASTER
);
373 $dbw->delete( 'linkscc', array( 'lcc_pageid' => $pid ) );