* follow-up r97636: decrease indentation & mention revision per Nikerabbit
[lhc/web/wiklou.git] / includes / GlobalUsageQuery.php
1 <?php
2 /**
3 * A helper class to query the globalimagelinks table
4 *
5 */
6 class GlobalUsageQuery {
7 private $limit = 50;
8 private $offset;
9 private $hasMore = false;
10 private $filterLocal = false;
11 private $result;
12 private $continue;
13 private $reversed = false;
14 private $target = null;
15
16 /**
17 * @param $target mixed Title or db key, or array of db keys of target(s)
18 */
19 public function __construct( $target ) {
20 global $wgGlobalDatabase;
21 $this->db = wfGetDB( DB_SLAVE, array(), $wgGlobalDatabase );
22 if ( $target instanceof Title && $target->getNamespace( ) == NS_FILE ) {
23 $this->target = $target->getDBKey();
24 } else {
25 $this->target = $target;
26 }
27 $this->offset = array();
28 }
29
30 /**
31 * Set the offset parameter
32 *
33 * @param $offset string offset
34 * @param $reversed bool True if this is the upper offset
35 */
36 public function setOffset( $offset, $reversed = null ) {
37 if ( !is_null( $reversed ) ) {
38 $this->reversed = $reversed;
39 }
40
41 if ( !is_array( $offset ) ) {
42 $offset = explode( '|', $offset );
43 }
44
45 if ( count( $offset ) == 3 ) {
46 $this->offset = $offset;
47 return true;
48 } else {
49 return false;
50 }
51 }
52 /**
53 * Return the offset set by the user
54 *
55 * @return array offset
56 */
57 public function getOffsetString() {
58 return implode( '|', $this->offset );
59 }
60 /**
61 * Is the result reversed
62 *
63 * @return bool
64 */
65 public function isReversed() {
66 return $this->reversed;
67 }
68
69 /**
70 * Returns the string used for continuation in a file search
71 *
72 * @return string
73 *
74 */
75 public function getContinueFileString() {
76 if ( $this->hasMore() ) {
77 return "{$this->lastRow->gil_to}|{$this->lastRow->gil_wiki}|{$this->lastRow->gil_page}";
78 } else {
79 return '';
80 }
81 }
82
83 /**
84 * Returns the string used for continuation in a template search
85 *
86 * @return string
87 *
88 */
89 public function getContinueTemplateString() {
90 if ( $this->hasMore() ) {
91 return "{$this->lastRow->gtl_to_title}|{$this->lastRow->gtl_from_wiki}|{$this->lastRow->gtl_from_page}";
92 } else {
93 return '';
94 }
95 }
96
97 /**
98 * Set the maximum amount of items to return. Capped at 500.
99 *
100 * @param $limit int The limit
101 */
102 public function setLimit( $limit ) {
103 $this->limit = min( $limit, 500 );
104 }
105 /**
106 * Returns the user set limit
107 */
108 public function getLimit() {
109 return $this->limit;
110 }
111
112 /**
113 * Set whether to filter out the local usage
114 */
115 public function filterLocal( $value = true ) {
116 $this->filterLocal = $value;
117 }
118
119 /**
120 * Executes the query for a file search
121 */
122 public function searchTemplate() {
123 global $wgLocalInterwiki;
124
125 /* Construct a where clause */
126 // Add target template(s)
127 $where = array( 'gtl_to_prefix' => $wgLocalInterwiki,
128 'gtl_to_namespace' => $this->target->getNamespace( ),
129 'gtl_to_title' => $this->target->getDBkey( )
130 );
131
132 // Set the continuation condition
133 $order = 'ASC';
134 if ( $this->offset ) {
135 $qTo = $this->db->addQuotes( $this->offset[0] );
136 $qWiki = $this->db->addQuotes( $this->offset[1] );
137 $qPage = intval( $this->offset[2] );
138
139 // Check which limit we got in order to determine which way to traverse rows
140 if ( $this->reversed ) {
141 // Reversed traversal; do not include offset row
142 $op1 = '<';
143 $op2 = '<';
144 $order = 'DESC';
145 } else {
146 // Normal traversal; include offset row
147 $op1 = '>';
148 $op2 = '>=';
149 $order = 'ASC';
150 }
151
152 $where[] = "(gtl_to_title $op1 $qTo) OR " .
153 "(gtl_to_title = $qTo AND gtl_from_wiki $op1 $qWiki) OR " .
154 "(gtl_to_title = $qTo AND gtl_from_wiki = $qWiki AND gtl_from_page $op2 $qPage)";
155 }
156
157 /* Perform select (Duh.) */
158 $res = $this->db->select(
159 array(
160 'globaltemplatelinks',
161 'globalnamespaces'
162 ),
163 array(
164 'gtl_to_title',
165 'gtl_from_wiki',
166 'gtl_from_page',
167 'gtl_from_namespace',
168 'gtl_from_title'
169 ),
170 $where,
171 __METHOD__,
172 array(
173 'ORDER BY' => "gtl_to_title $order, gtl_from_wiki $order, gtl_from_page $order",
174 // Select an extra row to check whether we have more rows available
175 'LIMIT' => $this->limit + 1,
176 ),
177 array(
178 'gtl_from_namespace = gn_namespace'
179 )
180 );
181
182 /* Process result */
183 // Always return the result in the same order; regardless whether reversed was specified
184 // reversed is really only used to determine from which direction the offset is
185 $rows = array();
186 foreach ( $res as $row ) {
187 $rows[] = $row;
188 }
189 if ( $this->reversed ) {
190 $rows = array_reverse( $rows );
191 }
192
193 // Build the result array
194 $count = 0;
195 $this->hasMore = false;
196 $this->result = array();
197 foreach ( $rows as $row ) {
198 $count++;
199 if ( $count > $this->limit ) {
200 // We've reached the extra row that indicates that there are more rows
201 $this->hasMore = true;
202 $this->lastRow = $row;
203 break;
204 }
205
206 if ( !isset( $this->result[$row->gtl_to_title] ) ) {
207 $this->result[$row->gtl_to_title] = array();
208 }
209 if ( !isset( $this->result[$row->gtl_to_title][$row->gtl_from_wiki] ) ) {
210 $this->result[$row->gtl_to_title][$row->gtl_from_wiki] = array();
211 }
212
213 $this->result[$row->gtl_to_title][$row->gtl_from_wiki][] = array(
214 'template' => $row->gtl_to_title,
215 'id' => $row->gtl_from_page,
216 'namespace' => $row->gn_namespacetext,
217 'title' => $row->gtl_from_title,
218 'wiki' => $row->gtl_from_wiki,
219 );
220 }
221 }
222
223 /**
224 * Executes the query for a template search
225 */
226 public function searchFile() {
227 /* Construct a where clause */
228 // Add target image(s)
229 $where = array( 'gil_to' => $this->target );
230
231 if ( $this->filterLocal ) {
232 // Don't show local file usage
233 $where[] = 'gil_wiki != ' . $this->db->addQuotes( wfWikiId() );
234 }
235
236 // Set the continuation condition
237 $order = 'ASC';
238 if ( $this->offset ) {
239 $qTo = $this->db->addQuotes( $this->offset[0] );
240 $qWiki = $this->db->addQuotes( $this->offset[1] );
241 $qPage = intval( $this->offset[2] );
242
243 // Check which limit we got in order to determine which way to traverse rows
244 if ( $this->reversed ) {
245 // Reversed traversal; do not include offset row
246 $op1 = '<';
247 $op2 = '<';
248 $order = 'DESC';
249 } else {
250 // Normal traversal; include offset row
251 $op1 = '>';
252 $op2 = '>=';
253 $order = 'ASC';
254 }
255
256 $where[] = "(gil_to $op1 $qTo) OR " .
257 "(gil_to = $qTo AND gil_wiki $op1 $qWiki) OR " .
258 "(gil_to = $qTo AND gil_wiki = $qWiki AND gil_page $op2 $qPage)";
259 }
260
261 /* Perform select (Duh.) */
262 $res = $this->db->select( 'globalimagelinks',
263 array(
264 'gil_to',
265 'gil_wiki',
266 'gil_page',
267 'gil_page_namespace',
268 'gil_page_title'
269 ),
270 $where,
271 __METHOD__,
272 array(
273 'ORDER BY' => "gil_to $order, gil_wiki $order, gil_page $order",
274 // Select an extra row to check whether we have more rows available
275 'LIMIT' => $this->limit + 1,
276 )
277 );
278
279 /* Process result */
280 // Always return the result in the same order; regardless whether reversed was specified
281 // reversed is really only used to determine from which direction the offset is
282 $rows = array();
283 foreach ( $res as $row ) {
284 $rows[] = $row;
285 }
286 if ( $this->reversed ) {
287 $rows = array_reverse( $rows );
288 }
289
290 // Build the result array
291 $count = 0;
292 $this->hasMore = false;
293 $this->result = array();
294 foreach ( $rows as $row ) {
295 $count++;
296 if ( $count > $this->limit ) {
297 // We've reached the extra row that indicates that there are more rows
298 $this->hasMore = true;
299 $this->lastRow = $row;
300 break;
301 }
302
303 if ( !isset( $this->result[$row->gil_to] ) ) {
304 $this->result[$row->gil_to] = array();
305 }
306 if ( !isset( $this->result[$row->gil_to][$row->gil_wiki] ) ) {
307 $this->result[$row->gil_to][$row->gil_wiki] = array();
308 }
309
310 $this->result[$row->gil_to][$row->gil_wiki][] = array(
311 'image' => $row->gil_to,
312 'id' => $row->gil_page,
313 'namespace' => $row->gil_page_namespace,
314 'title' => $row->gil_page_title,
315 'wiki' => $row->gil_wiki,
316 );
317 }
318 }
319
320 /**
321 * Returns the result set. The result is a 4 dimensional array
322 * (file, wiki, page), whose items are arrays with keys:
323 * - image or template: File name or template name
324 * - id: Page id
325 * - namespace: Page namespace text
326 * - title: Unprefixed page title
327 * - wiki: Wiki id
328 *
329 * @return array Result set
330 */
331 public function getResult() {
332 return $this->result;
333 }
334 /**
335 * Returns a 3 dimensional array with the result of the first file. Useful
336 * if only one resource was queried.
337 *
338 * For further information see documentation of getResult()
339 *
340 * @return array Result set
341 */
342 public function getSingleResult() {
343 if ( $this->result ) {
344 return current( $this->result );
345 } else {
346 return array();
347 }
348 }
349
350 /**
351 * Returns whether there are more results
352 *
353 * @return bool
354 */
355 public function hasMore() {
356 return $this->hasMore;
357 }
358
359 /**
360 * Returns the result length
361 *
362 * @return int
363 */
364 public function count() {
365 return count( $this->result );
366 }
367 }