[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/deferred/ -> LinksUpdate.php (source)

   1  <?php
   2  /**
   3   * Updater for link tracking tables after a page edit.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   */
  22  
  23  /**
  24   * See docs/deferred.txt
  25   *
  26   * @todo document (e.g. one-sentence top-level class description).
  27   */
  28  class LinksUpdate extends SqlDataUpdate {
  29      // @todo make members protected, but make sure extensions don't break
  30  
  31      /** @var int Page ID of the article linked from */
  32      public $mId;
  33  
  34      /** @var Title Title object of the article linked from */
  35      public $mTitle;
  36  
  37      /** @var ParserOutput */
  38      public $mParserOutput;
  39  
  40      /** @var array Map of title strings to IDs for the links in the document */
  41      public $mLinks;
  42  
  43      /** @var array DB keys of the images used, in the array key only */
  44      public $mImages;
  45  
  46      /** @var array Map of title strings to IDs for the template references, including broken ones */
  47      public $mTemplates;
  48  
  49      /** @var array URLs of external links, array key only */
  50      public $mExternals;
  51  
  52      /** @var array Map of category names to sort keys */
  53      public $mCategories;
  54  
  55      /** @var array Map of language codes to titles */
  56      public $mInterlangs;
  57  
  58      /** @var array Map of arbitrary name to value */
  59      public $mProperties;
  60  
  61      /** @var DatabaseBase Database connection reference */
  62      public $mDb;
  63  
  64      /** @var array SELECT options to be used */
  65      public $mOptions;
  66  
  67      /** @var bool Whether to queue jobs for recursive updates */
  68      public $mRecursive;
  69  
  70      /**
  71       * @var null|array Added links if calculated.
  72       */
  73      private $linkInsertions = null;
  74  
  75      /**
  76       * @var null|array Deleted links if calculated.
  77       */
  78      private $linkDeletions = null;
  79  
  80      /**
  81       * Constructor
  82       *
  83       * @param Title $title Title of the page we're updating
  84       * @param ParserOutput $parserOutput Output from a full parse of this page
  85       * @param bool $recursive Queue jobs for recursive updates?
  86       * @throws MWException
  87       */
  88  	function __construct( $title, $parserOutput, $recursive = true ) {
  89          parent::__construct( false ); // no implicit transaction
  90  
  91          if ( !( $title instanceof Title ) ) {
  92              throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
  93                  "Please see Article::editUpdates() for an invocation example.\n" );
  94          }
  95  
  96          if ( !( $parserOutput instanceof ParserOutput ) ) {
  97              throw new MWException( "The calling convention to LinksUpdate::__construct() has changed. " .
  98                  "Please see WikiPage::doEditUpdates() for an invocation example.\n" );
  99          }
 100  
 101          $this->mTitle = $title;
 102          $this->mId = $title->getArticleID();
 103  
 104          if ( !$this->mId ) {
 105              throw new MWException( "The Title object did not provide an article " .
 106                  "ID. Perhaps the page doesn't exist?" );
 107          }
 108  
 109          $this->mParserOutput = $parserOutput;
 110  
 111          $this->mLinks = $parserOutput->getLinks();
 112          $this->mImages = $parserOutput->getImages();
 113          $this->mTemplates = $parserOutput->getTemplates();
 114          $this->mExternals = $parserOutput->getExternalLinks();
 115          $this->mCategories = $parserOutput->getCategories();
 116          $this->mProperties = $parserOutput->getProperties();
 117          $this->mInterwikis = $parserOutput->getInterwikiLinks();
 118  
 119          # Convert the format of the interlanguage links
 120          # I didn't want to change it in the ParserOutput, because that array is passed all
 121          # the way back to the skin, so either a skin API break would be required, or an
 122          # inefficient back-conversion.
 123          $ill = $parserOutput->getLanguageLinks();
 124          $this->mInterlangs = array();
 125          foreach ( $ill as $link ) {
 126              list( $key, $title ) = explode( ':', $link, 2 );
 127              $this->mInterlangs[$key] = $title;
 128          }
 129  
 130          foreach ( $this->mCategories as &$sortkey ) {
 131              # If the sortkey is longer then 255 bytes,
 132              # it truncated by DB, and then doesn't get
 133              # matched when comparing existing vs current
 134              # categories, causing bug 25254.
 135              # Also. substr behaves weird when given "".
 136              if ( $sortkey !== '' ) {
 137                  $sortkey = substr( $sortkey, 0, 255 );
 138              }
 139          }
 140  
 141          $this->mRecursive = $recursive;
 142  
 143          wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
 144      }
 145  
 146      /**
 147       * Update link tables with outgoing links from an updated article
 148       */
 149  	public function doUpdate() {
 150          wfRunHooks( 'LinksUpdate', array( &$this ) );
 151          $this->doIncrementalUpdate();
 152          wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
 153      }
 154  
 155  	protected function doIncrementalUpdate() {
 156          wfProfileIn( __METHOD__ );
 157  
 158          # Page links
 159          $existing = $this->getExistingLinks();
 160          $this->linkDeletions = $this->getLinkDeletions( $existing );
 161          $this->linkInsertions = $this->getLinkInsertions( $existing );
 162          $this->incrTableUpdate( 'pagelinks', 'pl', $this->linkDeletions, $this->linkInsertions );
 163  
 164          # Image links
 165          $existing = $this->getExistingImages();
 166  
 167          $imageDeletes = $this->getImageDeletions( $existing );
 168          $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes,
 169              $this->getImageInsertions( $existing ) );
 170  
 171          # Invalidate all image description pages which had links added or removed
 172          $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing );
 173          $this->invalidateImageDescriptions( $imageUpdates );
 174  
 175          # External links
 176          $existing = $this->getExistingExternals();
 177          $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ),
 178              $this->getExternalInsertions( $existing ) );
 179  
 180          # Language links
 181          $existing = $this->getExistingInterlangs();
 182          $this->incrTableUpdate( 'langlinks', 'll', $this->getInterlangDeletions( $existing ),
 183              $this->getInterlangInsertions( $existing ) );
 184  
 185          # Inline interwiki links
 186          $existing = $this->getExistingInterwikis();
 187          $this->incrTableUpdate( 'iwlinks', 'iwl', $this->getInterwikiDeletions( $existing ),
 188              $this->getInterwikiInsertions( $existing ) );
 189  
 190          # Template links
 191          $existing = $this->getExistingTemplates();
 192          $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ),
 193              $this->getTemplateInsertions( $existing ) );
 194  
 195          # Category links
 196          $existing = $this->getExistingCategories();
 197  
 198          $categoryDeletes = $this->getCategoryDeletions( $existing );
 199  
 200          $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes,
 201              $this->getCategoryInsertions( $existing ) );
 202  
 203          # Invalidate all categories which were added, deleted or changed (set symmetric difference)
 204          $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
 205          $categoryUpdates = $categoryInserts + $categoryDeletes;
 206          $this->invalidateCategories( $categoryUpdates );
 207          $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
 208  
 209          # Page properties
 210          $existing = $this->getExistingProperties();
 211  
 212          $propertiesDeletes = $this->getPropertyDeletions( $existing );
 213  
 214          $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
 215              $this->getPropertyInsertions( $existing ) );
 216  
 217          # Invalidate the necessary pages
 218          $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
 219          $this->invalidateProperties( $changed );
 220  
 221          # Update the links table freshness for this title
 222          $this->updateLinksTimestamp();
 223  
 224          # Refresh links of all pages including this page
 225          # This will be in a separate transaction
 226          if ( $this->mRecursive ) {
 227              $this->queueRecursiveJobs();
 228          }
 229  
 230          wfProfileOut( __METHOD__ );
 231      }
 232  
 233      /**
 234       * Queue recursive jobs for this page
 235       *
 236       * Which means do LinksUpdate on all pages that include the current page,
 237       * using the job queue.
 238       */
 239  	function queueRecursiveJobs() {
 240          self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
 241          if ( $this->mTitle->getNamespace() == NS_FILE ) {
 242              // Process imagelinks in case the title is or was a redirect
 243              self::queueRecursiveJobsForTable( $this->mTitle, 'imagelinks' );
 244          }
 245      }
 246  
 247      /**
 248       * Queue a RefreshLinks job for any table.
 249       *
 250       * @param Title $title Title to do job for
 251       * @param string $table Table to use (e.g. 'templatelinks')
 252       */
 253  	public static function queueRecursiveJobsForTable( Title $title, $table ) {
 254          wfProfileIn( __METHOD__ );
 255          if ( $title->getBacklinkCache()->hasLinks( $table ) ) {
 256              $job = new RefreshLinksJob(
 257                  $title,
 258                  array(
 259                      'table' => $table,
 260                      'recursive' => true,
 261                  ) + Job::newRootJobParams( // "overall" refresh links job info
 262                      "refreshlinks:{$table}:{$title->getPrefixedText()}"
 263                  )
 264              );
 265              JobQueueGroup::singleton()->push( $job );
 266              JobQueueGroup::singleton()->deduplicateRootJob( $job );
 267          }
 268          wfProfileOut( __METHOD__ );
 269      }
 270  
 271      /**
 272       * @param array $cats
 273       */
 274  	function invalidateCategories( $cats ) {
 275          $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
 276      }
 277  
 278      /**
 279       * Update all the appropriate counts in the category table.
 280       * @param array $added Associative array of category name => sort key
 281       * @param array $deleted Associative array of category name => sort key
 282       */
 283  	function updateCategoryCounts( $added, $deleted ) {
 284          $a = WikiPage::factory( $this->mTitle );
 285          $a->updateCategoryCounts(
 286              array_keys( $added ), array_keys( $deleted )
 287          );
 288      }
 289  
 290      /**
 291       * @param array $images
 292       */
 293  	function invalidateImageDescriptions( $images ) {
 294          $this->invalidatePages( NS_FILE, array_keys( $images ) );
 295      }
 296  
 297      /**
 298       * Update a table by doing a delete query then an insert query
 299       * @param string $table Table name
 300       * @param string $prefix Field name prefix
 301       * @param array $deletions
 302       * @param array $insertions Rows to insert
 303       */
 304  	function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
 305          if ( $table == 'page_props' ) {
 306              $fromField = 'pp_page';
 307          } else {
 308              $fromField = "{$prefix}_from";
 309          }
 310          $where = array( $fromField => $this->mId );
 311          if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) {
 312              if ( $table == 'iwlinks' ) {
 313                  $baseKey = 'iwl_prefix';
 314              } else {
 315                  $baseKey = "{$prefix}_namespace";
 316              }
 317              $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" );
 318              if ( $clause ) {
 319                  $where[] = $clause;
 320              } else {
 321                  $where = false;
 322              }
 323          } else {
 324              if ( $table == 'langlinks' ) {
 325                  $toField = 'll_lang';
 326              } elseif ( $table == 'page_props' ) {
 327                  $toField = 'pp_propname';
 328              } else {
 329                  $toField = $prefix . '_to';
 330              }
 331              if ( count( $deletions ) ) {
 332                  $where[$toField] = array_keys( $deletions );
 333              } else {
 334                  $where = false;
 335              }
 336          }
 337          if ( $where ) {
 338              $this->mDb->delete( $table, $where, __METHOD__ );
 339          }
 340          if ( count( $insertions ) ) {
 341              $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
 342              wfRunHooks( 'LinksUpdateAfterInsert', array( $this, $table, $insertions ) );
 343          }
 344      }
 345  
 346      /**
 347       * Get an array of pagelinks insertions for passing to the DB
 348       * Skips the titles specified by the 2-D array $existing
 349       * @param array $existing
 350       * @return array
 351       */
 352  	private function getLinkInsertions( $existing = array() ) {
 353          $arr = array();
 354          foreach ( $this->mLinks as $ns => $dbkeys ) {
 355              $diffs = isset( $existing[$ns] )
 356                  ? array_diff_key( $dbkeys, $existing[$ns] )
 357                  : $dbkeys;
 358              foreach ( $diffs as $dbk => $id ) {
 359                  $arr[] = array(
 360                      'pl_from' => $this->mId,
 361                      'pl_from_namespace' => $this->mTitle->getNamespace(),
 362                      'pl_namespace' => $ns,
 363                      'pl_title' => $dbk
 364                  );
 365              }
 366          }
 367  
 368          return $arr;
 369      }
 370  
 371      /**
 372       * Get an array of template insertions. Like getLinkInsertions()
 373       * @param array $existing
 374       * @return array
 375       */
 376  	private function getTemplateInsertions( $existing = array() ) {
 377          $arr = array();
 378          foreach ( $this->mTemplates as $ns => $dbkeys ) {
 379              $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
 380              foreach ( $diffs as $dbk => $id ) {
 381                  $arr[] = array(
 382                      'tl_from' => $this->mId,
 383                      'tl_from_namespace' => $this->mTitle->getNamespace(),
 384                      'tl_namespace' => $ns,
 385                      'tl_title' => $dbk
 386                  );
 387              }
 388          }
 389  
 390          return $arr;
 391      }
 392  
 393      /**
 394       * Get an array of image insertions
 395       * Skips the names specified in $existing
 396       * @param array $existing
 397       * @return array
 398       */
 399  	private function getImageInsertions( $existing = array() ) {
 400          $arr = array();
 401          $diffs = array_diff_key( $this->mImages, $existing );
 402          foreach ( $diffs as $iname => $dummy ) {
 403              $arr[] = array(
 404                  'il_from' => $this->mId,
 405                  'il_from_namespace' => $this->mTitle->getNamespace(),
 406                  'il_to' => $iname
 407              );
 408          }
 409  
 410          return $arr;
 411      }
 412  
 413      /**
 414       * Get an array of externallinks insertions. Skips the names specified in $existing
 415       * @param array $existing
 416       * @return array
 417       */
 418  	private function getExternalInsertions( $existing = array() ) {
 419          $arr = array();
 420          $diffs = array_diff_key( $this->mExternals, $existing );
 421          foreach ( $diffs as $url => $dummy ) {
 422              foreach ( wfMakeUrlIndexes( $url ) as $index ) {
 423                  $arr[] = array(
 424                      'el_id' => $this->mDb->nextSequenceValue( 'externallinks_el_id_seq' ),
 425                      'el_from' => $this->mId,
 426                      'el_to' => $url,
 427                      'el_index' => $index,
 428                  );
 429              }
 430          }
 431  
 432          return $arr;
 433      }
 434  
 435      /**
 436       * Get an array of category insertions
 437       *
 438       * @param array $existing Mapping existing category names to sort keys. If both
 439       * match a link in $this, the link will be omitted from the output
 440       *
 441       * @return array
 442       */
 443  	private function getCategoryInsertions( $existing = array() ) {
 444          global $wgContLang, $wgCategoryCollation;
 445          $diffs = array_diff_assoc( $this->mCategories, $existing );
 446          $arr = array();
 447          foreach ( $diffs as $name => $prefix ) {
 448              $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
 449              $wgContLang->findVariantLink( $name, $nt, true );
 450  
 451              if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
 452                  $type = 'subcat';
 453              } elseif ( $this->mTitle->getNamespace() == NS_FILE ) {
 454                  $type = 'file';
 455              } else {
 456                  $type = 'page';
 457              }
 458  
 459              # Treat custom sortkeys as a prefix, so that if multiple
 460              # things are forced to sort as '*' or something, they'll
 461              # sort properly in the category rather than in page_id
 462              # order or such.
 463              $sortkey = Collation::singleton()->getSortKey(
 464                  $this->mTitle->getCategorySortkey( $prefix ) );
 465  
 466              $arr[] = array(
 467                  'cl_from' => $this->mId,
 468                  'cl_to' => $name,
 469                  'cl_sortkey' => $sortkey,
 470                  'cl_timestamp' => $this->mDb->timestamp(),
 471                  'cl_sortkey_prefix' => $prefix,
 472                  'cl_collation' => $wgCategoryCollation,
 473                  'cl_type' => $type,
 474              );
 475          }
 476  
 477          return $arr;
 478      }
 479  
 480      /**
 481       * Get an array of interlanguage link insertions
 482       *
 483       * @param array $existing Mapping existing language codes to titles
 484       *
 485       * @return array
 486       */
 487  	private function getInterlangInsertions( $existing = array() ) {
 488          $diffs = array_diff_assoc( $this->mInterlangs, $existing );
 489          $arr = array();
 490          foreach ( $diffs as $lang => $title ) {
 491              $arr[] = array(
 492                  'll_from' => $this->mId,
 493                  'll_lang' => $lang,
 494                  'll_title' => $title
 495              );
 496          }
 497  
 498          return $arr;
 499      }
 500  
 501      /**
 502       * Get an array of page property insertions
 503       * @param array $existing
 504       * @return array
 505       */
 506  	function getPropertyInsertions( $existing = array() ) {
 507          $diffs = array_diff_assoc( $this->mProperties, $existing );
 508  
 509          $arr = array();
 510          foreach ( array_keys( $diffs ) as $name ) {
 511              $arr[] = $this->getPagePropRowData( $name );
 512          }
 513  
 514          return $arr;
 515      }
 516  
 517      /**
 518       * Returns an associative array to be used for inserting a row into
 519       * the page_props table. Besides the given property name, this will
 520       * include the page id from $this->mId and any property value from
 521       * $this->mProperties.
 522       *
 523       * The array returned will include the pp_sortkey field if this
 524       * is present in the database (as indicated by $wgPagePropsHaveSortkey).
 525       * The sortkey value is currently determined by getPropertySortKeyValue().
 526       *
 527       * @note this assumes that $this->mProperties[$prop] is defined.
 528       *
 529       * @param string $prop The name of the property.
 530       *
 531       * @return array
 532       */
 533  	private function getPagePropRowData( $prop ) {
 534          global $wgPagePropsHaveSortkey;
 535  
 536          $value = $this->mProperties[$prop];
 537  
 538          $row = array(
 539              'pp_page' => $this->mId,
 540              'pp_propname' => $prop,
 541              'pp_value' => $value,
 542          );
 543  
 544          if ( $wgPagePropsHaveSortkey ) {
 545              $row['pp_sortkey'] = $this->getPropertySortKeyValue( $value );
 546          }
 547  
 548          return $row;
 549      }
 550  
 551      /**
 552       * Determines the sort key for the given property value.
 553       * This will return $value if it is a float or int,
 554       * 1 or resp. 0 if it is a bool, and null otherwise.
 555       *
 556       * @note In the future, we may allow the sortkey to be specified explicitly
 557       *       in ParserOutput::setProperty.
 558       *
 559       * @param mixed $value
 560       *
 561       * @return float|null
 562       */
 563  	private function getPropertySortKeyValue( $value ) {
 564          if ( is_int( $value ) || is_float( $value ) || is_bool( $value ) ) {
 565              return floatval( $value );
 566          }
 567  
 568          return null;
 569      }
 570  
 571      /**
 572       * Get an array of interwiki insertions for passing to the DB
 573       * Skips the titles specified by the 2-D array $existing
 574       * @param array $existing
 575       * @return array
 576       */
 577  	private function getInterwikiInsertions( $existing = array() ) {
 578          $arr = array();
 579          foreach ( $this->mInterwikis as $prefix => $dbkeys ) {
 580              $diffs = isset( $existing[$prefix] )
 581                  ? array_diff_key( $dbkeys, $existing[$prefix] )
 582                  : $dbkeys;
 583  
 584              foreach ( $diffs as $dbk => $id ) {
 585                  $arr[] = array(
 586                      'iwl_from' => $this->mId,
 587                      'iwl_prefix' => $prefix,
 588                      'iwl_title' => $dbk
 589                  );
 590              }
 591          }
 592  
 593          return $arr;
 594      }
 595  
 596      /**
 597       * Given an array of existing links, returns those links which are not in $this
 598       * and thus should be deleted.
 599       * @param array $existing
 600       * @return array
 601       */
 602  	private function getLinkDeletions( $existing ) {
 603          $del = array();
 604          foreach ( $existing as $ns => $dbkeys ) {
 605              if ( isset( $this->mLinks[$ns] ) ) {
 606                  $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
 607              } else {
 608                  $del[$ns] = $existing[$ns];
 609              }
 610          }
 611  
 612          return $del;
 613      }
 614  
 615      /**
 616       * Given an array of existing templates, returns those templates which are not in $this
 617       * and thus should be deleted.
 618       * @param array $existing
 619       * @return array
 620       */
 621  	private function getTemplateDeletions( $existing ) {
 622          $del = array();
 623          foreach ( $existing as $ns => $dbkeys ) {
 624              if ( isset( $this->mTemplates[$ns] ) ) {
 625                  $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
 626              } else {
 627                  $del[$ns] = $existing[$ns];
 628              }
 629          }
 630  
 631          return $del;
 632      }
 633  
 634      /**
 635       * Given an array of existing images, returns those images which are not in $this
 636       * and thus should be deleted.
 637       * @param array $existing
 638       * @return array
 639       */
 640  	private function getImageDeletions( $existing ) {
 641          return array_diff_key( $existing, $this->mImages );
 642      }
 643  
 644      /**
 645       * Given an array of existing external links, returns those links which are not
 646       * in $this and thus should be deleted.
 647       * @param array $existing
 648       * @return array
 649       */
 650  	private function getExternalDeletions( $existing ) {
 651          return array_diff_key( $existing, $this->mExternals );
 652      }
 653  
 654      /**
 655       * Given an array of existing categories, returns those categories which are not in $this
 656       * and thus should be deleted.
 657       * @param array $existing
 658       * @return array
 659       */
 660  	private function getCategoryDeletions( $existing ) {
 661          return array_diff_assoc( $existing, $this->mCategories );
 662      }
 663  
 664      /**
 665       * Given an array of existing interlanguage links, returns those links which are not
 666       * in $this and thus should be deleted.
 667       * @param array $existing
 668       * @return array
 669       */
 670  	private function getInterlangDeletions( $existing ) {
 671          return array_diff_assoc( $existing, $this->mInterlangs );
 672      }
 673  
 674      /**
 675       * Get array of properties which should be deleted.
 676       * @param array $existing
 677       * @return array
 678       */
 679  	function getPropertyDeletions( $existing ) {
 680          return array_diff_assoc( $existing, $this->mProperties );
 681      }
 682  
 683      /**
 684       * Given an array of existing interwiki links, returns those links which are not in $this
 685       * and thus should be deleted.
 686       * @param array $existing
 687       * @return array
 688       */
 689  	private function getInterwikiDeletions( $existing ) {
 690          $del = array();
 691          foreach ( $existing as $prefix => $dbkeys ) {
 692              if ( isset( $this->mInterwikis[$prefix] ) ) {
 693                  $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
 694              } else {
 695                  $del[$prefix] = $existing[$prefix];
 696              }
 697          }
 698  
 699          return $del;
 700      }
 701  
 702      /**
 703       * Get an array of existing links, as a 2-D array
 704       *
 705       * @return array
 706       */
 707  	private function getExistingLinks() {
 708          $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
 709              array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
 710          $arr = array();
 711          foreach ( $res as $row ) {
 712              if ( !isset( $arr[$row->pl_namespace] ) ) {
 713                  $arr[$row->pl_namespace] = array();
 714              }
 715              $arr[$row->pl_namespace][$row->pl_title] = 1;
 716          }
 717  
 718          return $arr;
 719      }
 720  
 721      /**
 722       * Get an array of existing templates, as a 2-D array
 723       *
 724       * @return array
 725       */
 726  	private function getExistingTemplates() {
 727          $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
 728              array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
 729          $arr = array();
 730          foreach ( $res as $row ) {
 731              if ( !isset( $arr[$row->tl_namespace] ) ) {
 732                  $arr[$row->tl_namespace] = array();
 733              }
 734              $arr[$row->tl_namespace][$row->tl_title] = 1;
 735          }
 736  
 737          return $arr;
 738      }
 739  
 740      /**
 741       * Get an array of existing images, image names in the keys
 742       *
 743       * @return array
 744       */
 745  	private function getExistingImages() {
 746          $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
 747              array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
 748          $arr = array();
 749          foreach ( $res as $row ) {
 750              $arr[$row->il_to] = 1;
 751          }
 752  
 753          return $arr;
 754      }
 755  
 756      /**
 757       * Get an array of existing external links, URLs in the keys
 758       *
 759       * @return array
 760       */
 761  	private function getExistingExternals() {
 762          $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
 763              array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
 764          $arr = array();
 765          foreach ( $res as $row ) {
 766              $arr[$row->el_to] = 1;
 767          }
 768  
 769          return $arr;
 770      }
 771  
 772      /**
 773       * Get an array of existing categories, with the name in the key and sort key in the value.
 774       *
 775       * @return array
 776       */
 777  	private function getExistingCategories() {
 778          $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
 779              array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
 780          $arr = array();
 781          foreach ( $res as $row ) {
 782              $arr[$row->cl_to] = $row->cl_sortkey_prefix;
 783          }
 784  
 785          return $arr;
 786      }
 787  
 788      /**
 789       * Get an array of existing interlanguage links, with the language code in the key and the
 790       * title in the value.
 791       *
 792       * @return array
 793       */
 794  	private function getExistingInterlangs() {
 795          $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
 796              array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
 797          $arr = array();
 798          foreach ( $res as $row ) {
 799              $arr[$row->ll_lang] = $row->ll_title;
 800          }
 801  
 802          return $arr;
 803      }
 804  
 805      /**
 806       * Get an array of existing inline interwiki links, as a 2-D array
 807       * @return array (prefix => array(dbkey => 1))
 808       */
 809  	protected function getExistingInterwikis() {
 810          $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
 811              array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
 812          $arr = array();
 813          foreach ( $res as $row ) {
 814              if ( !isset( $arr[$row->iwl_prefix] ) ) {
 815                  $arr[$row->iwl_prefix] = array();
 816              }
 817              $arr[$row->iwl_prefix][$row->iwl_title] = 1;
 818          }
 819  
 820          return $arr;
 821      }
 822  
 823      /**
 824       * Get an array of existing categories, with the name in the key and sort key in the value.
 825       *
 826       * @return array Array of property names and values
 827       */
 828  	private function getExistingProperties() {
 829          $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
 830              array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
 831          $arr = array();
 832          foreach ( $res as $row ) {
 833              $arr[$row->pp_propname] = $row->pp_value;
 834          }
 835  
 836          return $arr;
 837      }
 838  
 839      /**
 840       * Return the title object of the page being updated
 841       * @return Title
 842       */
 843  	public function getTitle() {
 844          return $this->mTitle;
 845      }
 846  
 847      /**
 848       * Returns parser output
 849       * @since 1.19
 850       * @return ParserOutput
 851       */
 852  	public function getParserOutput() {
 853          return $this->mParserOutput;
 854      }
 855  
 856      /**
 857       * Return the list of images used as generated by the parser
 858       * @return array
 859       */
 860  	public function getImages() {
 861          return $this->mImages;
 862      }
 863  
 864      /**
 865       * Invalidate any necessary link lists related to page property changes
 866       * @param array $changed
 867       */
 868  	private function invalidateProperties( $changed ) {
 869          global $wgPagePropLinkInvalidations;
 870  
 871          foreach ( $changed as $name => $value ) {
 872              if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
 873                  $inv = $wgPagePropLinkInvalidations[$name];
 874                  if ( !is_array( $inv ) ) {
 875                      $inv = array( $inv );
 876                  }
 877                  foreach ( $inv as $table ) {
 878                      $update = new HTMLCacheUpdate( $this->mTitle, $table );
 879                      $update->doUpdate();
 880                  }
 881              }
 882          }
 883      }
 884  
 885      /**
 886       * Fetch page links added by this LinksUpdate.  Only available after the update is complete.
 887       * @since 1.22
 888       * @return null|array Array of Titles
 889       */
 890  	public function getAddedLinks() {
 891          if ( $this->linkInsertions === null ) {
 892              return null;
 893          }
 894          $result = array();
 895          foreach ( $this->linkInsertions as $insertion ) {
 896              $result[] = Title::makeTitle( $insertion['pl_namespace'], $insertion['pl_title'] );
 897          }
 898  
 899          return $result;
 900      }
 901  
 902      /**
 903       * Fetch page links removed by this LinksUpdate.  Only available after the update is complete.
 904       * @since 1.22
 905       * @return null|array Array of Titles
 906       */
 907  	public function getRemovedLinks() {
 908          if ( $this->linkDeletions === null ) {
 909              return null;
 910          }
 911          $result = array();
 912          foreach ( $this->linkDeletions as $ns => $titles ) {
 913              foreach ( $titles as $title => $unused ) {
 914                  $result[] = Title::makeTitle( $ns, $title );
 915              }
 916          }
 917  
 918          return $result;
 919      }
 920  
 921      /**
 922       * Update links table freshness
 923       */
 924  	protected function updateLinksTimestamp() {
 925          if ( $this->mId ) {
 926              // The link updates made here only reflect the freshness of the parser output
 927              $timestamp = $this->mParserOutput->getCacheTime();
 928              $this->mDb->update( 'page',
 929                  array( 'page_links_updated' => $this->mDb->timestamp( $timestamp ) ),
 930                  array( 'page_id' => $this->mId ),
 931                  __METHOD__
 932              );
 933          }
 934      }
 935  }
 936  
 937  /**
 938   * Update object handling the cleanup of links tables after a page was deleted.
 939   **/
 940  class LinksDeletionUpdate extends SqlDataUpdate {
 941      /** @var WikiPage The WikiPage that was deleted */
 942      protected $mPage;
 943  
 944      /**
 945       * Constructor
 946       *
 947       * @param WikiPage $page Page we are updating
 948       * @throws MWException
 949       */
 950  	function __construct( WikiPage $page ) {
 951          parent::__construct( false ); // no implicit transaction
 952  
 953          $this->mPage = $page;
 954  
 955          if ( !$page->exists() ) {
 956              throw new MWException( "Page ID not known, perhaps the page doesn't exist?" );
 957          }
 958      }
 959  
 960      /**
 961       * Do some database updates after deletion
 962       */
 963  	public function doUpdate() {
 964          $title = $this->mPage->getTitle();
 965          $id = $this->mPage->getId();
 966  
 967          # Delete restrictions for it
 968          $this->mDb->delete( 'page_restrictions', array( 'pr_page' => $id ), __METHOD__ );
 969  
 970          # Fix category table counts
 971          $cats = array();
 972          $res = $this->mDb->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
 973  
 974          foreach ( $res as $row ) {
 975              $cats[] = $row->cl_to;
 976          }
 977  
 978          $this->mPage->updateCategoryCounts( array(), $cats );
 979  
 980          # If using cascading deletes, we can skip some explicit deletes
 981          if ( !$this->mDb->cascadingDeletes() ) {
 982              # Delete outgoing links
 983              $this->mDb->delete( 'pagelinks', array( 'pl_from' => $id ), __METHOD__ );
 984              $this->mDb->delete( 'imagelinks', array( 'il_from' => $id ), __METHOD__ );
 985              $this->mDb->delete( 'categorylinks', array( 'cl_from' => $id ), __METHOD__ );
 986              $this->mDb->delete( 'templatelinks', array( 'tl_from' => $id ), __METHOD__ );
 987              $this->mDb->delete( 'externallinks', array( 'el_from' => $id ), __METHOD__ );
 988              $this->mDb->delete( 'langlinks', array( 'll_from' => $id ), __METHOD__ );
 989              $this->mDb->delete( 'iwlinks', array( 'iwl_from' => $id ), __METHOD__ );
 990              $this->mDb->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ );
 991              $this->mDb->delete( 'page_props', array( 'pp_page' => $id ), __METHOD__ );
 992          }
 993  
 994          # If using cleanup triggers, we can skip some manual deletes
 995          if ( !$this->mDb->cleanupTriggers() ) {
 996              # Clean up recentchanges entries...
 997              $this->mDb->delete( 'recentchanges',
 998                  array( 'rc_type != ' . RC_LOG,
 999                      'rc_namespace' => $title->getNamespace(),
1000                      'rc_title' => $title->getDBkey() ),
1001                  __METHOD__ );
1002              $this->mDb->delete( 'recentchanges',
1003                  array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
1004                  __METHOD__ );
1005          }
1006      }
1007  
1008      /**
1009       * Update all the appropriate counts in the category table.
1010       * @param array $added Associative array of category name => sort key
1011       * @param array $deleted Associative array of category name => sort key
1012       */
1013  	function updateCategoryCounts( $added, $deleted ) {
1014          $a = WikiPage::factory( $this->mTitle );
1015          $a->updateCategoryCounts(
1016              array_keys( $added ), array_keys( $deleted )
1017          );
1018      }
1019  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1