MediaWiki  master
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                 global $wgUpdateRowsPerJob;
00242                 wfProfileIn( __METHOD__ );
00243 
00244                 $cache = $this->mTitle->getBacklinkCache();
00245                 $batches = $cache->partition( 'templatelinks', $wgUpdateRowsPerJob );
00246                 if ( !$batches ) {
00247                         wfProfileOut( __METHOD__ );
00248                         return;
00249                 }
00250                 $jobs = array();
00251                 foreach ( $batches as $batch ) {
00252                         list( $start, $end ) = $batch;
00253                         $params = array(
00254                                 'table' => 'templatelinks',
00255                                 'start' => $start,
00256                                 'end' => $end,
00257                         );
00258                         $jobs[] = new RefreshLinksJob2( $this->mTitle, $params );
00259                 }
00260                 Job::batchInsert( $jobs );
00261 
00262                 wfProfileOut( __METHOD__ );
00263         }
00264 
00268         function invalidateCategories( $cats ) {
00269                 $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
00270         }
00271 
00277         function updateCategoryCounts( $added, $deleted ) {
00278                 $a = WikiPage::factory( $this->mTitle );
00279                 $a->updateCategoryCounts(
00280                         array_keys( $added ), array_keys( $deleted )
00281                 );
00282         }
00283 
00287         function invalidateImageDescriptions( $images ) {
00288                 $this->invalidatePages( NS_FILE, array_keys( $images ) );
00289         }
00290 
00296         private function dumbTableUpdate( $table, $insertions, $fromField ) {
00297                 $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ );
00298                 if ( count( $insertions ) ) {
00299                         # The link array was constructed without FOR UPDATE, so there may
00300                         # be collisions.  This may cause minor link table inconsistencies,
00301                         # which is better than crippling the site with lock contention.
00302                         $this->mDb->insert( $table, $insertions, __METHOD__, array( 'IGNORE' ) );
00303                 }
00304         }
00305 
00313         function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
00314                 if ( $table == 'page_props' ) {
00315                         $fromField = 'pp_page';
00316                 } else {
00317                         $fromField = "{$prefix}_from";
00318                 }
00319                 $where = array( $fromField => $this->mId );
00320                 if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) {
00321                         if ( $table == 'iwlinks' ) {
00322                                 $baseKey = 'iwl_prefix';
00323                         } else {
00324                                 $baseKey = "{$prefix}_namespace";
00325                         }
00326                         $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" );
00327                         if ( $clause ) {
00328                                 $where[] = $clause;
00329                         } else {
00330                                 $where = false;
00331                         }
00332                 } else {
00333                         if ( $table == 'langlinks' ) {
00334                                 $toField = 'll_lang';
00335                         } elseif ( $table == 'page_props' ) {
00336                                 $toField = 'pp_propname';
00337                         } else {
00338                                 $toField = $prefix . '_to';
00339                         }
00340                         if ( count( $deletions ) ) {
00341                                 $where[] = "$toField IN (" . $this->mDb->makeList( array_keys( $deletions ) ) . ')';
00342                         } else {
00343                                 $where = false;
00344                         }
00345                 }
00346                 if ( $where ) {
00347                         $this->mDb->delete( $table, $where, __METHOD__ );
00348                 }
00349                 if ( count( $insertions ) ) {
00350                         $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
00351                 }
00352         }
00353 
00360         private function getLinkInsertions( $existing = array() ) {
00361                 $arr = array();
00362                 foreach( $this->mLinks as $ns => $dbkeys ) {
00363                         $diffs = isset( $existing[$ns] )
00364                                 ? array_diff_key( $dbkeys, $existing[$ns] )
00365                                 : $dbkeys;
00366                         foreach ( $diffs as $dbk => $id ) {
00367                                 $arr[] = array(
00368                                         'pl_from'      => $this->mId,
00369                                         'pl_namespace' => $ns,
00370                                         'pl_title'     => $dbk
00371                                 );
00372                         }
00373                 }
00374                 return $arr;
00375         }
00376 
00382         private function getTemplateInsertions( $existing = array() ) {
00383                 $arr = array();
00384                 foreach( $this->mTemplates as $ns => $dbkeys ) {
00385                         $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
00386                         foreach ( $diffs as $dbk => $id ) {
00387                                 $arr[] = array(
00388                                         'tl_from'      => $this->mId,
00389                                         'tl_namespace' => $ns,
00390                                         'tl_title'     => $dbk
00391                                 );
00392                         }
00393                 }
00394                 return $arr;
00395         }
00396 
00403         private function getImageInsertions( $existing = array() ) {
00404                 $arr = array();
00405                 $diffs = array_diff_key( $this->mImages, $existing );
00406                 foreach( $diffs as $iname => $dummy ) {
00407                         $arr[] = array(
00408                                 'il_from' => $this->mId,
00409                                 'il_to'   => $iname
00410                         );
00411                 }
00412                 return $arr;
00413         }
00414 
00420         private function getExternalInsertions( $existing = array() ) {
00421                 $arr = array();
00422                 $diffs = array_diff_key( $this->mExternals, $existing );
00423                 foreach( $diffs as $url => $dummy ) {
00424                         foreach( wfMakeUrlIndexes( $url ) as $index ) {
00425                                 $arr[] = array(
00426                                         'el_from'   => $this->mId,
00427                                         'el_to'     => $url,
00428                                         'el_index'  => $index,
00429                                 );
00430                         }
00431                 }
00432                 return $arr;
00433         }
00434 
00443         private function getCategoryInsertions( $existing = array() ) {
00444                 global $wgContLang, $wgCategoryCollation;
00445                 $diffs = array_diff_assoc( $this->mCategories, $existing );
00446                 $arr = array();
00447                 foreach ( $diffs as $name => $prefix ) {
00448                         $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
00449                         $wgContLang->findVariantLink( $name, $nt, true );
00450 
00451                         if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
00452                                 $type = 'subcat';
00453                         } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
00454                                 $type = 'file';
00455                         } else {
00456                                 $type = 'page';
00457                         }
00458 
00459                         # Treat custom sortkeys as a prefix, so that if multiple
00460                         # things are forced to sort as '*' or something, they'll
00461                         # sort properly in the category rather than in page_id
00462                         # order or such.
00463                         $sortkey = Collation::singleton()->getSortKey(
00464                                 $this->mTitle->getCategorySortkey( $prefix ) );
00465 
00466                         $arr[] = array(
00467                                 'cl_from'    => $this->mId,
00468                                 'cl_to'      => $name,
00469                                 'cl_sortkey' => $sortkey,
00470                                 'cl_timestamp' => $this->mDb->timestamp(),
00471                                 'cl_sortkey_prefix' => $prefix,
00472                                 'cl_collation' => $wgCategoryCollation,
00473                                 'cl_type' => $type,
00474                         );
00475                 }
00476                 return $arr;
00477         }
00478 
00486         private function getInterlangInsertions( $existing = array() ) {
00487                 $diffs = array_diff_assoc( $this->mInterlangs, $existing );
00488                 $arr = array();
00489                 foreach( $diffs as $lang => $title ) {
00490                         $arr[] = array(
00491                                 'll_from'  => $this->mId,
00492                                 'll_lang'  => $lang,
00493                                 'll_title' => $title
00494                         );
00495                 }
00496                 return $arr;
00497         }
00498 
00504         function getPropertyInsertions( $existing = array() ) {
00505                 $diffs = array_diff_assoc( $this->mProperties, $existing );
00506                 $arr = array();
00507                 foreach ( $diffs as $name => $value ) {
00508                         $arr[] = array(
00509                                 'pp_page'      => $this->mId,
00510                                 'pp_propname'  => $name,
00511                                 'pp_value'     => $value,
00512                         );
00513                 }
00514                 return $arr;
00515         }
00516 
00523         private function getInterwikiInsertions( $existing = array() ) {
00524                 $arr = array();
00525                 foreach( $this->mInterwikis as $prefix => $dbkeys ) {
00526                         $diffs = isset( $existing[$prefix] ) ? array_diff_key( $dbkeys, $existing[$prefix] ) : $dbkeys;
00527                         foreach ( $diffs as $dbk => $id ) {
00528                                 $arr[] = array(
00529                                         'iwl_from'   => $this->mId,
00530                                         'iwl_prefix' => $prefix,
00531                                         'iwl_title'  => $dbk
00532                                 );
00533                         }
00534                 }
00535                 return $arr;
00536         }
00537 
00544         private function getLinkDeletions( $existing ) {
00545                 $del = array();
00546                 foreach ( $existing as $ns => $dbkeys ) {
00547                         if ( isset( $this->mLinks[$ns] ) ) {
00548                                 $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
00549                         } else {
00550                                 $del[$ns] = $existing[$ns];
00551                         }
00552                 }
00553                 return $del;
00554         }
00555 
00562         private function getTemplateDeletions( $existing ) {
00563                 $del = array();
00564                 foreach ( $existing as $ns => $dbkeys ) {
00565                         if ( isset( $this->mTemplates[$ns] ) ) {
00566                                 $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
00567                         } else {
00568                                 $del[$ns] = $existing[$ns];
00569                         }
00570                 }
00571                 return $del;
00572         }
00573 
00580         private function getImageDeletions( $existing ) {
00581                 return array_diff_key( $existing, $this->mImages );
00582         }
00583 
00590         private function getExternalDeletions( $existing ) {
00591                 return array_diff_key( $existing, $this->mExternals );
00592         }
00593 
00600         private function getCategoryDeletions( $existing ) {
00601                 return array_diff_assoc( $existing, $this->mCategories );
00602         }
00603 
00610         private function getInterlangDeletions( $existing ) {
00611                 return array_diff_assoc( $existing, $this->mInterlangs );
00612         }
00613 
00619         function getPropertyDeletions( $existing ) {
00620                 return array_diff_assoc( $existing, $this->mProperties );
00621         }
00622 
00629         private function getInterwikiDeletions( $existing ) {
00630                 $del = array();
00631                 foreach ( $existing as $prefix => $dbkeys ) {
00632                         if ( isset( $this->mInterwikis[$prefix] ) ) {
00633                                 $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
00634                         } else {
00635                                 $del[$prefix] = $existing[$prefix];
00636                         }
00637                 }
00638                 return $del;
00639         }
00640 
00646         private function getExistingLinks() {
00647                 $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
00648                         array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
00649                 $arr = array();
00650                 foreach ( $res as $row ) {
00651                         if ( !isset( $arr[$row->pl_namespace] ) ) {
00652                                 $arr[$row->pl_namespace] = array();
00653                         }
00654                         $arr[$row->pl_namespace][$row->pl_title] = 1;
00655                 }
00656                 return $arr;
00657         }
00658 
00664         private function getExistingTemplates() {
00665                 $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
00666                         array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
00667                 $arr = array();
00668                 foreach ( $res as $row ) {
00669                         if ( !isset( $arr[$row->tl_namespace] ) ) {
00670                                 $arr[$row->tl_namespace] = array();
00671                         }
00672                         $arr[$row->tl_namespace][$row->tl_title] = 1;
00673                 }
00674                 return $arr;
00675         }
00676 
00682         private function getExistingImages() {
00683                 $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
00684                         array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
00685                 $arr = array();
00686                 foreach ( $res as $row ) {
00687                         $arr[$row->il_to] = 1;
00688                 }
00689                 return $arr;
00690         }
00691 
00697         private function getExistingExternals() {
00698                 $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
00699                         array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
00700                 $arr = array();
00701                 foreach ( $res as $row ) {
00702                         $arr[$row->el_to] = 1;
00703                 }
00704                 return $arr;
00705         }
00706 
00712         private function getExistingCategories() {
00713                 $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
00714                         array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
00715                 $arr = array();
00716                 foreach ( $res as $row ) {
00717                         $arr[$row->cl_to] = $row->cl_sortkey_prefix;
00718                 }
00719                 return $arr;
00720         }
00721 
00728         private function getExistingInterlangs() {
00729                 $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
00730                         array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
00731                 $arr = array();
00732                 foreach ( $res as $row ) {
00733                         $arr[$row->ll_lang] = $row->ll_title;
00734                 }
00735                 return $arr;
00736         }
00737 
00742         protected function getExistingInterwikis() {
00743                 $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
00744                         array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
00745                 $arr = array();
00746                 foreach ( $res as $row ) {
00747                         if ( !isset( $arr[$row->iwl_prefix] ) ) {
00748                                 $arr[$row->iwl_prefix] = array();
00749                         }
00750                         $arr[$row->iwl_prefix][$row->iwl_title] = 1;
00751                 }
00752                 return $arr;
00753         }
00754 
00760         private function getExistingProperties() {
00761                 $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
00762                         array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
00763                 $arr = array();
00764                 foreach ( $res as $row ) {
00765                         $arr[$row->pp_propname] = $row->pp_value;
00766                 }
00767                 return $arr;
00768         }
00769 
00774         public function getTitle() {
00775                 return $this->mTitle;
00776         }
00777 
00783         public function getParserOutput() {
00784                 return $this->mParserOutput;
00785         }
00786 
00791         public function getImages() {
00792                 return $this->mImages;
00793         }
00794 
00799         private function invalidateProperties( $changed ) {
00800                 global $wgPagePropLinkInvalidations;
00801 
00802                 foreach ( $changed as $name => $value ) {
00803                         if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
00804                                 $inv = $wgPagePropLinkInvalidations[$name];
00805                                 if ( !is_array( $inv ) ) {
00806                                         $inv = array( $inv );
00807                                 }
00808                                 foreach ( $inv as $table ) {
00809                                         $update = new HTMLCacheUpdate( $this->mTitle, $table );
00810                                         $update->doUpdate();
00811                                 }
00812                         }
00813                 }
00814         }
00815 }
00816 
00820 class LinksDeletionUpdate extends SqlDataUpdate {
00821 
00822         protected $mPage;     
00823 
00829         function __construct( WikiPage $page ) {
00830                 parent::__construct( false ); // no implicit transaction
00831 
00832                 $this->mPage = $page;
00833 
00834                 if ( !$page->exists() ) {
00835                         throw new MWException( "Page ID not known, perhaps the page doesn't exist?" );
00836                 }
00837         }
00838 
00842         public function doUpdate() {
00843                 $title = $this->mPage->getTitle();
00844                 $id = $this->mPage->getId();
00845 
00846                 # Delete restrictions for it
00847                 $this->mDb->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
00848 
00849                 # Fix category table counts
00850                 $cats = array();
00851                 $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
00852 
00853                 foreach ( $res as $row ) {
00854                         $cats [] = $row->cl_to;
00855                 }
00856 
00857                 $this->mPage->updateCategoryCounts( array(), $cats );
00858 
00859                 # If using cascading deletes, we can skip some explicit deletes
00860                 if ( !$this->mDb->cascadingDeletes() ) {
00861                         $this->mDb->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
00862 
00863                         # Delete outgoing links
00864                         $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ );
00865                         $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ );
00866                         $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ );
00867                         $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ );
00868                         $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ );
00869                         $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ );
00870                         $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ );
00871                         $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ );
00872                         $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ );
00873                 }
00874 
00875                 # If using cleanup triggers, we can skip some manual deletes
00876                 if ( !$this->mDb->cleanupTriggers() ) {
00877                         # Clean up recentchanges entries...
00878                         $this->mDb->delete( 'recentchanges',
00879                                 array( 'rc_type != ' . RC_LOG,
00880                                         'rc_namespace' => $title->getNamespace(),
00881                                         'rc_title' => $title->getDBkey() ),
00882                                 __METHOD__ );
00883                         $this->mDb->delete( 'recentchanges',
00884                                 array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
00885                                 __METHOD__ );
00886                 }
00887         }
00888 
00894         function updateCategoryCounts( $added, $deleted ) {
00895                 $a = WikiPage::factory( $this->mTitle );
00896                 $a->updateCategoryCounts(
00897                         array_keys( $added ), array_keys( $deleted )
00898                 );
00899         }
00900 }