MediaWiki  REL1_21
LinksUpdate.php
Go to the documentation of this file.
00001 <?php
00028 class LinksUpdate extends SqlDataUpdate {
00029 
00030         // @todo: make members protected, but make sure extensions don't break
00031 
00032         public $mId,         
00033                 $mTitle,         
00034                 $mParserOutput,  
00035                 $mLinks,         
00036                 $mImages,        
00037                 $mTemplates,     
00038                 $mExternals,     
00039                 $mCategories,    
00040                 $mInterlangs,    
00041                 $mProperties,    
00042                 $mDb,            
00043                 $mOptions,       
00044                 $mRecursive;     
00045 
00054         function __construct( $title, $parserOutput, $recursive = true ) {
00055                 parent::__construct( false ); // no implicit transaction
00056 
00057                 if ( !( $title instanceof Title ) ) {
00058                         throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
00059                                 "Please see Article::editUpdates() for an invocation example.\n" );
00060                 }
00061 
00062                 if ( !( $parserOutput instanceof ParserOutput ) ) {
00063                         throw new MWException( "The calling convention to LinksUpdate::__construct() has changed. " .
00064                                 "Please see WikiPage::doEditUpdates() for an invocation example.\n" );
00065                 }
00066 
00067                 $this->mTitle = $title;
00068                 $this->mId = $title->getArticleID();
00069 
00070                 if ( !$this->mId ) {
00071                         throw new MWException( "The Title object did not provide an article ID. Perhaps the page doesn't exist?" );
00072                 }
00073 
00074                 $this->mParserOutput = $parserOutput;
00075 
00076                 $this->mLinks = $parserOutput->getLinks();
00077                 $this->mImages = $parserOutput->getImages();
00078                 $this->mTemplates = $parserOutput->getTemplates();
00079                 $this->mExternals = $parserOutput->getExternalLinks();
00080                 $this->mCategories = $parserOutput->getCategories();
00081                 $this->mProperties = $parserOutput->getProperties();
00082                 $this->mInterwikis = $parserOutput->getInterwikiLinks();
00083 
00084                 # Convert the format of the interlanguage links
00085                 # I didn't want to change it in the ParserOutput, because that array is passed all
00086                 # the way back to the skin, so either a skin API break would be required, or an
00087                 # inefficient back-conversion.
00088                 $ill = $parserOutput->getLanguageLinks();
00089                 $this->mInterlangs = array();
00090                 foreach ( $ill as $link ) {
00091                         list( $key, $title ) = explode( ':', $link, 2 );
00092                         $this->mInterlangs[$key] = $title;
00093                 }
00094 
00095                 foreach ( $this->mCategories as &$sortkey ) {
00096                         # If the sortkey is longer then 255 bytes,
00097                         # it truncated by DB, and then doesn't get
00098                         # matched when comparing existing vs current
00099                         # categories, causing bug 25254.
00100                         # Also. substr behaves weird when given "".
00101                         if ( $sortkey !== '' ) {
00102                                 $sortkey = substr( $sortkey, 0, 255 );
00103                         }
00104                 }
00105 
00106                 $this->mRecursive = $recursive;
00107 
00108                 wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
00109         }
00110 
00114         public function doUpdate() {
00115                 global $wgUseDumbLinkUpdate;
00116 
00117                 wfRunHooks( 'LinksUpdate', array( &$this ) );
00118                 if ( $wgUseDumbLinkUpdate ) {
00119                         $this->doDumbUpdate();
00120                 } else {
00121                         $this->doIncrementalUpdate();
00122                 }
00123                 wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
00124         }
00125 
00126         protected function doIncrementalUpdate() {
00127                 wfProfileIn( __METHOD__ );
00128 
00129                 # Page links
00130                 $existing = $this->getExistingLinks();
00131                 $this->incrTableUpdate( 'pagelinks', 'pl', $this->getLinkDeletions( $existing ),
00132                         $this->getLinkInsertions( $existing ) );
00133 
00134                 # Image links
00135                 $existing = $this->getExistingImages();
00136 
00137                 $imageDeletes = $this->getImageDeletions( $existing );
00138                 $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes,
00139                         $this->getImageInsertions( $existing ) );
00140 
00141                 # Invalidate all image description pages which had links added or removed
00142                 $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing );
00143                 $this->invalidateImageDescriptions( $imageUpdates );
00144 
00145                 # External links
00146                 $existing = $this->getExistingExternals();
00147                 $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ),
00148                         $this->getExternalInsertions( $existing ) );
00149 
00150                 # Language links
00151                 $existing = $this->getExistingInterlangs();
00152                 $this->incrTableUpdate( 'langlinks', 'll', $this->getInterlangDeletions( $existing ),
00153                         $this->getInterlangInsertions( $existing ) );
00154 
00155                 # Inline interwiki links
00156                 $existing = $this->getExistingInterwikis();
00157                 $this->incrTableUpdate( 'iwlinks', 'iwl', $this->getInterwikiDeletions( $existing ),
00158                         $this->getInterwikiInsertions( $existing ) );
00159 
00160                 # Template links
00161                 $existing = $this->getExistingTemplates();
00162                 $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ),
00163                         $this->getTemplateInsertions( $existing ) );
00164 
00165                 # Category links
00166                 $existing = $this->getExistingCategories();
00167 
00168                 $categoryDeletes = $this->getCategoryDeletions( $existing );
00169 
00170                 $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes,
00171                         $this->getCategoryInsertions( $existing ) );
00172 
00173                 # Invalidate all categories which were added, deleted or changed (set symmetric difference)
00174                 $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
00175                 $categoryUpdates = $categoryInserts + $categoryDeletes;
00176                 $this->invalidateCategories( $categoryUpdates );
00177                 $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
00178 
00179                 # Page properties
00180                 $existing = $this->getExistingProperties();
00181 
00182                 $propertiesDeletes = $this->getPropertyDeletions( $existing );
00183 
00184                 $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
00185                         $this->getPropertyInsertions( $existing ) );
00186 
00187                 # Invalidate the necessary pages
00188                 $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
00189                 $this->invalidateProperties( $changed );
00190 
00191                 # Refresh links of all pages including this page
00192                 # This will be in a separate transaction
00193                 if ( $this->mRecursive ) {
00194                         $this->queueRecursiveJobs();
00195                 }
00196 
00197                 wfProfileOut( __METHOD__ );
00198         }
00199 
00205         protected function doDumbUpdate() {
00206                 wfProfileIn( __METHOD__ );
00207 
00208                 # Refresh category pages and image description pages
00209                 $existing = $this->getExistingCategories();
00210                 $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
00211                 $categoryDeletes = array_diff_assoc( $existing, $this->mCategories );
00212                 $categoryUpdates = $categoryInserts + $categoryDeletes;
00213                 $existing = $this->getExistingImages();
00214                 $imageUpdates = array_diff_key( $existing, $this->mImages ) + array_diff_key( $this->mImages, $existing );
00215 
00216                 $this->dumbTableUpdate( 'pagelinks', $this->getLinkInsertions(), 'pl_from' );
00217                 $this->dumbTableUpdate( 'imagelinks', $this->getImageInsertions(), 'il_from' );
00218                 $this->dumbTableUpdate( 'categorylinks', $this->getCategoryInsertions(), 'cl_from' );
00219                 $this->dumbTableUpdate( 'templatelinks', $this->getTemplateInsertions(), 'tl_from' );
00220                 $this->dumbTableUpdate( 'externallinks', $this->getExternalInsertions(), 'el_from' );
00221                 $this->dumbTableUpdate( 'langlinks', $this->getInterlangInsertions(), 'll_from' );
00222                 $this->dumbTableUpdate( 'iwlinks', $this->getInterwikiInsertions(), 'iwl_from' );
00223                 $this->dumbTableUpdate( 'page_props', $this->getPropertyInsertions(), 'pp_page' );
00224 
00225                 # Update the cache of all the category pages and image description
00226                 # pages which were changed, and fix the category table count
00227                 $this->invalidateCategories( $categoryUpdates );
00228                 $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
00229                 $this->invalidateImageDescriptions( $imageUpdates );
00230 
00231                 # Refresh links of all pages including this page
00232                 # This will be in a separate transaction
00233                 if ( $this->mRecursive ) {
00234                         $this->queueRecursiveJobs();
00235                 }
00236 
00237                 wfProfileOut( __METHOD__ );
00238         }
00239 
00240         function queueRecursiveJobs() {
00241                 wfProfileIn( __METHOD__ );
00242 
00243                 if ( $this->mTitle->getBacklinkCache()->hasLinks( 'templatelinks' ) ) {
00244                         $job = new RefreshLinksJob2(
00245                                 $this->mTitle,
00246                                 array(
00247                                         'table' => 'templatelinks',
00248                                 ) + Job::newRootJobParams( // "overall" refresh links job info
00249                                         "refreshlinks:templatelinks:{$this->mTitle->getPrefixedText()}"
00250                                 )
00251                         );
00252                         JobQueueGroup::singleton()->push( $job );
00253                         JobQueueGroup::singleton()->deduplicateRootJob( $job );
00254                 }
00255 
00256                 wfProfileOut( __METHOD__ );
00257         }
00258 
00262         function invalidateCategories( $cats ) {
00263                 $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
00264         }
00265 
00271         function updateCategoryCounts( $added, $deleted ) {
00272                 $a = WikiPage::factory( $this->mTitle );
00273                 $a->updateCategoryCounts(
00274                         array_keys( $added ), array_keys( $deleted )
00275                 );
00276         }
00277 
00281         function invalidateImageDescriptions( $images ) {
00282                 $this->invalidatePages( NS_FILE, array_keys( $images ) );
00283         }
00284 
00290         private function dumbTableUpdate( $table, $insertions, $fromField ) {
00291                 $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ );
00292                 if ( count( $insertions ) ) {
00293                         # The link array was constructed without FOR UPDATE, so there may
00294                         # be collisions. This may cause minor link table inconsistencies,
00295                         # which is better than crippling the site with lock contention.
00296                         $this->mDb->insert( $table, $insertions, __METHOD__, array( 'IGNORE' ) );
00297                 }
00298         }
00299 
00307         function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
00308                 if ( $table == 'page_props' ) {
00309                         $fromField = 'pp_page';
00310                 } else {
00311                         $fromField = "{$prefix}_from";
00312                 }
00313                 $where = array( $fromField => $this->mId );
00314                 if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) {
00315                         if ( $table == 'iwlinks' ) {
00316                                 $baseKey = 'iwl_prefix';
00317                         } else {
00318                                 $baseKey = "{$prefix}_namespace";
00319                         }
00320                         $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" );
00321                         if ( $clause ) {
00322                                 $where[] = $clause;
00323                         } else {
00324                                 $where = false;
00325                         }
00326                 } else {
00327                         if ( $table == 'langlinks' ) {
00328                                 $toField = 'll_lang';
00329                         } elseif ( $table == 'page_props' ) {
00330                                 $toField = 'pp_propname';
00331                         } else {
00332                                 $toField = $prefix . '_to';
00333                         }
00334                         if ( count( $deletions ) ) {
00335                                 $where[] = "$toField IN (" . $this->mDb->makeList( array_keys( $deletions ) ) . ')';
00336                         } else {
00337                                 $where = false;
00338                         }
00339                 }
00340                 if ( $where ) {
00341                         $this->mDb->delete( $table, $where, __METHOD__ );
00342                 }
00343                 if ( count( $insertions ) ) {
00344                         $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
00345                         wfRunHooks( 'LinksUpdateAfterInsert', array( $this, $table, $insertions ) );
00346                 }
00347         }
00348 
00355         private function getLinkInsertions( $existing = array() ) {
00356                 $arr = array();
00357                 foreach( $this->mLinks as $ns => $dbkeys ) {
00358                         $diffs = isset( $existing[$ns] )
00359                                 ? array_diff_key( $dbkeys, $existing[$ns] )
00360                                 : $dbkeys;
00361                         foreach ( $diffs as $dbk => $id ) {
00362                                 $arr[] = array(
00363                                         'pl_from' => $this->mId,
00364                                         'pl_namespace' => $ns,
00365                                         'pl_title' => $dbk
00366                                 );
00367                         }
00368                 }
00369                 return $arr;
00370         }
00371 
00377         private function getTemplateInsertions( $existing = array() ) {
00378                 $arr = array();
00379                 foreach( $this->mTemplates as $ns => $dbkeys ) {
00380                         $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
00381                         foreach ( $diffs as $dbk => $id ) {
00382                                 $arr[] = array(
00383                                         'tl_from' => $this->mId,
00384                                         'tl_namespace' => $ns,
00385                                         'tl_title' => $dbk
00386                                 );
00387                         }
00388                 }
00389                 return $arr;
00390         }
00391 
00398         private function getImageInsertions( $existing = array() ) {
00399                 $arr = array();
00400                 $diffs = array_diff_key( $this->mImages, $existing );
00401                 foreach( $diffs as $iname => $dummy ) {
00402                         $arr[] = array(
00403                                 'il_from' => $this->mId,
00404                                 'il_to' => $iname
00405                         );
00406                 }
00407                 return $arr;
00408         }
00409 
00415         private function getExternalInsertions( $existing = array() ) {
00416                 $arr = array();
00417                 $diffs = array_diff_key( $this->mExternals, $existing );
00418                 foreach( $diffs as $url => $dummy ) {
00419                         foreach( wfMakeUrlIndexes( $url ) as $index ) {
00420                                 $arr[] = array(
00421                                         'el_from' => $this->mId,
00422                                         'el_to' => $url,
00423                                         'el_index' => $index,
00424                                 );
00425                         }
00426                 }
00427                 return $arr;
00428         }
00429 
00438         private function getCategoryInsertions( $existing = array() ) {
00439                 global $wgContLang, $wgCategoryCollation;
00440                 $diffs = array_diff_assoc( $this->mCategories, $existing );
00441                 $arr = array();
00442                 foreach ( $diffs as $name => $prefix ) {
00443                         $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
00444                         $wgContLang->findVariantLink( $name, $nt, true );
00445 
00446                         if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
00447                                 $type = 'subcat';
00448                         } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
00449                                 $type = 'file';
00450                         } else {
00451                                 $type = 'page';
00452                         }
00453 
00454                         # Treat custom sortkeys as a prefix, so that if multiple
00455                         # things are forced to sort as '*' or something, they'll
00456                         # sort properly in the category rather than in page_id
00457                         # order or such.
00458                         $sortkey = Collation::singleton()->getSortKey(
00459                                 $this->mTitle->getCategorySortkey( $prefix ) );
00460 
00461                         $arr[] = array(
00462                                 'cl_from' => $this->mId,
00463                                 'cl_to' => $name,
00464                                 'cl_sortkey' => $sortkey,
00465                                 'cl_timestamp' => $this->mDb->timestamp(),
00466                                 'cl_sortkey_prefix' => $prefix,
00467                                 'cl_collation' => $wgCategoryCollation,
00468                                 'cl_type' => $type,
00469                         );
00470                 }
00471                 return $arr;
00472         }
00473 
00481         private function getInterlangInsertions( $existing = array() ) {
00482                 $diffs = array_diff_assoc( $this->mInterlangs, $existing );
00483                 $arr = array();
00484                 foreach( $diffs as $lang => $title ) {
00485                         $arr[] = array(
00486                                 'll_from' => $this->mId,
00487                                 'll_lang' => $lang,
00488                                 'll_title' => $title
00489                         );
00490                 }
00491                 return $arr;
00492         }
00493 
00499         function getPropertyInsertions( $existing = array() ) {
00500                 $diffs = array_diff_assoc( $this->mProperties, $existing );
00501                 $arr = array();
00502                 foreach ( $diffs as $name => $value ) {
00503                         $arr[] = array(
00504                                 'pp_page' => $this->mId,
00505                                 'pp_propname' => $name,
00506                                 'pp_value' => $value,
00507                         );
00508                 }
00509                 return $arr;
00510         }
00511 
00518         private function getInterwikiInsertions( $existing = array() ) {
00519                 $arr = array();
00520                 foreach( $this->mInterwikis as $prefix => $dbkeys ) {
00521                         $diffs = isset( $existing[$prefix] ) ? array_diff_key( $dbkeys, $existing[$prefix] ) : $dbkeys;
00522                         foreach ( $diffs as $dbk => $id ) {
00523                                 $arr[] = array(
00524                                         'iwl_from' => $this->mId,
00525                                         'iwl_prefix' => $prefix,
00526                                         'iwl_title' => $dbk
00527                                 );
00528                         }
00529                 }
00530                 return $arr;
00531         }
00532 
00539         private function getLinkDeletions( $existing ) {
00540                 $del = array();
00541                 foreach ( $existing as $ns => $dbkeys ) {
00542                         if ( isset( $this->mLinks[$ns] ) ) {
00543                                 $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
00544                         } else {
00545                                 $del[$ns] = $existing[$ns];
00546                         }
00547                 }
00548                 return $del;
00549         }
00550 
00557         private function getTemplateDeletions( $existing ) {
00558                 $del = array();
00559                 foreach ( $existing as $ns => $dbkeys ) {
00560                         if ( isset( $this->mTemplates[$ns] ) ) {
00561                                 $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
00562                         } else {
00563                                 $del[$ns] = $existing[$ns];
00564                         }
00565                 }
00566                 return $del;
00567         }
00568 
00575         private function getImageDeletions( $existing ) {
00576                 return array_diff_key( $existing, $this->mImages );
00577         }
00578 
00585         private function getExternalDeletions( $existing ) {
00586                 return array_diff_key( $existing, $this->mExternals );
00587         }
00588 
00595         private function getCategoryDeletions( $existing ) {
00596                 return array_diff_assoc( $existing, $this->mCategories );
00597         }
00598 
00605         private function getInterlangDeletions( $existing ) {
00606                 return array_diff_assoc( $existing, $this->mInterlangs );
00607         }
00608 
00614         function getPropertyDeletions( $existing ) {
00615                 return array_diff_assoc( $existing, $this->mProperties );
00616         }
00617 
00624         private function getInterwikiDeletions( $existing ) {
00625                 $del = array();
00626                 foreach ( $existing as $prefix => $dbkeys ) {
00627                         if ( isset( $this->mInterwikis[$prefix] ) ) {
00628                                 $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
00629                         } else {
00630                                 $del[$prefix] = $existing[$prefix];
00631                         }
00632                 }
00633                 return $del;
00634         }
00635 
00641         private function getExistingLinks() {
00642                 $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
00643                         array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
00644                 $arr = array();
00645                 foreach ( $res as $row ) {
00646                         if ( !isset( $arr[$row->pl_namespace] ) ) {
00647                                 $arr[$row->pl_namespace] = array();
00648                         }
00649                         $arr[$row->pl_namespace][$row->pl_title] = 1;
00650                 }
00651                 return $arr;
00652         }
00653 
00659         private function getExistingTemplates() {
00660                 $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
00661                         array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
00662                 $arr = array();
00663                 foreach ( $res as $row ) {
00664                         if ( !isset( $arr[$row->tl_namespace] ) ) {
00665                                 $arr[$row->tl_namespace] = array();
00666                         }
00667                         $arr[$row->tl_namespace][$row->tl_title] = 1;
00668                 }
00669                 return $arr;
00670         }
00671 
00677         private function getExistingImages() {
00678                 $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
00679                         array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
00680                 $arr = array();
00681                 foreach ( $res as $row ) {
00682                         $arr[$row->il_to] = 1;
00683                 }
00684                 return $arr;
00685         }
00686 
00692         private function getExistingExternals() {
00693                 $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
00694                         array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
00695                 $arr = array();
00696                 foreach ( $res as $row ) {
00697                         $arr[$row->el_to] = 1;
00698                 }
00699                 return $arr;
00700         }
00701 
00707         private function getExistingCategories() {
00708                 $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
00709                         array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
00710                 $arr = array();
00711                 foreach ( $res as $row ) {
00712                         $arr[$row->cl_to] = $row->cl_sortkey_prefix;
00713                 }
00714                 return $arr;
00715         }
00716 
00723         private function getExistingInterlangs() {
00724                 $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
00725                         array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
00726                 $arr = array();
00727                 foreach ( $res as $row ) {
00728                         $arr[$row->ll_lang] = $row->ll_title;
00729                 }
00730                 return $arr;
00731         }
00732 
00737         protected function getExistingInterwikis() {
00738                 $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
00739                         array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
00740                 $arr = array();
00741                 foreach ( $res as $row ) {
00742                         if ( !isset( $arr[$row->iwl_prefix] ) ) {
00743                                 $arr[$row->iwl_prefix] = array();
00744                         }
00745                         $arr[$row->iwl_prefix][$row->iwl_title] = 1;
00746                 }
00747                 return $arr;
00748         }
00749 
00755         private function getExistingProperties() {
00756                 $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
00757                         array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
00758                 $arr = array();
00759                 foreach ( $res as $row ) {
00760                         $arr[$row->pp_propname] = $row->pp_value;
00761                 }
00762                 return $arr;
00763         }
00764 
00769         public function getTitle() {
00770                 return $this->mTitle;
00771         }
00772 
00778         public function getParserOutput() {
00779                 return $this->mParserOutput;
00780         }
00781 
00786         public function getImages() {
00787                 return $this->mImages;
00788         }
00789 
00794         private function invalidateProperties( $changed ) {
00795                 global $wgPagePropLinkInvalidations;
00796 
00797                 foreach ( $changed as $name => $value ) {
00798                         if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
00799                                 $inv = $wgPagePropLinkInvalidations[$name];
00800                                 if ( !is_array( $inv ) ) {
00801                                         $inv = array( $inv );
00802                                 }
00803                                 foreach ( $inv as $table ) {
00804                                         $update = new HTMLCacheUpdate( $this->mTitle, $table );
00805                                         $update->doUpdate();
00806                                 }
00807                         }
00808                 }
00809         }
00810 }
00811 
00815 class LinksDeletionUpdate extends SqlDataUpdate {
00816 
00817         protected $mPage;     
00818 
00825         function __construct( WikiPage $page ) {
00826                 parent::__construct( false ); // no implicit transaction
00827 
00828                 $this->mPage = $page;
00829 
00830                 if ( !$page->exists() ) {
00831                         throw new MWException( "Page ID not known, perhaps the page doesn't exist?" );
00832                 }
00833         }
00834 
00838         public function doUpdate() {
00839                 $title = $this->mPage->getTitle();
00840                 $id = $this->mPage->getId();
00841 
00842                 # Delete restrictions for it
00843                 $this->mDb->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
00844 
00845                 # Fix category table counts
00846                 $cats = array();
00847                 $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
00848 
00849                 foreach ( $res as $row ) {
00850                         $cats [] = $row->cl_to;
00851                 }
00852 
00853                 $this->mPage->updateCategoryCounts( array(), $cats );
00854 
00855                 # If using cascading deletes, we can skip some explicit deletes
00856                 if ( !$this->mDb->cascadingDeletes() ) {
00857                         $this->mDb->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
00858 
00859                         # Delete outgoing links
00860                         $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ );
00861                         $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ );
00862                         $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ );
00863                         $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ );
00864                         $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ );
00865                         $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ );
00866                         $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ );
00867                         $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ );
00868                         $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ );
00869                 }
00870 
00871                 # If using cleanup triggers, we can skip some manual deletes
00872                 if ( !$this->mDb->cleanupTriggers() ) {
00873                         # Clean up recentchanges entries...
00874                         $this->mDb->delete( 'recentchanges',
00875                                 array( 'rc_type != ' . RC_LOG,
00876                                         'rc_namespace' => $title->getNamespace(),
00877                                         'rc_title' => $title->getDBkey() ),
00878                                 __METHOD__ );
00879                         $this->mDb->delete( 'recentchanges',
00880                                 array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
00881                                 __METHOD__ );
00882                 }
00883         }
00884 
00890         function updateCategoryCounts( $added, $deleted ) {
00891                 $a = WikiPage::factory( $this->mTitle );
00892                 $a->updateCategoryCounts(
00893                         array_keys( $added ), array_keys( $deleted )
00894                 );
00895         }
00896 }