Merge "maintenance: Script to rename titles for Unicode uppercasing changes"
[lhc/web/wiklou.git] / includes / api / ApiQueryLanguageinfo.php
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21 /**
22 * API module to enumerate language information.
23 *
24 * @ingroup API
25 */
26 class ApiQueryLanguageinfo extends ApiQueryBase {
27
28 /**
29 * The maximum time for {@link execute()};
30 * if execution takes longer than this, apply continuation.
31 *
32 * If the localization cache is used, this time is not expected to ever be
33 * exceeded; on the other hand, if it is not used, a typical request will
34 * not yield more than a handful of languages before the time is exceeded
35 * and continuation is applied, if one of the expensive props is requested.
36 *
37 * @var float
38 */
39 const MAX_EXECUTE_SECONDS = 2.0;
40
41 /** @var callable|null */
42 private $microtimeFunction;
43
44 /**
45 * @param ApiQuery $queryModule
46 * @param string $moduleName
47 * @param callable|null $microtimeFunction Function to use instead of microtime(), for testing.
48 * Should accept no arguments and return float seconds. (null means real microtime().)
49 */
50 public function __construct(
51 ApiQuery $queryModule,
52 $moduleName,
53 $microtimeFunction = null
54 ) {
55 parent::__construct( $queryModule, $moduleName, 'li' );
56 $this->microtimeFunction = $microtimeFunction;
57 }
58
59 /** @return float */
60 private function microtime() {
61 if ( $this->microtimeFunction ) {
62 return ( $this->microtimeFunction )();
63 } else {
64 return microtime( true );
65 }
66 }
67
68 public function execute() {
69 $endTime = $this->microtime() + self::MAX_EXECUTE_SECONDS;
70
71 $props = array_flip( $this->getParameter( 'prop' ) );
72 $includeCode = isset( $props['code'] );
73 $includeBcp47 = isset( $props['bcp47'] );
74 $includeDir = isset( $props['dir'] );
75 $includeAutonym = isset( $props['autonym'] );
76 $includeName = isset( $props['name'] );
77 $includeFallbacks = isset( $props['fallbacks'] );
78 $includeVariants = isset( $props['variants'] );
79
80 $targetLanguageCode = $this->getLanguage()->getCode();
81 $include = 'all';
82
83 $availableLanguageCodes = array_keys( Language::fetchLanguageNames(
84 // MediaWiki and extensions may return different sets of language codes
85 // when asked for language names in different languages;
86 // asking for English language names is most likely to give us the full set,
87 // even though we may not need those at all
88 'en',
89 $include
90 ) );
91 $selectedLanguageCodes = $this->getParameter( 'code' );
92 if ( $selectedLanguageCodes === [ '*' ] ) {
93 $languageCodes = $availableLanguageCodes;
94 } else {
95 $languageCodes = array_values( array_intersect(
96 $availableLanguageCodes,
97 $selectedLanguageCodes
98 ) );
99 $unrecognizedCodes = array_values( array_diff(
100 $selectedLanguageCodes,
101 $availableLanguageCodes
102 ) );
103 if ( $unrecognizedCodes !== [] ) {
104 $this->addWarning( [
105 'apiwarn-unrecognizedvalues',
106 $this->encodeParamName( 'code' ),
107 Message::listParam( $unrecognizedCodes, 'comma' ),
108 count( $unrecognizedCodes ),
109 ] );
110 }
111 }
112 // order of $languageCodes is guaranteed by Language::fetchLanguageNames()
113 // and preserved by array_values() + array_intersect()
114
115 $continue = $this->getParameter( 'continue' );
116 if ( $continue === null ) {
117 $continue = reset( $languageCodes );
118 }
119
120 $result = $this->getResult();
121 $rootPath = [
122 $this->getQuery()->getModuleName(),
123 $this->getModuleName(),
124 ];
125 $result->addArrayType( $rootPath, 'assoc' );
126
127 foreach ( $languageCodes as $languageCode ) {
128 if ( $languageCode < $continue ) {
129 continue;
130 }
131
132 $now = $this->microtime();
133 if ( $now >= $endTime ) {
134 $this->setContinueEnumParameter( 'continue', $languageCode );
135 break;
136 }
137
138 $info = [];
139 ApiResult::setArrayType( $info, 'assoc' );
140
141 if ( $includeCode ) {
142 $info['code'] = $languageCode;
143 }
144
145 if ( $includeBcp47 ) {
146 $bcp47 = LanguageCode::bcp47( $languageCode );
147 $info['bcp47'] = $bcp47;
148 }
149
150 if ( $includeDir ) {
151 $dir = Language::factory( $languageCode )->getDir();
152 $info['dir'] = $dir;
153 }
154
155 if ( $includeAutonym ) {
156 $autonym = Language::fetchLanguageName(
157 $languageCode,
158 Language::AS_AUTONYMS,
159 $include
160 );
161 $info['autonym'] = $autonym;
162 }
163
164 if ( $includeName ) {
165 $name = Language::fetchLanguageName(
166 $languageCode,
167 $targetLanguageCode,
168 $include
169 );
170 $info['name'] = $name;
171 }
172
173 if ( $includeFallbacks ) {
174 $fallbacks = Language::getFallbacksFor(
175 $languageCode,
176 // allow users to distinguish between implicit and explicit 'en' fallbacks
177 Language::STRICT_FALLBACKS
178 );
179 ApiResult::setIndexedTagName( $fallbacks, 'fb' );
180 $info['fallbacks'] = $fallbacks;
181 }
182
183 if ( $includeVariants ) {
184 $variants = Language::factory( $languageCode )->getVariants();
185 ApiResult::setIndexedTagName( $variants, 'var' );
186 $info['variants'] = $variants;
187 }
188
189 $fit = $result->addValue( $rootPath, $languageCode, $info );
190 if ( !$fit ) {
191 $this->setContinueEnumParameter( 'continue', $languageCode );
192 break;
193 }
194 }
195 }
196
197 public function getCacheMode( $params ) {
198 return 'public';
199 }
200
201 public function getAllowedParams() {
202 return [
203 'prop' => [
204 self::PARAM_DFLT => 'code',
205 self::PARAM_ISMULTI => true,
206 self::PARAM_TYPE => [
207 'code',
208 'bcp47',
209 'dir',
210 'autonym',
211 'name',
212 'fallbacks',
213 'variants',
214 ],
215 self::PARAM_HELP_MSG_PER_VALUE => [],
216 ],
217 'code' => [
218 self::PARAM_DFLT => '*',
219 self::PARAM_ISMULTI => true,
220 ],
221 'continue' => [
222 self::PARAM_HELP_MSG => 'api-help-param-continue',
223 ],
224 ];
225 }
226
227 protected function getExamplesMessages() {
228 $pathUrl = 'action=' . $this->getQuery()->getModuleName() .
229 '&meta=' . $this->getModuleName();
230 $pathMsg = $this->getModulePath();
231 $prefix = $this->getModulePrefix();
232
233 return [
234 "$pathUrl"
235 => "apihelp-$pathMsg-example-simple",
236 "$pathUrl&{$prefix}prop=autonym|name&uselang=de"
237 => "apihelp-$pathMsg-example-autonym-name-de",
238 "$pathUrl&{$prefix}prop=fallbacks|variants&{$prefix}code=oc"
239 => "apihelp-$pathMsg-example-fallbacks-variants-oc",
240 "$pathUrl&{$prefix}prop=bcp47|dir"
241 => "apihelp-$pathMsg-example-bcp47-dir",
242 ];
243 }
244
245 }