00001 <?php
00002
# Cache for article titles (prefixed DB keys) and ids linked from one source
00003
00004
# These are used in incrementalSetup()
00005 define ('LINKCACHE_GOOD', 0);
00006 define ('LINKCACHE_BAD', 1);
00007 define ('LINKCACHE_IMAGE', 2);
00008
00009 class LinkCache {
00010
00011
00012 var
$mClassVer = 2;
00013
00014 var
$mGoodLinks,
$mBadLinks,
$mActive;
00015 var
$mImageLinks,
$mCategoryLinks;
00016 var
$mPreFilled,
$mOldGoodLinks,
$mOldBadLinks;
00017
00018 function
getKey( $title ) {
00019 global
$wgDBname;
00020
return "$wgDBname:lc:title:$title";
00021 }
00022
00023 function
LinkCache()
00024 {
00025 $this->mActive =
true;
00026 $this->mPreFilled =
false;
00027 $this->mGoodLinks = array();
00028 $this->mBadLinks = array();
00029 $this->mImageLinks = array();
00030 $this->mCategoryLinks = array();
00031 $this->mOldGoodLinks = array();
00032 $this->mOldBadLinks = array();
00033 }
00034
00035 function
getGoodLinkID( $title )
00036 {
00037
if ( array_key_exists( $
title, $this->mGoodLinks ) ) {
00038
return $this->mGoodLinks[
$title];
00039 }
else {
00040
return 0;
00041 }
00042 }
00043
00044 function
isBadLink( $title )
00045 {
00046
return array_key_exists( $
title, $this->mBadLinks );
00047 }
00048
00049 function
addGoodLink( $
id, $title )
00050 {
00051
if ( $this->mActive ) {
00052 $this->mGoodLinks[
$title] = $id;
00053 }
00054 }
00055
00056 function
addBadLink( $title )
00057 {
00058
if ( $this->mActive && ( ! $this->isBadLink( $
title ) ) ) {
00059 $this->mBadLinks[
$title] = 1;
00060 }
00061 }
00062
00063 function
addImageLink( $title )
00064 {
00065
if ( $this->mActive ) { $this->mImageLinks[
$title] = 1; }
00066 }
00067
00068 function
addImageLinkObj( $nt )
00069 {
00070
if ( $this->mActive ) { $this->mImageLinks[$nt->getDBkey()] = 1; }
00071 }
00072
00073 function
addCategoryLink( $title, $sortkey ) {
00074
if ( $this->mActive ) { $this->mCategoryLinks[
$title] = $sortkey; }
00075 }
00076
00077 function
addCategoryLinkObj( &$nt, $sortkey ) {
00078 $this->addCategoryLink( $nt->getDBkey(), $sortkey );
00079 }
00080
00081 function
clearBadLink( $title )
00082 {
00083 unset( $this->mBadLinks[$
title] );
00084 $this->
clearLink( $
title );
00085 }
00086
00087 function
clearLink( $title )
00088 {
00089 global
$wgMemc,
$wgLinkCacheMemcached;
00090
if(
$wgLinkCacheMemcached )
00091
$wgMemc->delete( $this->getKey( $
title ) );
00092 }
00093
00094 function
suspend() { $this->mActive =
false; }
00095 function
resume() { $this->mActive =
true; }
00096 function
getGoodLinks() {
return $this->mGoodLinks; }
00097 function
getBadLinks() {
return array_keys( $this->mBadLinks ); }
00098 function
getImageLinks() {
return $this->mImageLinks; }
00099 function
getCategoryLinks() {
return $this->mCategoryLinks; }
00100
00101 function
addLink( $title )
00102 {
00103 $nt = Title::newFromDBkey( $
title );
00104
if( $nt ) {
00105
return $this->
addLinkObj( $nt );
00106 }
else {
00107
return 0;
00108 }
00109 }
00110
00111 function
addLinkObj( &$nt )
00112 {
00113 global
$wgMemc,
$wgLinkCacheMemcached;
00114
$title = $nt->getPrefixedDBkey();
00115
if ( $this->isBadLink( $
title ) ) {
return 0; }
00116 $id = $this->getGoodLinkID( $
title );
00117
if ( 0 != $id ) {
return $id; }
00118
00119 $fname =
"LinkCache::addLinkObj";
00120
wfProfileIn( $fname );
00121
00122 $ns = $nt->getNamespace();
00123 $t = $nt->getDBkey();
00124
00125
if (
"" ==
$title ) {
00126
wfProfileOut( $fname );
00127
return 0;
00128 }
00129
00130 $id = NULL;
00131
if(
$wgLinkCacheMemcached )
00132 $id =
$wgMemc->get( $key = $this->getKey( $
title ) );
00133
if( ! is_integer( $
id ) ) {
00134
$sql =
"SELECT cur_id FROM cur WHERE cur_namespace=" .
00135
"{$ns} AND cur_title='" .
wfStrencode( $t ) .
"'";
00136
$res =
wfQuery( $sql,
DB_READ,
"LinkCache::addLink" );
00137
00138
if ( 0 ==
wfNumRows( $res ) ) {
00139 $id = 0;
00140 }
else {
00141
$s =
wfFetchObject( $res );
00142 $id =
$s->cur_id;
00143 }
00144
if(
$wgLinkCacheMemcached )
00145
$wgMemc->add( $key, $
id, 3600*24 );
00146 }
00147
00148
if ( 0 == $id ) { $this->addBadLink( $
title ); }
00149
else { $this->addGoodLink( $
id, $
title ); }
00150
wfProfileOut( $fname );
00151
return $id;
00152 }
00153
00154 function
preFill( &$fromtitle )
00155 {
00156 global
$wgEnablePersistentLC;
00157
00158 $fname =
"LinkCache::preFill";
00159
wfProfileIn( $fname );
00160
# Note -- $fromtitle is a Title *object*
00161
00162 $this->
suspend();
00163 $id = $fromtitle->getArticleID();
00164 $this->
resume();
00165
00166
if( $id == 0 ) {
00167
wfDebug(
"$fname - got id 0 for title '" . $fromtitle->getPrefixedDBkey() .
"'\n" );
00168
wfProfileOut( $fname );
00169
return;
00170 }
00171
00172
if (
$wgEnablePersistentLC ) {
00173
if( $this->
fillFromLinkscc( $
id ) ){
00174
wfProfileOut( $fname );
00175
return;
00176 }
00177 }
00178
00179
$sql =
"SELECT cur_id,cur_namespace,cur_title
00180
FROM cur,links
00181
WHERE cur_id=l_to AND l_from=$id";
00182
$res =
wfQuery( $sql,
DB_READ, $fname );
00183
while(
$s =
wfFetchObject( $res ) ) {
00184 $this->addGoodLink( $s->cur_id,
00185 Title::makeName( $s->cur_namespace, $s->cur_title )
00186 );
00187 }
00188
00189
$sql =
"SELECT bl_to
00190
FROM brokenlinks
00191
WHERE bl_from='{$id}'";
00192
$res =
wfQuery( $sql,
DB_READ,
"LinkCache::preFill" );
00193
while(
$s =
wfFetchObject( $res ) ) {
00194 $this->addBadLink( $s->bl_to );
00195 }
00196
00197 $this->mOldBadLinks = $this->mBadLinks;
00198 $this->mOldGoodLinks = $this->mGoodLinks;
00199 $this->mPreFilled =
true;
00200
00201
if (
$wgEnablePersistentLC ) {
00202 $this->
saveToLinkscc( $
id );
00203 }
00204
wfProfileOut( $fname );
00205 }
00206
00207 function
getGoodAdditions()
00208 {
00209
return array_diff( $this->mGoodLinks, $this->mOldGoodLinks );
00210 }
00211
00212 function
getBadAdditions()
00213 {
00214
#wfDebug( "mOldBadLinks: " . implode( ', ', array_keys( $this->mOldBadLinks ) ) . "\n" );
00215
#wfDebug( "mBadLinks: " . implode( ', ', array_keys( $this->mBadLinks ) ) . "\n" );
00216
return array_values( array_diff( array_keys( $this->mBadLinks ), array_keys( $this->mOldBadLinks ) ) );
00217 }
00218
00219 function
getImageAdditions()
00220 {
00221
return array_diff_assoc( $this->mImageLinks, $this->mOldImageLinks );
00222 }
00223
00224 function
getGoodDeletions()
00225 {
00226
return array_diff( $this->mOldGoodLinks, $this->mGoodLinks );
00227 }
00228
00229 function
getBadDeletions()
00230 {
00231
return array_values( array_diff( array_keys( $this->mOldBadLinks ), array_keys( $this->mBadLinks ) ));
00232 }
00233
00234 function
getImageDeletions()
00235 {
00236
return array_diff_assoc( $this->mOldImageLinks, $this->mImageLinks );
00237 }
00238
00239
# Parameters: $which is one of the LINKCACHE_xxx constants, $del and $add are
00240
# the incremental update arrays which will be filled. Returns whether or not it's
00241
# worth doing the incremental version. For example, if [[List of mathematical topics]]
00242
# was blanked, it would take a long, long time to do incrementally.
00243 function
incrementalSetup( $which, &$del, &$add )
00244 {
00245
if ( ! $this->mPreFilled ) {
00246
return false;
00247 }
00248
00249
switch ( $which ) {
00250
case LINKCACHE_GOOD:
00251 $old =& $this->mOldGoodLinks;
00252 $cur =& $this->mGoodLinks;
00253 $del = $this->
getGoodDeletions();
00254 $add = $this->
getGoodAdditions();
00255
break;
00256
case LINKCACHE_BAD:
00257 $old =& $this->mOldBadLinks;
00258 $cur =& $this->mBadLinks;
00259 $del = $this->
getBadDeletions();
00260 $add = $this->
getBadAdditions();
00261
break;
00262
default: #
LINKCACHE_IMAGE
00263
return false;
00264 }
00265
00266
return true;
00267 }
00268
00269
# Clears cache but leaves old preFill copies alone
00270 function
clear()
00271 {
00272 $this->mGoodLinks = array();
00273 $this->mBadLinks = array();
00274 $this->mImageLinks = array();
00275 }
00276
00277 function
fillFromLinkscc( $
id ){
00278 $id = IntVal( $
id );
00279
$res =
wfQuery(
"SELECT lcc_cacheobj FROM linkscc WHERE lcc_pageid = $id",
00280
DB_READ);
00281
$row =
wfFetchObject( $res );
00282
if(
$row == FALSE)
00283
return false;
00284
00285 $cacheobj =
false;
00286
if( function_exists(
"gzuncompress" ) )
00287 $cacheobj = @gzuncompress( $row->lcc_cacheobj );
00288
00289
if($cacheobj == FALSE){
00290 $cacheobj =
$row->lcc_cacheobj;
00291 }
00292 $cc = @unserialize( $cacheobj );
00293
if( isset( $cc->mClassVer ) and ($cc->mClassVer == $this->mClassVer ) ){
00294 $this->mOldGoodLinks = $this->mGoodLinks = $cc->mGoodLinks;
00295 $this->mOldBadLinks = $this->mBadLinks = $cc->mBadLinks;
00296 $this->mPreFilled =
true;
00297
return TRUE;
00298 }
else {
00299
return FALSE;
00300 }
00301
00302 }
00303
00304 function
saveToLinkscc( $pid ){
00305 global
$wgCompressedPersistentLC,
$wgIsMySQL;
00306
if(
$wgCompressedPersistentLC and function_exists(
"gzcompress" ) ) {
00307 $ser =
wfStrencode( gzcompress( serialize( $
this ), 3 ));
00308 }
else {
00309 $ser =
wfStrencode( serialize( $
this ) );
00310 }
00311
if (
$wgIsMySQL) {
00312
wfQuery(
"REPLACE INTO linkscc(lcc_pageid,lcc_cacheobj) " .
00313
"VALUES({$pid}, '{$ser}')",
DB_WRITE);
00314 }
else {
00315
wfQuery(
"DELETE FROM linkscc WHERE lcc_pageid={$pid}",
DB_WRITE);
00316
wfQuery(
"INSERT INTO linkscc(lcc_pageid,lcc_cacheobj) " .
00317
"VALUES({$pid}, '{$ser}')",
DB_WRITE);
00318 }
00319 }
00320
00321
# $pid is a page id
00322 function
linksccClearLinksTo( $pid ){
00323 global
$wgEnablePersistentLC,
$wgIsMySQL;
00324
if (
$wgEnablePersistentLC ) {
00325 $pid = intval( $pid );
00326
if (
$wgIsMySQL) {
00327
wfQuery(
"DELETE linkscc FROM linkscc,links ".
00328
"WHERE lcc_pageid=links.l_from AND l_to={$pid}",
DB_WRITE);
00329 }
else {
00330
wfQuery(
"DELETE FROM linkscc WHERE lcc_pageid IN ".
00331
"(SELECT l_from FROM links WHERE l_to={$pid})",
DB_WRITE);
00332 }
00333
wfQuery(
"DELETE FROM linkscc WHERE lcc_pageid='{$pid}'",
DB_WRITE);
00334 }
00335 }
00336
00337
# $title is a prefixed db title, for example like Title->getPrefixedDBkey() returns.
00338 function
linksccClearBrokenLinksTo( $title ){
00339 global
$wgEnablePersistentLC,
$wgIsMySQL;
00340
if (
$wgEnablePersistentLC ) {
00341
$title =
wfStrencode( $
title );
00342
if (
$wgIsMySQL) {
00343
wfQuery(
"DELETE linkscc FROM linkscc,brokenlinks ".
00344
"WHERE lcc_pageid=bl_from AND bl_to='{$title}'",
DB_WRITE);
00345 }
else {
00346
wfQuery(
"DELETE FROM linkscc WHERE lcc_pageid IN ".
00347
"(SELECT bl_from FROM brokenlinks ".
00348
"WHERE bl_to='{$title}')",
DB_WRITE);
00349 }
00350 }
00351 }
00352
00353
# $pid is a page id
00354 function
linksccClearPage( $pid ){
00355 global
$wgEnablePersistentLC;
00356
if (
$wgEnablePersistentLC ) {
00357 $pid = intval( $pid );
00358
wfQuery(
"DELETE FROM linkscc WHERE lcc_pageid='{$pid}'",
DB_WRITE);
00359 }
00360 }
00361 }
00362 ?>