MediaWiki  master
ApiQueryLinks.php
Go to the documentation of this file.
1 <?php
25 
32 
33  private const LINKS = 'links';
34  private const TEMPLATES = 'templates';
35 
37 
40 
42  private $linksMigration;
43 
50  public function __construct(
51  ApiQuery $query,
52  $moduleName,
55  ) {
56  switch ( $moduleName ) {
57  case self::LINKS:
58  $this->table = 'pagelinks';
59  $this->prefix = 'pl';
60  $this->titlesParam = 'titles';
61  $this->helpUrl = 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Links';
62  break;
63  case self::TEMPLATES:
64  $this->table = 'templatelinks';
65  $this->prefix = 'tl';
66  $this->titlesParam = 'templates';
67  $this->helpUrl = 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Templates';
68  break;
69  default:
70  ApiBase::dieDebug( __METHOD__, 'Unknown module name' );
71  }
72 
73  parent::__construct( $query, $moduleName, $this->prefix );
74  $this->linkBatchFactory = $linkBatchFactory;
75  $this->linksMigration = $linksMigration;
76  }
77 
78  public function execute() {
79  $this->run();
80  }
81 
82  public function getCacheMode( $params ) {
83  return 'public';
84  }
85 
86  public function executeGenerator( $resultPageSet ) {
87  $this->run( $resultPageSet );
88  }
89 
93  private function run( $resultPageSet = null ) {
94  $pages = $this->getPageSet()->getGoodPages();
95  if ( $pages === [] ) {
96  return; // nothing to do
97  }
98 
99  $params = $this->extractRequestParams();
100 
101  if ( isset( $this->linksMigration::$mapping[$this->table] ) ) {
102  list( $nsField, $titleField ) = $this->linksMigration->getTitleFields( $this->table );
103  $queryInfo = $this->linksMigration->getQueryInfo( $this->table );
104  $this->addTables( $queryInfo['tables'] );
105  $this->addJoinConds( $queryInfo['joins'] );
106  } else {
107  $this->addTables( $this->table );
108  $nsField = $this->prefix . '_namespace';
109  $titleField = $this->prefix . '_title';
110  }
111 
112  $this->addFields( [
113  'pl_from' => $this->prefix . '_from',
114  'pl_namespace' => $nsField,
115  'pl_title' => $titleField,
116  ] );
117 
118  $this->addWhereFld( $this->prefix . '_from', array_keys( $pages ) );
119 
120  $multiNS = true;
121  $multiTitle = true;
122  if ( $params[$this->titlesParam] ) {
123  // Filter the titles in PHP so our ORDER BY bug avoidance below works right.
124  $filterNS = $params['namespace'] ? array_fill_keys( $params['namespace'], true ) : false;
125 
126  $lb = $this->linkBatchFactory->newLinkBatch();
127  foreach ( $params[$this->titlesParam] as $t ) {
129  if ( !$title ) {
130  $this->addWarning( [ 'apiwarn-invalidtitle', wfEscapeWikiText( $t ) ] );
131  } elseif ( !$filterNS || isset( $filterNS[$title->getNamespace()] ) ) {
132  $lb->addObj( $title );
133  }
134  }
135  $cond = $lb->constructSet( $this->prefix, $this->getDB() );
136  if ( $cond ) {
137  $this->addWhere( $cond );
138  $multiNS = count( $lb->data ) !== 1;
139  $multiTitle = count( array_merge( ...$lb->data ) ) !== 1;
140  } else {
141  // No titles so no results
142  return;
143  }
144  } elseif ( $params['namespace'] ) {
145  $this->addWhereFld( $nsField, $params['namespace'] );
146  $multiNS = $params['namespace'] === null || count( $params['namespace'] ) !== 1;
147  }
148 
149  if ( $params['continue'] !== null ) {
150  $cont = explode( '|', $params['continue'] );
151  $this->dieContinueUsageIf( count( $cont ) != 3 );
152  $op = $params['dir'] == 'descending' ? '<' : '>';
153  $plfrom = (int)$cont[0];
154  $plns = (int)$cont[1];
155  $pltitle = $this->getDB()->addQuotes( $cont[2] );
156  $this->addWhere(
157  "{$this->prefix}_from $op $plfrom OR " .
158  "({$this->prefix}_from = $plfrom AND " .
159  "($nsField $op $plns OR " .
160  "($nsField = $plns AND " .
161  "$titleField $op= $pltitle)))"
162  );
163  }
164 
165  $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
166  // Here's some MySQL craziness going on: if you use WHERE foo='bar'
167  // and later ORDER BY foo MySQL doesn't notice the ORDER BY is pointless
168  // but instead goes and filesorts, because the index for foo was used
169  // already. To work around this, we drop constant fields in the WHERE
170  // clause from the ORDER BY clause
171  $order = [];
172  if ( count( $pages ) !== 1 ) {
173  $order[] = $this->prefix . '_from' . $sort;
174  }
175  if ( $multiNS ) {
176  $order[] = $nsField . $sort;
177  }
178  if ( $multiTitle ) {
179  $order[] = $titleField . $sort;
180  }
181  if ( $order ) {
182  $this->addOption( 'ORDER BY', $order );
183  }
184  $this->addOption( 'LIMIT', $params['limit'] + 1 );
185 
186  $res = $this->select( __METHOD__ );
187 
188  if ( $resultPageSet === null ) {
189  $this->executeGenderCacheFromResultWrapper( $res, __METHOD__, 'pl' );
190 
191  $count = 0;
192  foreach ( $res as $row ) {
193  if ( ++$count > $params['limit'] ) {
194  // We've reached the one extra which shows that
195  // there are additional pages to be had. Stop here...
196  $this->setContinueEnumParameter( 'continue',
197  "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
198  break;
199  }
200  $vals = [];
201  ApiQueryBase::addTitleInfo( $vals, Title::makeTitle( $row->pl_namespace, $row->pl_title ) );
202  $fit = $this->addPageSubItem( $row->pl_from, $vals );
203  if ( !$fit ) {
204  $this->setContinueEnumParameter( 'continue',
205  "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
206  break;
207  }
208  }
209  } else {
210  $titles = [];
211  $count = 0;
212  foreach ( $res as $row ) {
213  if ( ++$count > $params['limit'] ) {
214  // We've reached the one extra which shows that
215  // there are additional pages to be had. Stop here...
216  $this->setContinueEnumParameter( 'continue',
217  "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
218  break;
219  }
220  $titles[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
221  }
222  $resultPageSet->populateFromTitles( $titles );
223  }
224  }
225 
226  public function getAllowedParams() {
227  return [
228  'namespace' => [
229  ApiBase::PARAM_TYPE => 'namespace',
230  ApiBase::PARAM_ISMULTI => true,
232  ],
233  'limit' => [
234  ApiBase::PARAM_DFLT => 10,
235  ApiBase::PARAM_TYPE => 'limit',
236  ApiBase::PARAM_MIN => 1,
239  ],
240  'continue' => [
241  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
242  ],
243  $this->titlesParam => [
244  ApiBase::PARAM_ISMULTI => true,
245  ],
246  'dir' => [
247  ApiBase::PARAM_DFLT => 'ascending',
249  'ascending',
250  'descending'
251  ]
252  ],
253  ];
254  }
255 
256  protected function getExamplesMessages() {
257  $name = $this->getModuleName();
258  $path = $this->getModulePath();
259 
260  return [
261  "action=query&prop={$name}&titles=Main%20Page"
262  => "apihelp-{$path}-example-simple",
263  "action=query&generator={$name}&titles=Main%20Page&prop=info"
264  => "apihelp-{$path}-example-generator",
265  "action=query&prop={$name}&titles=Main%20Page&{$this->prefix}namespace=2|10"
266  => "apihelp-{$path}-example-namespaces",
267  ];
268  }
269 
270  public function getHelpUrls() {
271  return $this->helpUrl;
272  }
273 }
const NS_SPECIAL
Definition: Defines.php:53
const NS_MEDIA
Definition: Defines.php:52
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
const PARAM_MAX2
Definition: ApiBase.php:90
const PARAM_MAX
Definition: ApiBase.php:86
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition: ApiBase.php:1638
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:1651
const PARAM_TYPE
Definition: ApiBase.php:82
const PARAM_DFLT
Definition: ApiBase.php:74
const PARAM_MIN
Definition: ApiBase.php:94
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:221
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:765
getModulePath()
Get the path to this module.
Definition: ApiBase.php:573
const PARAM_EXTRA_NAMESPACES
Definition: ApiBase.php:122
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:163
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1364
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:223
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:498
const PARAM_ISMULTI
Definition: ApiBase.php:78
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
addFields( $value)
Add a set of fields to select to the internal array.
addPageSubItem( $pageId, $item, $elemname=null)
Same as addPageSubItems(), but one element of $data at a time.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
getDB()
Get the Query database connection (read-only)
executeGenderCacheFromResultWrapper(IResultWrapper $res, $fname=__METHOD__, $fieldPrefix='page')
Preprocess the result set to fill the GenderCache with the necessary information before using self::a...
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addWhere( $value)
Add a set of WHERE clauses to the internal array.
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
getPageSet()
Get the PageSet object to work on.
This is the main query class.
Definition: ApiQuery.php:40
Service for compat reading of links tables.
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:369
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:637