LinksUpdate.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. <?php
  2. /**
  3. * See docs/deferred.txt
  4. *
  5. * @todo document (e.g. one-sentence top-level class description).
  6. */
  7. class LinksUpdate {
  8. /**@{{
  9. * @private
  10. */
  11. var $mId, //!< Page ID of the article linked from
  12. $mTitle, //!< Title object of the article linked from
  13. $mLinks, //!< Map of title strings to IDs for the links in the document
  14. $mImages, //!< DB keys of the images used, in the array key only
  15. $mTemplates, //!< Map of title strings to IDs for the template references, including broken ones
  16. $mExternals, //!< URLs of external links, array key only
  17. $mCategories, //!< Map of category names to sort keys
  18. $mInterlangs, //!< Map of language codes to titles
  19. $mProperties, //!< Map of arbitrary name to value
  20. $mDb, //!< Database connection reference
  21. $mOptions, //!< SELECT options to be used (array)
  22. $mRecursive; //!< Whether to queue jobs for recursive updates
  23. /**@}}*/
  24. /**
  25. * Constructor
  26. *
  27. * @param Title $title Title of the page we're updating
  28. * @param ParserOutput $parserOutput Output from a full parse of this page
  29. * @param bool $recursive Queue jobs for recursive updates?
  30. */
  31. function LinksUpdate( $title, $parserOutput, $recursive = true ) {
  32. global $wgAntiLockFlags;
  33. if ( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) {
  34. $this->mOptions = array();
  35. } else {
  36. $this->mOptions = array( 'FOR UPDATE' );
  37. }
  38. $this->mDb = wfGetDB( DB_MASTER );
  39. if ( !is_object( $title ) ) {
  40. throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
  41. "Please see Article::editUpdates() for an invocation example.\n" );
  42. }
  43. $this->mTitle = $title;
  44. $this->mId = $title->getArticleID();
  45. $this->mParserOutput = $parserOutput;
  46. $this->mLinks = $parserOutput->getLinks();
  47. $this->mImages = $parserOutput->getImages();
  48. $this->mTemplates = $parserOutput->getTemplates();
  49. $this->mExternals = $parserOutput->getExternalLinks();
  50. $this->mCategories = $parserOutput->getCategories();
  51. $this->mProperties = $parserOutput->getProperties();
  52. # Convert the format of the interlanguage links
  53. # I didn't want to change it in the ParserOutput, because that array is passed all
  54. # the way back to the skin, so either a skin API break would be required, or an
  55. # inefficient back-conversion.
  56. $ill = $parserOutput->getLanguageLinks();
  57. $this->mInterlangs = array();
  58. foreach ( $ill as $link ) {
  59. list( $key, $title ) = explode( ':', $link, 2 );
  60. $this->mInterlangs[$key] = $title;
  61. }
  62. $this->mRecursive = $recursive;
  63. $this->mTouchTmplLinks = false;
  64. wfRunHooks( 'LinksUpdateConstructed', array( &$this ) );
  65. }
  66. /**
  67. * Update link tables with outgoing links from an updated article
  68. */
  69. public function doUpdate() {
  70. global $wgUseDumbLinkUpdate;
  71. wfRunHooks( 'LinksUpdate', array( &$this ) );
  72. if ( $wgUseDumbLinkUpdate ) {
  73. $this->doDumbUpdate();
  74. } else {
  75. $this->doIncrementalUpdate();
  76. }
  77. wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
  78. }
  79. protected function doIncrementalUpdate() {
  80. wfProfileIn( __METHOD__ );
  81. # Page links
  82. $existing = $this->getExistingLinks();
  83. $this->incrTableUpdate( 'pagelinks', 'pl', $this->getLinkDeletions( $existing ),
  84. $this->getLinkInsertions( $existing ) );
  85. # Image links
  86. $existing = $this->getExistingImages();
  87. $imageDeletes = $this->getImageDeletions( $existing );
  88. $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes, $this->getImageInsertions( $existing ) );
  89. # Invalidate all image description pages which had links added or removed
  90. $imageUpdates = $imageDeletes + array_diff_key( $this->mImages, $existing );
  91. $this->invalidateImageDescriptions( $imageUpdates );
  92. # External links
  93. $existing = $this->getExistingExternals();
  94. $this->incrTableUpdate( 'externallinks', 'el', $this->getExternalDeletions( $existing ),
  95. $this->getExternalInsertions( $existing ) );
  96. # Language links
  97. $existing = $this->getExistingInterlangs();
  98. $this->incrTableUpdate( 'langlinks', 'll', $this->getInterlangDeletions( $existing ),
  99. $this->getInterlangInsertions( $existing ) );
  100. # Template links
  101. $existing = $this->getExistingTemplates();
  102. $this->incrTableUpdate( 'templatelinks', 'tl', $this->getTemplateDeletions( $existing ),
  103. $this->getTemplateInsertions( $existing ) );
  104. # Category links
  105. $existing = $this->getExistingCategories();
  106. $categoryDeletes = $this->getCategoryDeletions( $existing );
  107. $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes, $this->getCategoryInsertions( $existing ) );
  108. # Invalidate all categories which were added, deleted or changed (set symmetric difference)
  109. $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
  110. $categoryUpdates = $categoryInserts + $categoryDeletes;
  111. $this->invalidateCategories( $categoryUpdates );
  112. $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
  113. # Page properties
  114. $existing = $this->getExistingProperties();
  115. $propertiesDeletes = $this->getPropertyDeletions( $existing );
  116. $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes, $this->getPropertyInsertions( $existing ) );
  117. # Invalidate the necessary pages
  118. $changed = $propertiesDeletes + array_diff_assoc( $this->mProperties, $existing );
  119. $this->invalidateProperties( $changed );
  120. # Refresh links of all pages including this page
  121. # This will be in a separate transaction
  122. if ( $this->mRecursive ) {
  123. $this->queueRecursiveJobs();
  124. }
  125. wfProfileOut( __METHOD__ );
  126. }
  127. /**
  128. * Link update which clears the previous entries and inserts new ones
  129. * May be slower or faster depending on level of lock contention and write speed of DB
  130. * Also useful where link table corruption needs to be repaired, e.g. in refreshLinks.php
  131. */
  132. protected function doDumbUpdate() {
  133. wfProfileIn( __METHOD__ );
  134. # Refresh category pages and image description pages
  135. $existing = $this->getExistingCategories();
  136. $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
  137. $categoryDeletes = array_diff_assoc( $existing, $this->mCategories );
  138. $categoryUpdates = $categoryInserts + $categoryDeletes;
  139. $existing = $this->getExistingImages();
  140. $imageUpdates = array_diff_key( $existing, $this->mImages ) + array_diff_key( $this->mImages, $existing );
  141. $this->dumbTableUpdate( 'pagelinks', $this->getLinkInsertions(), 'pl_from' );
  142. $this->dumbTableUpdate( 'imagelinks', $this->getImageInsertions(), 'il_from' );
  143. $this->dumbTableUpdate( 'categorylinks', $this->getCategoryInsertions(), 'cl_from' );
  144. $this->dumbTableUpdate( 'templatelinks', $this->getTemplateInsertions(), 'tl_from' );
  145. $this->dumbTableUpdate( 'externallinks', $this->getExternalInsertions(), 'el_from' );
  146. $this->dumbTableUpdate( 'langlinks', $this->getInterlangInsertions(),'ll_from' );
  147. $this->dumbTableUpdate( 'page_props', $this->getPropertyInsertions(), 'pp_page' );
  148. # Update the cache of all the category pages and image description
  149. # pages which were changed, and fix the category table count
  150. $this->invalidateCategories( $categoryUpdates );
  151. $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
  152. $this->invalidateImageDescriptions( $imageUpdates );
  153. # Refresh links of all pages including this page
  154. # This will be in a separate transaction
  155. if ( $this->mRecursive ) {
  156. $this->queueRecursiveJobs();
  157. }
  158. wfProfileOut( __METHOD__ );
  159. }
  160. function queueRecursiveJobs() {
  161. global $wgUpdateRowsPerJob;
  162. wfProfileIn( __METHOD__ );
  163. $cache = $this->mTitle->getBacklinkCache();
  164. $batches = $cache->partition( 'templatelinks', $wgUpdateRowsPerJob );
  165. if ( !$batches ) {
  166. wfProfileOut( __METHOD__ );
  167. return;
  168. }
  169. $jobs = array();
  170. foreach ( $batches as $batch ) {
  171. list( $start, $end ) = $batch;
  172. $params = array(
  173. 'start' => $start,
  174. 'end' => $end,
  175. );
  176. $jobs[] = new RefreshLinksJob2( $this->mTitle, $params );
  177. }
  178. Job::batchInsert( $jobs );
  179. wfProfileOut( __METHOD__ );
  180. }
  181. /**
  182. * Invalidate the cache of a list of pages from a single namespace
  183. *
  184. * @param integer $namespace
  185. * @param array $dbkeys
  186. */
  187. function invalidatePages( $namespace, $dbkeys ) {
  188. if ( !count( $dbkeys ) ) {
  189. return;
  190. }
  191. /**
  192. * Determine which pages need to be updated
  193. * This is necessary to prevent the job queue from smashing the DB with
  194. * large numbers of concurrent invalidations of the same page
  195. */
  196. $now = $this->mDb->timestamp();
  197. $ids = array();
  198. $res = $this->mDb->select( 'page', array( 'page_id' ),
  199. array(
  200. 'page_namespace' => $namespace,
  201. 'page_title IN (' . $this->mDb->makeList( $dbkeys ) . ')',
  202. 'page_touched < ' . $this->mDb->addQuotes( $now )
  203. ), __METHOD__
  204. );
  205. while ( $row = $this->mDb->fetchObject( $res ) ) {
  206. $ids[] = $row->page_id;
  207. }
  208. if ( !count( $ids ) ) {
  209. return;
  210. }
  211. /**
  212. * Do the update
  213. * We still need the page_touched condition, in case the row has changed since
  214. * the non-locking select above.
  215. */
  216. $this->mDb->update( 'page', array( 'page_touched' => $now ),
  217. array(
  218. 'page_id IN (' . $this->mDb->makeList( $ids ) . ')',
  219. 'page_touched < ' . $this->mDb->addQuotes( $now )
  220. ), __METHOD__
  221. );
  222. }
  223. function invalidateCategories( $cats ) {
  224. $this->invalidatePages( NS_CATEGORY, array_keys( $cats ) );
  225. }
  226. /**
  227. * Update all the appropriate counts in the category table.
  228. * @param $added associative array of category name => sort key
  229. * @param $deleted associative array of category name => sort key
  230. */
  231. function updateCategoryCounts( $added, $deleted ) {
  232. $a = new Article($this->mTitle);
  233. $a->updateCategoryCounts(
  234. array_keys( $added ), array_keys( $deleted )
  235. );
  236. }
  237. function invalidateImageDescriptions( $images ) {
  238. $this->invalidatePages( NS_FILE, array_keys( $images ) );
  239. }
  240. function dumbTableUpdate( $table, $insertions, $fromField ) {
  241. $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ );
  242. if ( count( $insertions ) ) {
  243. # The link array was constructed without FOR UPDATE, so there may
  244. # be collisions. This may cause minor link table inconsistencies,
  245. # which is better than crippling the site with lock contention.
  246. $this->mDb->insert( $table, $insertions, __METHOD__, array( 'IGNORE' ) );
  247. }
  248. }
  249. /**
  250. * Make a WHERE clause from a 2-d NS/dbkey array
  251. *
  252. * @param array $arr 2-d array indexed by namespace and DB key
  253. * @param string $prefix Field name prefix, without the underscore
  254. */
  255. function makeWhereFrom2d( &$arr, $prefix ) {
  256. $lb = new LinkBatch;
  257. $lb->setArray( $arr );
  258. return $lb->constructSet( $prefix, $this->mDb );
  259. }
  260. /**
  261. * Update a table by doing a delete query then an insert query
  262. * @private
  263. */
  264. function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
  265. if ( $table == 'page_props' ) {
  266. $fromField = 'pp_page';
  267. } else {
  268. $fromField = "{$prefix}_from";
  269. }
  270. $where = array( $fromField => $this->mId );
  271. if ( $table == 'pagelinks' || $table == 'templatelinks' ) {
  272. $clause = $this->makeWhereFrom2d( $deletions, $prefix );
  273. if ( $clause ) {
  274. $where[] = $clause;
  275. } else {
  276. $where = false;
  277. }
  278. } else {
  279. if ( $table == 'langlinks' ) {
  280. $toField = 'll_lang';
  281. } elseif ( $table == 'page_props' ) {
  282. $toField = 'pp_propname';
  283. } else {
  284. $toField = $prefix . '_to';
  285. }
  286. if ( count( $deletions ) ) {
  287. $where[] = "$toField IN (" . $this->mDb->makeList( array_keys( $deletions ) ) . ')';
  288. } else {
  289. $where = false;
  290. }
  291. }
  292. if ( $where ) {
  293. $this->mDb->delete( $table, $where, __METHOD__ );
  294. }
  295. if ( count( $insertions ) ) {
  296. $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
  297. }
  298. }
  299. /**
  300. * Get an array of pagelinks insertions for passing to the DB
  301. * Skips the titles specified by the 2-D array $existing
  302. * @private
  303. */
  304. function getLinkInsertions( $existing = array() ) {
  305. $arr = array();
  306. foreach( $this->mLinks as $ns => $dbkeys ) {
  307. # array_diff_key() was introduced in PHP 5.1, there is a compatibility function
  308. # in GlobalFunctions.php
  309. $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
  310. foreach ( $diffs as $dbk => $id ) {
  311. $arr[] = array(
  312. 'pl_from' => $this->mId,
  313. 'pl_namespace' => $ns,
  314. 'pl_title' => $dbk
  315. );
  316. }
  317. }
  318. return $arr;
  319. }
  320. /**
  321. * Get an array of template insertions. Like getLinkInsertions()
  322. * @private
  323. */
  324. function getTemplateInsertions( $existing = array() ) {
  325. $arr = array();
  326. foreach( $this->mTemplates as $ns => $dbkeys ) {
  327. $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
  328. foreach ( $diffs as $dbk => $id ) {
  329. $arr[] = array(
  330. 'tl_from' => $this->mId,
  331. 'tl_namespace' => $ns,
  332. 'tl_title' => $dbk
  333. );
  334. }
  335. }
  336. return $arr;
  337. }
  338. /**
  339. * Get an array of image insertions
  340. * Skips the names specified in $existing
  341. * @private
  342. */
  343. function getImageInsertions( $existing = array() ) {
  344. $arr = array();
  345. $diffs = array_diff_key( $this->mImages, $existing );
  346. foreach( $diffs as $iname => $dummy ) {
  347. $arr[] = array(
  348. 'il_from' => $this->mId,
  349. 'il_to' => $iname
  350. );
  351. }
  352. return $arr;
  353. }
  354. /**
  355. * Get an array of externallinks insertions. Skips the names specified in $existing
  356. * @private
  357. */
  358. function getExternalInsertions( $existing = array() ) {
  359. $arr = array();
  360. $diffs = array_diff_key( $this->mExternals, $existing );
  361. foreach( $diffs as $url => $dummy ) {
  362. $arr[] = array(
  363. 'el_from' => $this->mId,
  364. 'el_to' => $url,
  365. 'el_index' => wfMakeUrlIndex( $url ),
  366. );
  367. }
  368. return $arr;
  369. }
  370. /**
  371. * Get an array of category insertions
  372. * @param array $existing Array mapping existing category names to sort keys. If both
  373. * match a link in $this, the link will be omitted from the output
  374. * @private
  375. */
  376. function getCategoryInsertions( $existing = array() ) {
  377. global $wgContLang;
  378. $diffs = array_diff_assoc( $this->mCategories, $existing );
  379. $arr = array();
  380. foreach ( $diffs as $name => $sortkey ) {
  381. $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
  382. $wgContLang->findVariantLink( $name, $nt, true );
  383. $arr[] = array(
  384. 'cl_from' => $this->mId,
  385. 'cl_to' => $name,
  386. 'cl_sortkey' => $sortkey,
  387. 'cl_timestamp' => $this->mDb->timestamp()
  388. );
  389. }
  390. return $arr;
  391. }
  392. /**
  393. * Get an array of interlanguage link insertions
  394. * @param array $existing Array mapping existing language codes to titles
  395. * @private
  396. */
  397. function getInterlangInsertions( $existing = array() ) {
  398. $diffs = array_diff_assoc( $this->mInterlangs, $existing );
  399. $arr = array();
  400. foreach( $diffs as $lang => $title ) {
  401. $arr[] = array(
  402. 'll_from' => $this->mId,
  403. 'll_lang' => $lang,
  404. 'll_title' => $title
  405. );
  406. }
  407. return $arr;
  408. }
  409. /**
  410. * Get an array of page property insertions
  411. */
  412. function getPropertyInsertions( $existing = array() ) {
  413. $diffs = array_diff_assoc( $this->mProperties, $existing );
  414. $arr = array();
  415. foreach ( $diffs as $name => $value ) {
  416. $arr[] = array(
  417. 'pp_page' => $this->mId,
  418. 'pp_propname' => $name,
  419. 'pp_value' => $value,
  420. );
  421. }
  422. return $arr;
  423. }
  424. /**
  425. * Given an array of existing links, returns those links which are not in $this
  426. * and thus should be deleted.
  427. * @private
  428. */
  429. function getLinkDeletions( $existing ) {
  430. $del = array();
  431. foreach ( $existing as $ns => $dbkeys ) {
  432. if ( isset( $this->mLinks[$ns] ) ) {
  433. $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
  434. } else {
  435. $del[$ns] = $existing[$ns];
  436. }
  437. }
  438. return $del;
  439. }
  440. /**
  441. * Given an array of existing templates, returns those templates which are not in $this
  442. * and thus should be deleted.
  443. * @private
  444. */
  445. function getTemplateDeletions( $existing ) {
  446. $del = array();
  447. foreach ( $existing as $ns => $dbkeys ) {
  448. if ( isset( $this->mTemplates[$ns] ) ) {
  449. $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
  450. } else {
  451. $del[$ns] = $existing[$ns];
  452. }
  453. }
  454. return $del;
  455. }
  456. /**
  457. * Given an array of existing images, returns those images which are not in $this
  458. * and thus should be deleted.
  459. * @private
  460. */
  461. function getImageDeletions( $existing ) {
  462. return array_diff_key( $existing, $this->mImages );
  463. }
  464. /**
  465. * Given an array of existing external links, returns those links which are not
  466. * in $this and thus should be deleted.
  467. * @private
  468. */
  469. function getExternalDeletions( $existing ) {
  470. return array_diff_key( $existing, $this->mExternals );
  471. }
  472. /**
  473. * Given an array of existing categories, returns those categories which are not in $this
  474. * and thus should be deleted.
  475. * @private
  476. */
  477. function getCategoryDeletions( $existing ) {
  478. return array_diff_assoc( $existing, $this->mCategories );
  479. }
  480. /**
  481. * Given an array of existing interlanguage links, returns those links which are not
  482. * in $this and thus should be deleted.
  483. * @private
  484. */
  485. function getInterlangDeletions( $existing ) {
  486. return array_diff_assoc( $existing, $this->mInterlangs );
  487. }
  488. /**
  489. * Get array of properties which should be deleted.
  490. * @private
  491. */
  492. function getPropertyDeletions( $existing ) {
  493. return array_diff_assoc( $existing, $this->mProperties );
  494. }
  495. /**
  496. * Get an array of existing links, as a 2-D array
  497. * @private
  498. */
  499. function getExistingLinks() {
  500. $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
  501. array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
  502. $arr = array();
  503. while ( $row = $this->mDb->fetchObject( $res ) ) {
  504. if ( !isset( $arr[$row->pl_namespace] ) ) {
  505. $arr[$row->pl_namespace] = array();
  506. }
  507. $arr[$row->pl_namespace][$row->pl_title] = 1;
  508. }
  509. $this->mDb->freeResult( $res );
  510. return $arr;
  511. }
  512. /**
  513. * Get an array of existing templates, as a 2-D array
  514. * @private
  515. */
  516. function getExistingTemplates() {
  517. $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
  518. array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
  519. $arr = array();
  520. while ( $row = $this->mDb->fetchObject( $res ) ) {
  521. if ( !isset( $arr[$row->tl_namespace] ) ) {
  522. $arr[$row->tl_namespace] = array();
  523. }
  524. $arr[$row->tl_namespace][$row->tl_title] = 1;
  525. }
  526. $this->mDb->freeResult( $res );
  527. return $arr;
  528. }
  529. /**
  530. * Get an array of existing images, image names in the keys
  531. * @private
  532. */
  533. function getExistingImages() {
  534. $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
  535. array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
  536. $arr = array();
  537. while ( $row = $this->mDb->fetchObject( $res ) ) {
  538. $arr[$row->il_to] = 1;
  539. }
  540. $this->mDb->freeResult( $res );
  541. return $arr;
  542. }
  543. /**
  544. * Get an array of existing external links, URLs in the keys
  545. * @private
  546. */
  547. function getExistingExternals() {
  548. $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
  549. array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
  550. $arr = array();
  551. while ( $row = $this->mDb->fetchObject( $res ) ) {
  552. $arr[$row->el_to] = 1;
  553. }
  554. $this->mDb->freeResult( $res );
  555. return $arr;
  556. }
  557. /**
  558. * Get an array of existing categories, with the name in the key and sort key in the value.
  559. * @private
  560. */
  561. function getExistingCategories() {
  562. $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey' ),
  563. array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
  564. $arr = array();
  565. while ( $row = $this->mDb->fetchObject( $res ) ) {
  566. $arr[$row->cl_to] = $row->cl_sortkey;
  567. }
  568. $this->mDb->freeResult( $res );
  569. return $arr;
  570. }
  571. /**
  572. * Get an array of existing interlanguage links, with the language code in the key and the
  573. * title in the value.
  574. * @private
  575. */
  576. function getExistingInterlangs() {
  577. $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
  578. array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
  579. $arr = array();
  580. while ( $row = $this->mDb->fetchObject( $res ) ) {
  581. $arr[$row->ll_lang] = $row->ll_title;
  582. }
  583. return $arr;
  584. }
  585. /**
  586. * Get an array of existing categories, with the name in the key and sort key in the value.
  587. * @private
  588. */
  589. function getExistingProperties() {
  590. $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
  591. array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
  592. $arr = array();
  593. while ( $row = $this->mDb->fetchObject( $res ) ) {
  594. $arr[$row->pp_propname] = $row->pp_value;
  595. }
  596. $this->mDb->freeResult( $res );
  597. return $arr;
  598. }
  599. /**
  600. * Return the title object of the page being updated
  601. */
  602. function getTitle() {
  603. return $this->mTitle;
  604. }
  605. /**
  606. * Invalidate any necessary link lists related to page property changes
  607. */
  608. function invalidateProperties( $changed ) {
  609. global $wgPagePropLinkInvalidations;
  610. foreach ( $changed as $name => $value ) {
  611. if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
  612. $inv = $wgPagePropLinkInvalidations[$name];
  613. if ( !is_array( $inv ) ) {
  614. $inv = array( $inv );
  615. }
  616. foreach ( $inv as $table ) {
  617. $update = new HTMLCacheUpdate( $this->mTitle, $table );
  618. $update->doUpdate();
  619. }
  620. }
  621. }
  622. }
  623. }