]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/cache/LinkBatch.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / cache / LinkBatch.php
1 <?php
2 /**
3  * Batch query to determine page existence.
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  * @ingroup Cache
22  */
23 use MediaWiki\Linker\LinkTarget;
24 use MediaWiki\MediaWikiServices;
25 use Wikimedia\Rdbms\ResultWrapper;
26 use Wikimedia\Rdbms\IDatabase;
27
28 /**
29  * Class representing a list of titles
30  * The execute() method checks them all for existence and adds them to a LinkCache object
31  *
32  * @ingroup Cache
33  */
34 class LinkBatch {
35         /**
36          * 2-d array, first index namespace, second index dbkey, value arbitrary
37          */
38         public $data = [];
39
40         /**
41          * For debugging which method is using this class.
42          */
43         protected $caller;
44
45         /**
46          * @param Traversable|LinkTarget[] $arr Initial items to be added to the batch
47          */
48         public function __construct( $arr = [] ) {
49                 foreach ( $arr as $item ) {
50                         $this->addObj( $item );
51                 }
52         }
53
54         /**
55          * Use ->setCaller( __METHOD__ ) to indicate which code is using this
56          * class. Only used in debugging output.
57          * @since 1.17
58          *
59          * @param string $caller
60          */
61         public function setCaller( $caller ) {
62                 $this->caller = $caller;
63         }
64
65         /**
66          * @param LinkTarget $linkTarget
67          */
68         public function addObj( $linkTarget ) {
69                 if ( is_object( $linkTarget ) ) {
70                         $this->add( $linkTarget->getNamespace(), $linkTarget->getDBkey() );
71                 } else {
72                         wfDebug( "Warning: LinkBatch::addObj got invalid LinkTarget object\n" );
73                 }
74         }
75
76         /**
77          * @param int $ns
78          * @param string $dbkey
79          */
80         public function add( $ns, $dbkey ) {
81                 if ( $ns < 0 || $dbkey === '' ) {
82                         return; // T137083
83                 }
84                 if ( !array_key_exists( $ns, $this->data ) ) {
85                         $this->data[$ns] = [];
86                 }
87
88                 $this->data[$ns][strtr( $dbkey, ' ', '_' )] = 1;
89         }
90
91         /**
92          * Set the link list to a given 2-d array
93          * First key is the namespace, second is the DB key, value arbitrary
94          *
95          * @param array $array
96          */
97         public function setArray( $array ) {
98                 $this->data = $array;
99         }
100
101         /**
102          * Returns true if no pages have been added, false otherwise.
103          *
104          * @return bool
105          */
106         public function isEmpty() {
107                 return $this->getSize() == 0;
108         }
109
110         /**
111          * Returns the size of the batch.
112          *
113          * @return int
114          */
115         public function getSize() {
116                 return count( $this->data );
117         }
118
119         /**
120          * Do the query and add the results to the LinkCache object
121          *
122          * @return array Mapping PDBK to ID
123          */
124         public function execute() {
125                 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
126
127                 return $this->executeInto( $linkCache );
128         }
129
130         /**
131          * Do the query and add the results to a given LinkCache object
132          * Return an array mapping PDBK to ID
133          *
134          * @param LinkCache &$cache
135          * @return array Remaining IDs
136          */
137         protected function executeInto( &$cache ) {
138                 $res = $this->doQuery();
139                 $this->doGenderQuery();
140                 $ids = $this->addResultToCache( $cache, $res );
141
142                 return $ids;
143         }
144
145         /**
146          * Add a ResultWrapper containing IDs and titles to a LinkCache object.
147          * As normal, titles will go into the static Title cache field.
148          * This function *also* stores extra fields of the title used for link
149          * parsing to avoid extra DB queries.
150          *
151          * @param LinkCache $cache
152          * @param ResultWrapper $res
153          * @return array Array of remaining titles
154          */
155         public function addResultToCache( $cache, $res ) {
156                 if ( !$res ) {
157                         return [];
158                 }
159
160                 $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
161                 // For each returned entry, add it to the list of good links, and remove it from $remaining
162
163                 $ids = [];
164                 $remaining = $this->data;
165                 foreach ( $res as $row ) {
166                         $title = new TitleValue( (int)$row->page_namespace, $row->page_title );
167                         $cache->addGoodLinkObjFromRow( $title, $row );
168                         $pdbk = $titleFormatter->getPrefixedDBkey( $title );
169                         $ids[$pdbk] = $row->page_id;
170                         unset( $remaining[$row->page_namespace][$row->page_title] );
171                 }
172
173                 // The remaining links in $data are bad links, register them as such
174                 foreach ( $remaining as $ns => $dbkeys ) {
175                         foreach ( $dbkeys as $dbkey => $unused ) {
176                                 $title = new TitleValue( (int)$ns, (string)$dbkey );
177                                 $cache->addBadLinkObj( $title );
178                                 $pdbk = $titleFormatter->getPrefixedDBkey( $title );
179                                 $ids[$pdbk] = 0;
180                         }
181                 }
182
183                 return $ids;
184         }
185
186         /**
187          * Perform the existence test query, return a ResultWrapper with page_id fields
188          * @return bool|ResultWrapper
189          */
190         public function doQuery() {
191                 if ( $this->isEmpty() ) {
192                         return false;
193                 }
194
195                 // This is similar to LinkHolderArray::replaceInternal
196                 $dbr = wfGetDB( DB_REPLICA );
197                 $table = 'page';
198                 $fields = array_merge(
199                         LinkCache::getSelectFields(),
200                         [ 'page_namespace', 'page_title' ]
201                 );
202
203                 $conds = $this->constructSet( 'page', $dbr );
204
205                 // Do query
206                 $caller = __METHOD__;
207                 if ( strval( $this->caller ) !== '' ) {
208                         $caller .= " (for {$this->caller})";
209                 }
210                 $res = $dbr->select( $table, $fields, $conds, $caller );
211
212                 return $res;
213         }
214
215         /**
216          * Do (and cache) {{GENDER:...}} information for userpages in this LinkBatch
217          *
218          * @return bool Whether the query was successful
219          */
220         public function doGenderQuery() {
221                 if ( $this->isEmpty() ) {
222                         return false;
223                 }
224
225                 global $wgContLang;
226                 if ( !$wgContLang->needsGenderDistinction() ) {
227                         return false;
228                 }
229
230                 $genderCache = MediaWikiServices::getInstance()->getGenderCache();
231                 $genderCache->doLinkBatch( $this->data, $this->caller );
232
233                 return true;
234         }
235
236         /**
237          * Construct a WHERE clause which will match all the given titles.
238          *
239          * @param string $prefix The appropriate table's field name prefix ('page', 'pl', etc)
240          * @param IDatabase $db DB object to use
241          * @return string|bool String with SQL where clause fragment, or false if no items.
242          */
243         public function constructSet( $prefix, $db ) {
244                 return $db->makeWhereFrom2d( $this->data, "{$prefix}_namespace", "{$prefix}_title" );
245         }
246 }