MediaWiki  master
ApiQueryLinks.php
Go to the documentation of this file.
1 <?php
29 
30  const LINKS = 'links';
31  const TEMPLATES = 'templates';
32 
34 
35  public function __construct( ApiQuery $query, $moduleName ) {
36  switch ( $moduleName ) {
37  case self::LINKS:
38  $this->table = 'pagelinks';
39  $this->prefix = 'pl';
40  $this->titlesParam = 'titles';
41  $this->helpUrl = 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Links';
42  break;
43  case self::TEMPLATES:
44  $this->table = 'templatelinks';
45  $this->prefix = 'tl';
46  $this->titlesParam = 'templates';
47  $this->helpUrl = 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Templates';
48  break;
49  default:
50  ApiBase::dieDebug( __METHOD__, 'Unknown module name' );
51  }
52 
53  parent::__construct( $query, $moduleName, $this->prefix );
54  }
55 
56  public function execute() {
57  $this->run();
58  }
59 
60  public function getCacheMode( $params ) {
61  return 'public';
62  }
63 
64  public function executeGenerator( $resultPageSet ) {
65  $this->run( $resultPageSet );
66  }
67 
71  private function run( $resultPageSet = null ) {
72  if ( $this->getPageSet()->getGoodTitleCount() == 0 ) {
73  return; // nothing to do
74  }
75 
76  $params = $this->extractRequestParams();
77 
78  $this->addFields( [
79  'pl_from' => $this->prefix . '_from',
80  'pl_namespace' => $this->prefix . '_namespace',
81  'pl_title' => $this->prefix . '_title'
82  ] );
83 
84  $this->addTables( $this->table );
85  $this->addWhereFld( $this->prefix . '_from', array_keys( $this->getPageSet()->getGoodTitles() ) );
86 
87  $multiNS = true;
88  $multiTitle = true;
89  if ( $params[$this->titlesParam] ) {
90  // Filter the titles in PHP so our ORDER BY bug avoidance below works right.
91  $filterNS = $params['namespace'] ? array_flip( $params['namespace'] ) : false;
92 
93  $lb = new LinkBatch;
94  foreach ( $params[$this->titlesParam] as $t ) {
95  $title = Title::newFromText( $t );
96  if ( !$title ) {
97  $this->addWarning( [ 'apiwarn-invalidtitle', wfEscapeWikiText( $t ) ] );
98  } elseif ( !$filterNS || isset( $filterNS[$title->getNamespace()] ) ) {
99  $lb->addObj( $title );
100  }
101  }
102  $cond = $lb->constructSet( $this->prefix, $this->getDB() );
103  if ( $cond ) {
104  $this->addWhere( $cond );
105  $multiNS = count( $lb->data ) !== 1;
106  $multiTitle = count( array_merge( ...$lb->data ) ) !== 1;
107  } else {
108  // No titles so no results
109  return;
110  }
111  } elseif ( $params['namespace'] ) {
112  $this->addWhereFld( $this->prefix . '_namespace', $params['namespace'] );
113  $multiNS = $params['namespace'] === null || count( $params['namespace'] ) !== 1;
114  }
115 
116  if ( !is_null( $params['continue'] ) ) {
117  $cont = explode( '|', $params['continue'] );
118  $this->dieContinueUsageIf( count( $cont ) != 3 );
119  $op = $params['dir'] == 'descending' ? '<' : '>';
120  $plfrom = (int)$cont[0];
121  $plns = (int)$cont[1];
122  $pltitle = $this->getDB()->addQuotes( $cont[2] );
123  $this->addWhere(
124  "{$this->prefix}_from $op $plfrom OR " .
125  "({$this->prefix}_from = $plfrom AND " .
126  "({$this->prefix}_namespace $op $plns OR " .
127  "({$this->prefix}_namespace = $plns AND " .
128  "{$this->prefix}_title $op= $pltitle)))"
129  );
130  }
131 
132  $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
133  // Here's some MySQL craziness going on: if you use WHERE foo='bar'
134  // and later ORDER BY foo MySQL doesn't notice the ORDER BY is pointless
135  // but instead goes and filesorts, because the index for foo was used
136  // already. To work around this, we drop constant fields in the WHERE
137  // clause from the ORDER BY clause
138  $order = [];
139  if ( count( $this->getPageSet()->getGoodTitles() ) != 1 ) {
140  $order[] = $this->prefix . '_from' . $sort;
141  }
142  if ( $multiNS ) {
143  $order[] = $this->prefix . '_namespace' . $sort;
144  }
145  if ( $multiTitle ) {
146  $order[] = $this->prefix . '_title' . $sort;
147  }
148  if ( $order ) {
149  $this->addOption( 'ORDER BY', $order );
150  }
151  $this->addOption( 'LIMIT', $params['limit'] + 1 );
152 
153  $res = $this->select( __METHOD__ );
154 
155  if ( is_null( $resultPageSet ) ) {
156  $count = 0;
157  foreach ( $res as $row ) {
158  if ( ++$count > $params['limit'] ) {
159  // We've reached the one extra which shows that
160  // there are additional pages to be had. Stop here...
161  $this->setContinueEnumParameter( 'continue',
162  "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
163  break;
164  }
165  $vals = [];
166  ApiQueryBase::addTitleInfo( $vals, Title::makeTitle( $row->pl_namespace, $row->pl_title ) );
167  $fit = $this->addPageSubItem( $row->pl_from, $vals );
168  if ( !$fit ) {
169  $this->setContinueEnumParameter( 'continue',
170  "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
171  break;
172  }
173  }
174  } else {
175  $titles = [];
176  $count = 0;
177  foreach ( $res as $row ) {
178  if ( ++$count > $params['limit'] ) {
179  // We've reached the one extra which shows that
180  // there are additional pages to be had. Stop here...
181  $this->setContinueEnumParameter( 'continue',
182  "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
183  break;
184  }
185  $titles[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
186  }
187  $resultPageSet->populateFromTitles( $titles );
188  }
189  }
190 
191  public function getAllowedParams() {
192  return [
193  'namespace' => [
194  ApiBase::PARAM_TYPE => 'namespace',
195  ApiBase::PARAM_ISMULTI => true,
197  ],
198  'limit' => [
199  ApiBase::PARAM_DFLT => 10,
200  ApiBase::PARAM_TYPE => 'limit',
201  ApiBase::PARAM_MIN => 1,
204  ],
205  'continue' => [
206  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
207  ],
208  $this->titlesParam => [
209  ApiBase::PARAM_ISMULTI => true,
210  ],
211  'dir' => [
212  ApiBase::PARAM_DFLT => 'ascending',
214  'ascending',
215  'descending'
216  ]
217  ],
218  ];
219  }
220 
221  protected function getExamplesMessages() {
222  $name = $this->getModuleName();
223  $path = $this->getModulePath();
224 
225  return [
226  "action=query&prop={$name}&titles=Main%20Page"
227  => "apihelp-{$path}-example-simple",
228  "action=query&generator={$name}&titles=Main%20Page&prop=info"
229  => "apihelp-{$path}-example-generator",
230  "action=query&prop={$name}&titles=Main%20Page&{$this->prefix}namespace=2|10"
231  => "apihelp-{$path}-example-namespaces",
232  ];
233  }
234 
235  public function getHelpUrls() {
236  return $this->helpUrl;
237  }
238 }
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below...
Definition: ApiBase.php:94
getDB()
Get the Query database connection (read-only)
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:261
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:55
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:259
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE &#39;integer&#39; and &#39;limit&#39;.
Definition: ApiBase.php:97
$sort
const NS_SPECIAL
Definition: Defines.php:49
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user...
Definition: ApiBase.php:761
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:34
addPageSubItem( $pageId, $item, $elemname=null)
Same as addPageSubItems(), but one element of $data at a time.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
const NS_MEDIA
Definition: Defines.php:48
dieContinueUsageIf( $condition)
Die with the &#39;badcontinue&#39; error.
Definition: ApiBase.php:2199
getModulePath()
Get the path to this module.
Definition: ApiBase.php:584
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:520
addFields( $value)
Add a set of fields to select to the internal array.
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right, for PARAM_TYPE &#39;limit&#39;.
Definition: ApiBase.php:103
This is the main query class.
Definition: ApiQuery.php:37
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter...
Definition: ApiBase.php:131
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:586
addWhere( $value)
Add a set of WHERE clauses to the internal array.
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition: ApiBase.php:1924
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:58
static dieDebug( $method, $message)
Internal code errors should be reported with this method.
Definition: ApiBase.php:2211
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
const PARAM_EXTRA_NAMESPACES
(int[]) When PARAM_TYPE is &#39;namespace&#39;, include these as additional possible values.
Definition: ApiBase.php:193
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE &#39;integer&#39; and &#39;limit&#39;.
Definition: ApiBase.php:106
getPageSet()
Get the PageSet object to work on.
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:319