Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 319
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQueryBacklinks
0.00% covered (danger)
0.00%
0 / 318
0.00% covered (danger)
0.00%
0 / 10
7482
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getCacheMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 executeGenerator
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 runFirstQuery
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
380
 runSecondQuery
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 1
756
 run
0.00% covered (danger)
0.00%
0 / 109
0.00% covered (danger)
0.00%
0 / 1
1056
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
6
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
2
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Api;
10
11use MediaWiki\Deferred\LinksUpdate\ImageLinksTable;
12use MediaWiki\Deferred\LinksUpdate\PageLinksTable;
13use MediaWiki\Deferred\LinksUpdate\TemplateLinksTable;
14use MediaWiki\Linker\LinksMigration;
15use MediaWiki\Title\Title;
16use Wikimedia\ParamValidator\ParamValidator;
17use Wikimedia\ParamValidator\TypeDef\IntegerDef;
18
19/**
20 * This is a three-in-one module to query:
21 *   * backlinks  - links pointing to the given page,
22 *   * embeddedin - what pages transclude the given page within themselves,
23 *   * imageusage - what pages use the given image
24 *
25 * @ingroup API
26 */
27class ApiQueryBacklinks extends ApiQueryGeneratorBase {
28
29    /**
30     * @var Title
31     */
32    private $rootTitle;
33
34    /** @var array */
35    private $params;
36    /** @var array */
37    private $cont;
38    /** @var bool */
39    private $redirect;
40
41    private string $bl_ns;
42    private string $bl_from;
43    private string $bl_from_ns;
44    private string $bl_table;
45    private string $bl_code;
46    private string $bl_title;
47    /** @var string|false */
48    private $virtual_domain;
49    private bool $hasNS;
50
51    /** @var string */
52    private $helpUrl;
53
54    /**
55     * Maps ns and title to pageid
56     *
57     * @var array
58     */
59    private $pageMap = [];
60    /** @var array */
61    private $resultArr;
62
63    /** @var array */
64    private $redirTitles = [];
65    /** @var string|null */
66    private $continueStr = null;
67
68    /** @var string[][] output element name, database column field prefix, database table */
69    private $backlinksSettings = [
70        'backlinks' => [
71            'code' => 'bl',
72            'prefix' => 'pl',
73            'linktbl' => 'pagelinks',
74            'helpurl' => 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Backlinks',
75            'virtualdomain' => PageLinksTable::VIRTUAL_DOMAIN,
76        ],
77        'embeddedin' => [
78            'code' => 'ei',
79            'prefix' => 'tl',
80            'linktbl' => 'templatelinks',
81            'helpurl' => 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Embeddedin',
82            'virtualdomain' => TemplateLinksTable::VIRTUAL_DOMAIN,
83        ],
84        'imageusage' => [
85            'code' => 'iu',
86            'prefix' => 'il',
87            'linktbl' => 'imagelinks',
88            'helpurl' => 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Imageusage',
89            'virtualdomain' => ImageLinksTable::VIRTUAL_DOMAIN,
90        ]
91    ];
92
93    public function __construct(
94        ApiQuery $query,
95        string $moduleName,
96        private readonly LinksMigration $linksMigration,
97    ) {
98        $settings = $this->backlinksSettings[$moduleName];
99        $prefix = $settings['prefix'];
100        $code = $settings['code'];
101        $this->resultArr = [];
102
103        parent::__construct( $query, $moduleName, $code );
104        $this->bl_table = $settings['linktbl'];
105        $this->hasNS = $moduleName !== 'imageusage';
106        [ $this->bl_ns, $this->bl_title ] = $this->linksMigration->getTitleFields( $this->bl_table );
107        $this->bl_from = $prefix . '_from';
108        $this->bl_from_ns = $prefix . '_from_namespace';
109        $this->bl_code = $code;
110        $this->helpUrl = $settings['helpurl'];
111        $this->virtual_domain = $settings['virtualdomain'];
112    }
113
114    public function execute() {
115        $this->run();
116    }
117
118    /** @inheritDoc */
119    public function getCacheMode( $params ) {
120        return 'public';
121    }
122
123    /** @inheritDoc */
124    public function executeGenerator( $resultPageSet ) {
125        $this->run( $resultPageSet );
126    }
127
128    /**
129     * @param ApiPageSet|null $resultPageSet
130     * @return void
131     */
132    private function runFirstQuery( $resultPageSet = null ) {
133        $this->addTables( [ $this->bl_table, 'page' ] );
134        $this->addWhere( "{$this->bl_from}=page_id" );
135        if ( $resultPageSet === null ) {
136            $this->addFields( [ 'page_id', 'page_title', 'page_namespace' ] );
137        } else {
138            $this->addFields( $resultPageSet->getPageTableFields() );
139        }
140        $this->addFields( [ 'page_is_redirect', 'from_ns' => 'page_namespace' ] );
141
142        $this->addWhere( $this->linksMigration->getLinksConditions( $this->bl_table, $this->rootTitle ) );
143        $this->addWhereFld( $this->bl_from_ns, $this->params['namespace'] );
144
145        if ( count( $this->cont ) >= 2 ) {
146            $db = $this->getDB();
147            $op = $this->params['dir'] == 'descending' ? '<=' : '>=';
148            if ( $this->params['namespace'] !== null && count( $this->params['namespace'] ) > 1 ) {
149                $this->addWhere( $db->buildComparison( $op, [
150                    $this->bl_from_ns => $this->cont[0],
151                    $this->bl_from => $this->cont[1],
152                ] ) );
153            } else {
154                $this->addWhere( $db->buildComparison( $op, [ $this->bl_from => $this->cont[1] ] ) );
155            }
156        }
157
158        if ( $this->params['filterredir'] == 'redirects' ) {
159            $this->addWhereFld( 'page_is_redirect', 1 );
160        } elseif ( $this->params['filterredir'] == 'nonredirects' && !$this->redirect ) {
161            // T24245 - Check for !redirect, as filtering nonredirects, when
162            // getting what links to them is contradictory
163            $this->addWhereFld( 'page_is_redirect', 0 );
164        }
165
166        $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
167        $sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
168        $orderBy = [];
169        if ( $this->params['namespace'] !== null && count( $this->params['namespace'] ) > 1 ) {
170            $orderBy[] = $this->bl_from_ns . $sort;
171        }
172        $orderBy[] = $this->bl_from . $sort;
173        $this->addOption( 'ORDER BY', $orderBy );
174        $this->addOption( 'STRAIGHT_JOIN' );
175
176        $this->setVirtualDomain( $this->virtual_domain );
177        $res = $this->select( __METHOD__ );
178        $this->resetVirtualDomain();
179
180        if ( $resultPageSet === null ) {
181            $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
182        }
183
184        $count = 0;
185        foreach ( $res as $row ) {
186            if ( ++$count > $this->params['limit'] ) {
187                // We've reached the one extra which shows that there are
188                // additional pages to be had. Stop here...
189                // Continue string may be overridden at a later step
190                $this->continueStr = "{$row->from_ns}|{$row->page_id}";
191                break;
192            }
193
194            // Fill in continuation fields for later steps
195            if ( count( $this->cont ) < 2 ) {
196                $this->cont[] = $row->from_ns;
197                $this->cont[] = $row->page_id;
198            }
199
200            $this->pageMap[$row->page_namespace][$row->page_title] = $row->page_id;
201            $t = Title::makeTitle( $row->page_namespace, $row->page_title );
202            if ( $row->page_is_redirect ) {
203                $this->redirTitles[] = $t;
204            }
205
206            if ( $resultPageSet === null ) {
207                $a = [ 'pageid' => (int)$row->page_id ];
208                ApiQueryBase::addTitleInfo( $a, $t );
209                if ( $row->page_is_redirect ) {
210                    $a['redirect'] = true;
211                }
212                // Put all the results in an array first
213                $this->resultArr[$a['pageid']] = $a;
214            } else {
215                $resultPageSet->processDbRow( $row );
216            }
217        }
218    }
219
220    /**
221     * @param ApiPageSet|null $resultPageSet
222     * @return void
223     */
224    private function runSecondQuery( $resultPageSet = null ) {
225        $db = $this->getDB();
226        $queryInfo = $this->linksMigration->getQueryInfo( $this->bl_table, $this->bl_table );
227        $this->addTables( [ ...$queryInfo['tables'], 'page' ] );
228        $this->addJoinConds( [ ...$queryInfo['joins'], 'page' => [ 'JOIN', "{$this->bl_from}=page_id" ] ] );
229
230        if ( $resultPageSet === null ) {
231            $this->addFields( [ 'page_id', 'page_title', 'page_namespace', 'page_is_redirect' ] );
232        } else {
233            $this->addFields( $resultPageSet->getPageTableFields() );
234        }
235
236        $this->addFields( [ $this->bl_title, 'from_ns' => 'page_namespace' ] );
237        if ( $this->hasNS ) {
238            $this->addFields( $this->bl_ns );
239        }
240
241        // We can't use LinkBatch here because $this->hasNS may be false
242        $titleWhere = [];
243        $allRedirNs = [];
244        $allRedirDBkey = [];
245        /** @var Title $t */
246        foreach ( $this->redirTitles as $t ) {
247            $redirNs = $t->getNamespace();
248            $redirDBkey = $t->getDBkey();
249            $expr = $db->expr( $this->bl_title, '=', $redirDBkey );
250            if ( $this->hasNS ) {
251                $expr = $expr->and( $this->bl_ns, '=', $redirNs );
252            }
253            $titleWhere[] = $expr;
254            $allRedirNs[$redirNs] = true;
255            $allRedirDBkey[$redirDBkey] = true;
256        }
257        $this->addWhere( $db->orExpr( $titleWhere ) );
258        $this->addWhereFld( 'page_namespace', $this->params['namespace'] );
259
260        if ( count( $this->cont ) >= 6 ) {
261            $op = $this->params['dir'] == 'descending' ? '<=' : '>=';
262
263            $conds = [];
264            if ( $this->hasNS && count( $allRedirNs ) > 1 ) {
265                $conds[ $this->bl_ns ] = $this->cont[2];
266            }
267            if ( count( $allRedirDBkey ) > 1 ) {
268                $conds[ $this->bl_title ] = $this->cont[3];
269            }
270            // Don't bother with namespace, title, or from_namespace if it's
271            // otherwise constant in the where clause.
272            if ( $this->params['namespace'] !== null && count( $this->params['namespace'] ) > 1 ) {
273                $conds[ $this->bl_from_ns ] = $this->cont[4];
274            }
275            $conds[ $this->bl_from ] = $this->cont[5];
276
277            $this->addWhere( $db->buildComparison( $op, $conds ) );
278        }
279        if ( $this->params['filterredir'] == 'redirects' ) {
280            $this->addWhereFld( 'page_is_redirect', 1 );
281        } elseif ( $this->params['filterredir'] == 'nonredirects' ) {
282            $this->addWhereFld( 'page_is_redirect', 0 );
283        }
284
285        $this->addOption( 'LIMIT', $this->params['limit'] + 1 );
286        $orderBy = [];
287        $sort = ( $this->params['dir'] == 'descending' ? ' DESC' : '' );
288        // Don't order by namespace/title/from_namespace if it's constant in the WHERE clause
289        if ( $this->hasNS && count( $allRedirNs ) > 1 ) {
290            $orderBy[] = $this->bl_ns . $sort;
291        }
292        if ( count( $allRedirDBkey ) > 1 ) {
293            $orderBy[] = $this->bl_title . $sort;
294        }
295        if ( $this->params['namespace'] !== null && count( $this->params['namespace'] ) > 1 ) {
296            $orderBy[] = $this->bl_from_ns . $sort;
297        }
298        $orderBy[] = $this->bl_from . $sort;
299        $this->addOption( 'ORDER BY', $orderBy );
300        $this->addOption( 'USE INDEX', [ 'page' => 'PRIMARY' ] );
301        // T290379: Avoid MariaDB deciding to scan all of `page`.
302        $this->addOption( 'STRAIGHT_JOIN' );
303
304        $res = $this->select( __METHOD__ );
305
306        if ( $resultPageSet === null ) {
307            $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
308        }
309
310        $count = 0;
311        foreach ( $res as $row ) {
312            $ns = $this->hasNS ? $row->{$this->bl_ns} : NS_FILE;
313
314            if ( ++$count > $this->params['limit'] ) {
315                // We've reached the one extra which shows that there are
316                // additional pages to be had. Stop here...
317                // Note we must keep the parameters for the first query constant
318                // This may be overridden at a later step
319                $title = $row->{$this->bl_title};
320                $this->continueStr = implode( '|', array_slice( $this->cont, 0, 2 ) ) .
321                    "|$ns|$title|{$row->from_ns}|{$row->page_id}";
322                break;
323            }
324
325            // Fill in continuation fields for later steps
326            if ( count( $this->cont ) < 6 ) {
327                $this->cont[] = $ns;
328                $this->cont[] = $row->{$this->bl_title};
329                $this->cont[] = $row->from_ns;
330                $this->cont[] = $row->page_id;
331            }
332
333            if ( $resultPageSet === null ) {
334                $a = [ 'pageid' => (int)$row->page_id ];
335                ApiQueryBase::addTitleInfo( $a, Title::makeTitle( $row->page_namespace, $row->page_title ) );
336                if ( $row->page_is_redirect ) {
337                    $a['redirect'] = true;
338                }
339                $parentID = $this->pageMap[$ns][$row->{$this->bl_title}];
340                // Put all the results in an array first
341                $this->resultArr[$parentID]['redirlinks'][$row->page_id] = $a;
342            } else {
343                $resultPageSet->processDbRow( $row );
344            }
345        }
346    }
347
348    /**
349     * @param ApiPageSet|null $resultPageSet
350     * @return void
351     */
352    private function run( $resultPageSet = null ) {
353        $this->params = $this->extractRequestParams( false );
354        $this->redirect = isset( $this->params['redirect'] ) && $this->params['redirect'];
355        $userMax = ( $this->redirect ? ApiBase::LIMIT_BIG1 / 2 : ApiBase::LIMIT_BIG1 );
356        $botMax = ( $this->redirect ? ApiBase::LIMIT_BIG2 / 2 : ApiBase::LIMIT_BIG2 );
357
358        $result = $this->getResult();
359
360        if ( $this->params['limit'] == 'max' ) {
361            $this->params['limit'] = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
362            $result->addParsedLimit( $this->getModuleName(), $this->params['limit'] );
363        } else {
364            $this->params['limit'] = $this->getMain()->getParamValidator()->validateValue(
365                $this, 'limit', (int)$this->params['limit'], [
366                    ParamValidator::PARAM_TYPE => 'limit',
367                    IntegerDef::PARAM_MIN => 1,
368                    IntegerDef::PARAM_MAX => $userMax,
369                    IntegerDef::PARAM_MAX2 => $botMax,
370                    IntegerDef::PARAM_IGNORE_RANGE => true,
371                ]
372            );
373        }
374
375        $this->rootTitle = $this->getTitleFromTitleOrPageId( $this->params );
376
377        // only image titles are allowed for the root in imageinfo mode
378        if ( !$this->hasNS && $this->rootTitle->getNamespace() !== NS_FILE ) {
379            $this->dieWithError(
380                [ 'apierror-imageusage-badtitle', $this->getModuleName() ],
381                'bad_image_title'
382            );
383        }
384
385        // Parse and validate continuation parameter
386        // (Can't use parseContinueParamOrDie(), because the length is variable)
387        $this->cont = [];
388        if ( $this->params['continue'] !== null ) {
389            $cont = explode( '|', $this->params['continue'] );
390
391            switch ( count( $cont ) ) {
392                case 8:
393                    // redirect page ID for result adding
394                    $this->cont[7] = (int)$cont[7];
395                    $this->dieContinueUsageIf( $cont[7] !== (string)$this->cont[7] );
396
397                    /* Fall through */
398
399                case 7:
400                    // top-level page ID for result adding
401                    $this->cont[6] = (int)$cont[6];
402                    $this->dieContinueUsageIf( $cont[6] !== (string)$this->cont[6] );
403
404                    /* Fall through */
405
406                case 6:
407                    // ns for 2nd query (even for imageusage)
408                    $this->cont[2] = (int)$cont[2];
409                    $this->dieContinueUsageIf( $cont[2] !== (string)$this->cont[2] );
410
411                    // title for 2nd query
412                    $this->cont[3] = $cont[3];
413
414                    // from_ns for 2nd query
415                    $this->cont[4] = (int)$cont[4];
416                    $this->dieContinueUsageIf( $cont[4] !== (string)$this->cont[4] );
417
418                    // from_id for 1st query
419                    $this->cont[5] = (int)$cont[5];
420                    $this->dieContinueUsageIf( $cont[5] !== (string)$this->cont[5] );
421
422                    /* Fall through */
423
424                case 2:
425                    // from_ns for 1st query
426                    $this->cont[0] = (int)$cont[0];
427                    $this->dieContinueUsageIf( $cont[0] !== (string)$this->cont[0] );
428
429                    // from_id for 1st query
430                    $this->cont[1] = (int)$cont[1];
431                    $this->dieContinueUsageIf( $cont[1] !== (string)$this->cont[1] );
432
433                    break;
434
435                default:
436                    // @phan-suppress-next-line PhanImpossibleCondition
437                    $this->dieContinueUsageIf( true );
438            }
439
440            ksort( $this->cont );
441        }
442
443        $this->runFirstQuery( $resultPageSet );
444        if ( $this->redirect && count( $this->redirTitles ) ) {
445            $this->resetQueryParams();
446            $this->runSecondQuery( $resultPageSet );
447        }
448
449        // Fill in any missing fields in case it's needed below
450        $this->cont += [ 0, 0, 0, '', 0, 0, 0 ];
451
452        if ( $resultPageSet === null ) {
453            // Try to add the result data in one go and pray that it fits
454            $code = $this->bl_code;
455            $data = array_map( static function ( $arr ) use ( $code ) {
456                if ( isset( $arr['redirlinks'] ) ) {
457                    $arr['redirlinks'] = array_values( $arr['redirlinks'] );
458                    ApiResult::setIndexedTagName( $arr['redirlinks'], $code );
459                }
460                return $arr;
461            }, array_values( $this->resultArr ) );
462            $fit = $result->addValue( 'query', $this->getModuleName(), $data );
463            if ( !$fit ) {
464                // It didn't fit. Add elements one by one until the
465                // result is full.
466                ksort( $this->resultArr );
467                // @phan-suppress-next-line PhanRedundantValueComparison
468                if ( count( $this->cont ) >= 7 ) {
469                    $startAt = $this->cont[6];
470                } else {
471                    $startAt = array_key_first( $this->resultArr );
472                }
473                $idx = 0;
474                foreach ( $this->resultArr as $pageID => $arr ) {
475                    if ( $pageID < $startAt ) {
476                        continue;
477                    }
478
479                    // Add the basic entry without redirlinks first
480                    $fit = $result->addValue(
481                        [ 'query', $this->getModuleName() ],
482                        $idx, array_diff_key( $arr, [ 'redirlinks' => '' ] ) );
483                    if ( !$fit ) {
484                        $this->continueStr = implode( '|', array_slice( $this->cont, 0, 6 ) ) .
485                            "|$pageID";
486                        break;
487                    }
488
489                    $hasRedirs = false;
490                    $redirLinks = isset( $arr['redirlinks'] ) ? (array)$arr['redirlinks'] : [];
491                    ksort( $redirLinks );
492                    // @phan-suppress-next-line PhanRedundantValueComparisonInLoop
493                    if ( count( $this->cont ) >= 8 && $pageID == $startAt ) {
494                        $redirStartAt = $this->cont[7];
495                    } else {
496                        $redirStartAt = array_key_first( $redirLinks );
497                    }
498                    foreach ( $redirLinks as $key => $redir ) {
499                        if ( $key < $redirStartAt ) {
500                            continue;
501                        }
502
503                        $fit = $result->addValue(
504                            [ 'query', $this->getModuleName(), $idx, 'redirlinks' ],
505                            null, $redir );
506                        if ( !$fit ) {
507                            $this->continueStr = implode( '|', array_slice( $this->cont, 0, 6 ) ) .
508                                "|$pageID|$key";
509                            break;
510                        }
511                        $hasRedirs = true;
512                    }
513                    if ( $hasRedirs ) {
514                        $result->addIndexedTagName(
515                            [ 'query', $this->getModuleName(), $idx, 'redirlinks' ],
516                            $this->bl_code );
517                    }
518                    if ( !$fit ) {
519                        break;
520                    }
521
522                    $idx++;
523                }
524            }
525
526            $result->addIndexedTagName(
527                [ 'query', $this->getModuleName() ],
528                $this->bl_code
529            );
530        }
531        if ( $this->continueStr !== null ) {
532            $this->setContinueEnumParameter( 'continue', $this->continueStr );
533        }
534    }
535
536    /** @inheritDoc */
537    public function getAllowedParams() {
538        $retval = [
539            'title' => [
540                ParamValidator::PARAM_TYPE => 'string',
541            ],
542            'pageid' => [
543                ParamValidator::PARAM_TYPE => 'integer',
544            ],
545            'continue' => [
546                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
547            ],
548            'namespace' => [
549                ParamValidator::PARAM_ISMULTI => true,
550                ParamValidator::PARAM_TYPE => 'namespace'
551            ],
552            'dir' => [
553                ParamValidator::PARAM_DEFAULT => 'ascending',
554                ParamValidator::PARAM_TYPE => [
555                    'ascending',
556                    'descending'
557                ]
558            ],
559            'filterredir' => [
560                ParamValidator::PARAM_DEFAULT => 'all',
561                ParamValidator::PARAM_TYPE => [
562                    'all',
563                    'redirects',
564                    'nonredirects'
565                ]
566            ],
567            'limit' => [
568                ParamValidator::PARAM_DEFAULT => 10,
569                ParamValidator::PARAM_TYPE => 'limit',
570                IntegerDef::PARAM_MIN => 1,
571                IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
572                IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
573            ]
574        ];
575        if ( $this->getModuleName() !== 'embeddedin' ) {
576            $retval['redirect'] = false;
577        }
578
579        return $retval;
580    }
581
582    /** @inheritDoc */
583    protected function getExamplesMessages() {
584        $title = Title::newMainPage()->getPrefixedText();
585        $mp = rawurlencode( $title );
586        $examples = [
587            'backlinks' => [
588                "action=query&list=backlinks&bltitle={$mp}"
589                    => 'apihelp-query+backlinks-example-simple',
590                "action=query&generator=backlinks&gbltitle={$mp}&prop=info"
591                    => 'apihelp-query+backlinks-example-generator',
592            ],
593            'embeddedin' => [
594                'action=query&list=embeddedin&eititle=Template:Stub'
595                    => 'apihelp-query+embeddedin-example-simple',
596                'action=query&generator=embeddedin&geititle=Template:Stub&prop=info'
597                    => 'apihelp-query+embeddedin-example-generator',
598            ],
599            'imageusage' => [
600                'action=query&list=imageusage&iutitle=File:Albert%20Einstein%20Head.jpg'
601                    => 'apihelp-query+imageusage-example-simple',
602                'action=query&generator=imageusage&giutitle=File:Albert%20Einstein%20Head.jpg&prop=info'
603                    => 'apihelp-query+imageusage-example-generator',
604            ]
605        ];
606
607        return $examples[$this->getModuleName()];
608    }
609
610    /** @inheritDoc */
611    public function getHelpUrls() {
612        return $this->helpUrl;
613    }
614}
615
616/** @deprecated class alias since 1.43 */
617class_alias( ApiQueryBacklinks::class, 'ApiQueryBacklinks' );