MediaWiki  master
ApiQueryExtLinksUsage.php
Go to the documentation of this file.
1 <?php
2 
30 
35 
36  private UrlUtils $urlUtils;
37 
43  public function __construct( ApiQuery $query, $moduleName, UrlUtils $urlUtils ) {
44  parent::__construct( $query, $moduleName, 'eu' );
45 
46  $this->urlUtils = $urlUtils;
47  }
48 
49  public function execute() {
50  $this->run();
51  }
52 
53  public function getCacheMode( $params ) {
54  return 'public';
55  }
56 
57  public function executeGenerator( $resultPageSet ) {
58  $this->run( $resultPageSet );
59  }
60 
65  private function run( $resultPageSet = null ) {
66  $params = $this->extractRequestParams();
67  $db = $this->getDB();
68 
69  $query = $params['query'];
70  $protocol = LinkFilter::getProtocolPrefix( $params['protocol'] );
71 
72  $this->addTables( [ 'externallinks', 'page' ] );
73  $this->addJoinConds( [ 'page' => [ 'JOIN', 'page_id=el_from' ] ] );
74  $continueField = 'el_to_domain_index';
75  $fields = [ 'el_to_domain_index', 'el_to_path' ];
76 
77  $miser_ns = [];
78  if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
79  $miser_ns = $params['namespace'] ?: [];
80  } else {
81  $this->addWhereFld( 'page_namespace', $params['namespace'] );
82  }
83  if ( $query !== null && $query !== '' ) {
84  // Normalize query to match the normalization applied for the externallinks table
85  $query = Parser::normalizeLinkUrl( $query );
86  $conds = LinkFilter::getQueryConditions( $query, [
87  'protocol' => $protocol,
88  'oneWildcard' => true,
89  'db' => $db
90  ] );
91  if ( !$conds ) {
92  $this->dieWithError( 'apierror-badquery' );
93  }
94  $this->addWhere( $conds );
95  } else {
96  if ( $protocol !== null ) {
97  $this->addWhere( $continueField . $db->buildLike( "$protocol", $db->anyString() ) );
98  }
99  }
100  $orderBy = [ 'el_id' ];
101 
102  $this->addOption( 'ORDER BY', $orderBy );
103  $this->addFields( $orderBy ); // Make sure
104 
105  $prop = array_fill_keys( $params['prop'], true );
106  $fld_ids = isset( $prop['ids'] );
107  $fld_title = isset( $prop['title'] );
108  $fld_url = isset( $prop['url'] );
109 
110  if ( $resultPageSet === null ) {
111  $this->addFields( [
112  'page_id',
113  'page_namespace',
114  'page_title'
115  ] );
116  foreach ( $fields as $field ) {
117  $this->addFieldsIf( $field, $fld_url );
118  }
119  } else {
120  $this->addFields( $resultPageSet->getPageTableFields() );
121  }
122 
123  $limit = $params['limit'];
124  $this->addOption( 'LIMIT', $limit + 1 );
125 
126  // T244254: Avoid MariaDB deciding to scan all of `page`.
127  $this->addOption( 'STRAIGHT_JOIN' );
128 
129  if ( $params['continue'] !== null ) {
130  $cont = $this->parseContinueParamOrDie( $params['continue'],
131  array_fill( 0, count( $orderBy ), 'string' ) );
132  $conds = array_combine( $orderBy, array_map( 'rawurldecode', $cont ) );
133  $this->addWhere( $db->buildComparison( '>=', $conds ) );
134  }
135 
136  $res = $this->select( __METHOD__ );
137 
138  $result = $this->getResult();
139 
140  if ( $resultPageSet === null ) {
141  $this->executeGenderCacheFromResultWrapper( $res, __METHOD__ );
142  }
143 
144  $count = 0;
145  foreach ( $res as $row ) {
146  if ( ++$count > $limit ) {
147  // We've reached the one extra which shows that there are
148  // additional pages to be had. Stop here...
149  $this->setContinue( $orderBy, $row );
150  break;
151  }
152 
153  if ( count( $miser_ns ) && !in_array( $row->page_namespace, $miser_ns ) ) {
154  continue;
155  }
156 
157  if ( $resultPageSet === null ) {
158  $vals = [
159  ApiResult::META_TYPE => 'assoc',
160  ];
161  if ( $fld_ids ) {
162  $vals['pageid'] = (int)$row->page_id;
163  }
164  if ( $fld_title ) {
165  $title = Title::makeTitle( $row->page_namespace, $row->page_title );
166  ApiQueryBase::addTitleInfo( $vals, $title );
167  }
168  if ( $fld_url ) {
169  $to = LinkFilter::reverseIndexes( $row->el_to_domain_index ) . $row->el_to_path;
170  // expand protocol-relative urls
171  if ( $params['expandurl'] ) {
172  $to = (string)$this->urlUtils->expand( $to, PROTO_CANONICAL );
173  }
174  $vals['url'] = $to;
175  }
176  $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $vals );
177  if ( !$fit ) {
178  $this->setContinue( $orderBy, $row );
179  break;
180  }
181  } else {
182  $resultPageSet->processDbRow( $row );
183  }
184  }
185 
186  if ( $resultPageSet === null ) {
187  $result->addIndexedTagName( [ 'query', $this->getModuleName() ],
188  $this->getModulePrefix() );
189  }
190  }
191 
192  private function setContinue( $orderBy, $row ) {
193  $fields = [];
194  foreach ( $orderBy as $field ) {
195  $fields[] = strtr( $row->$field, [ '%' => '%25', '|' => '%7C' ] );
196  }
197  $this->setContinueEnumParameter( 'continue', implode( '|', $fields ) );
198  }
199 
200  public function getAllowedParams() {
201  $ret = [
202  'prop' => [
203  ParamValidator::PARAM_ISMULTI => true,
204  ParamValidator::PARAM_DEFAULT => 'ids|title|url',
205  ParamValidator::PARAM_TYPE => [
206  'ids',
207  'title',
208  'url'
209  ],
211  ],
212  'continue' => [
213  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
214  ],
215  'protocol' => [
216  ParamValidator::PARAM_TYPE => LinkFilter::prepareProtocols(),
217  ParamValidator::PARAM_DEFAULT => '',
218  ],
219  'query' => null,
220  'namespace' => [
221  ParamValidator::PARAM_ISMULTI => true,
222  ParamValidator::PARAM_TYPE => 'namespace'
223  ],
224  'limit' => [
225  ParamValidator::PARAM_DEFAULT => 10,
226  ParamValidator::PARAM_TYPE => 'limit',
227  IntegerDef::PARAM_MIN => 1,
228  IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
229  IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
230  ],
231  'expandurl' => [
232  ParamValidator::PARAM_TYPE => 'boolean',
233  ParamValidator::PARAM_DEFAULT => false,
234  ParamValidator::PARAM_DEPRECATED => true,
235  ],
236  ];
237 
238  if ( $this->getConfig()->get( MainConfigNames::MiserMode ) ) {
239  $ret['namespace'][ApiBase::PARAM_HELP_MSG_APPEND] = [
240  'api-help-param-limited-in-miser-mode',
241  ];
242  }
243 
244  return $ret;
245  }
246 
247  protected function getExamplesMessages() {
248  return [
249  'action=query&list=exturlusage&euquery=www.mediawiki.org'
250  => 'apihelp-query+exturlusage-example-simple',
251  ];
252  }
253 
254  public function getHelpUrls() {
255  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Exturlusage';
256  }
257 }
const PROTO_CANONICAL
Definition: Defines.php:197
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition: ApiBase.php:1516
getModulePrefix()
Get parameter prefix (usually two letters or an empty string).
Definition: ApiBase.php:538
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition: ApiBase.php:177
parseContinueParamOrDie(string $continue, array $types)
Parse the 'continue' parameter in the usual format and validate the types of each part,...
Definition: ApiBase.php:1718
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, or 'string' with PARAM_ISMULTI,...
Definition: ApiBase.php:210
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:235
getResult()
Get the result object.
Definition: ApiBase.php:668
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:808
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:170
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:237
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:529
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.
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.
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
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.
executeGenerator( $resultPageSet)
Execute this module as a generator.
__construct(ApiQuery $query, $moduleName, UrlUtils $urlUtils)
getExamplesMessages()
Returns usage examples for this module.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getHelpUrls()
Return links to more detailed help pages about the module.
getCacheMode( $params)
Get the cache mode for the data generated by this module.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
setContinueEnumParameter( $paramName, $paramValue)
Overridden to set the generator param if in generator mode.
This is the main query class.
Definition: ApiQuery.php:43
const META_TYPE
Key for the 'type' metadata item.
Definition: ApiResult.php:110
A class containing constants representing the names of configuration variables.
Represents a title within MediaWiki.
Definition: Title.php:76
A service to expand, parse, and otherwise manipulate URLs.
Definition: UrlUtils.php:17
static normalizeLinkUrl( $url)
Replace unusual escape codes in a URL with their equivalent characters.
Definition: Parser.php:2248
Service for formatting and validating API parameters.
Type definition for integer types.
Definition: IntegerDef.php:23