MediaWiki REL1_34
TitleLibrary.php
Go to the documentation of this file.
1<?php
2
4 // Note these caches are naturally limited to
5 // $wgExpensiveParserFunctionLimit + 1 actual Title objects because any
6 // addition besides the one for the current page calls
7 // incrementExpensiveFunctionCount()
8 private $titleCache = [];
9 private $idCache = [ 0 => null ];
10
11 public function register() {
12 $lib = [
13 'newTitle' => [ $this, 'newTitle' ],
14 'makeTitle' => [ $this, 'makeTitle' ],
15 'getExpensiveData' => [ $this, 'getExpensiveData' ],
16 'getUrl' => [ $this, 'getUrl' ],
17 'getContent' => [ $this, 'getContent' ],
18 'getFileInfo' => [ $this, 'getFileInfo' ],
19 'protectionLevels' => [ $this, 'protectionLevels' ],
20 'cascadingProtection' => [ $this, 'cascadingProtection' ],
21 'redirectTarget' => [ $this, 'redirectTarget' ],
22 ];
23 return $this->getEngine()->registerInterface( 'mw.title.lua', $lib, [
24 'thisTitle' => $this->getInexpensiveTitleData( $this->getTitle() ),
25 'NS_MEDIA' => NS_MEDIA,
26 ] );
27 }
28
29 private function checkNamespace( $name, $argIdx, &$arg, $default = null ) {
30 global $wgContLang;
31
32 if ( $arg === null && $default !== null ) {
33 $arg = $default;
34 } elseif ( is_numeric( $arg ) ) {
35 $arg = (int)$arg;
36 if ( !MWNamespace::exists( $arg ) ) {
37 throw new Scribunto_LuaError(
38 "bad argument #$argIdx to '$name' (unrecognized namespace number '$arg')"
39 );
40 }
41 } elseif ( is_string( $arg ) ) {
42 $ns = $wgContLang->getNsIndex( $arg );
43 if ( $ns === false ) {
44 throw new Scribunto_LuaError(
45 "bad argument #$argIdx to '$name' (unrecognized namespace name '$arg')"
46 );
47 }
48 $arg = $ns;
49 } else {
50 $this->checkType( $name, $argIdx, $arg, 'namespace number or name' );
51 }
52 }
53
60 private function getInexpensiveTitleData( Title $title ) {
61 $ns = $title->getNamespace();
62 $ret = [
63 'isLocal' => (bool)$title->isLocal(),
64 'interwiki' => $title->getInterwiki(),
65 'namespace' => $ns,
66 'nsText' => $title->getNsText(),
67 'text' => $title->getText(),
68 'fragment' => $title->getFragment(),
69 'thePartialUrl' => $title->getPartialURL(),
70 ];
71 if ( $ns === NS_SPECIAL ) {
72 // Core doesn't currently record special page links, but it may in the future.
73 if ( $this->getParser() && !$title->equals( $this->getTitle() ) ) {
74 $this->getParser()->getOutput()->addLink( $title );
75 }
76 $ret['exists'] = (bool)SpecialPageFactory::exists( $title->getDBkey() );
77 }
78 if ( $ns !== NS_FILE && $ns !== NS_MEDIA ) {
79 $ret['file'] = false;
80 }
81 return $ret;
82 }
83
95 public function getExpensiveData( $text ) {
96 $this->checkType( 'getExpensiveData', 1, $text, 'string' );
97 $title = Title::newFromText( $text );
98 if ( !$title ) {
99 return [ null ];
100 }
101 $dbKey = $title->getPrefixedDBkey();
102 if ( isset( $this->titleCache[$dbKey] ) ) {
103 // It was already cached, so we already did the expensive work and added a link
104 $title = $this->titleCache[$dbKey];
105 } else {
106 if ( !$title->equals( $this->getTitle() ) ) {
108
109 // Record a link
110 if ( $this->getParser() ) {
111 $this->getParser()->getOutput()->addLink( $title );
112 }
113 }
114
115 // Cache it
116 $this->titleCache[$dbKey] = $title;
117 if ( $title->getArticleID() > 0 ) {
118 $this->idCache[$title->getArticleID()] = $title;
119 }
120 }
121
122 $ret = [
123 'isRedirect' => (bool)$title->isRedirect(),
124 'id' => $title->getArticleID(),
125 'contentModel' => $title->getContentModel(),
126 ];
127 if ( $title->getNamespace() === NS_SPECIAL ) {
128 $ret['exists'] = (bool)SpecialPageFactory::exists( $title->getDBkey() );
129 } else {
130 // bug 70495: don't just check whether the ID != 0
131 $ret['exists'] = $title->exists();
132 }
133 return [ $ret ];
134 }
135
148 public function newTitle( $text_or_id, $defaultNamespace = null ) {
149 $type = $this->getLuaType( $text_or_id );
150 if ( $type === 'number' ) {
151 if ( array_key_exists( $text_or_id, $this->idCache ) ) {
152 $title = $this->idCache[$text_or_id];
153 } else {
155 $title = Title::newFromID( $text_or_id );
156 $this->idCache[$text_or_id] = $title;
157
158 // Record a link
159 if ( $this->getParser() && $title && !$title->equals( $this->getTitle() ) ) {
160 $this->getParser()->getOutput()->addLink( $title );
161 }
162 }
163 if ( $title ) {
164 $this->titleCache[$title->getPrefixedDBkey()] = $title;
165 } else {
166 return [ null ];
167 }
168 } elseif ( $type === 'string' ) {
169 $this->checkNamespace( 'title.new', 2, $defaultNamespace, NS_MAIN );
170
171 // Note this just fills in the given fields, it doesn't fetch from
172 // the page table.
173 $title = Title::newFromText( $text_or_id, $defaultNamespace );
174 if ( !$title ) {
175 return [ null ];
176 }
177 } else {
178 // This will always fail
179 $this->checkType( 'title.new', 1, $text_or_id, 'number or string' );
180 }
181
182 return [ $this->getInexpensiveTitleData( $title ) ];
183 }
184
197 public function makeTitle( $ns, $text, $fragment = null, $interwiki = null ) {
198 $this->checkNamespace( 'makeTitle', 1, $ns );
199 $this->checkType( 'makeTitle', 2, $text, 'string' );
200 $this->checkTypeOptional( 'makeTitle', 3, $fragment, 'string', '' );
201 $this->checkTypeOptional( 'makeTitle', 4, $interwiki, 'string', '' );
202
203 // Note this just fills in the given fields, it doesn't fetch from the
204 // page table.
205 $title = Title::makeTitleSafe( $ns, $text, $fragment, $interwiki );
206 if ( !$title ) {
207 return [ null ];
208 }
209
210 return [ $this->getInexpensiveTitleData( $title ) ];
211 }
212
222 public function getUrl( $text, $which, $query = null, $proto = null ) {
223 static $protoMap = [
224 'http' => PROTO_HTTP,
225 'https' => PROTO_HTTPS,
226 'relative' => PROTO_RELATIVE,
227 'canonical' => PROTO_CANONICAL,
228 ];
229
230 $this->checkType( 'getUrl', 1, $text, 'string' );
231 $this->checkType( 'getUrl', 2, $which, 'string' );
232 if ( !in_array( $which, [ 'fullUrl', 'localUrl', 'canonicalUrl' ], true ) ) {
233 $this->checkType( 'getUrl', 2, $which, "'fullUrl', 'localUrl', or 'canonicalUrl'" );
234 }
235
236 // May call the following Title methods:
237 // getFullUrl, getLocalUrl, getCanonicalUrl
238 $func = "get" . ucfirst( $which );
239
240 $args = [ $query, false ];
241 if ( !is_string( $query ) && !is_array( $query ) ) {
242 $this->checkTypeOptional( $which, 1, $query, 'table or string', '' );
243 }
244 if ( $which === 'fullUrl' ) {
245 $this->checkTypeOptional( $which, 2, $proto, 'string', 'relative' );
246 if ( !isset( $protoMap[$proto] ) ) {
247 $this->checkType( $which, 2, $proto, "'http', 'https', 'relative', or 'canonical'" );
248 }
249 $args[] = $protoMap[$proto];
250 }
251
252 $title = Title::newFromText( $text );
253 if ( !$title ) {
254 return [ null ];
255 }
256 return [ $title->$func( ...$args ) ];
257 }
258
267 private function getContentInternal( $text ) {
268 $title = Title::newFromText( $text );
269 if ( !$title ) {
270 return null;
271 }
272
273 // Record in templatelinks, so edits cause the page to be refreshed
274 $this->getParser()->getOutput()->addTemplate(
275 $title, $title->getArticleID(), $title->getLatestRevID()
276 );
277
278 $rev = $this->getParser()->fetchCurrentRevisionOfTitle( $title );
279
280 if ( $title->equals( $this->getTitle() ) ) {
281 $parserOutput = $this->getParser()->getOutput();
282 $parserOutput->setFlag( 'vary-revision-sha1' );
283 $parserOutput->setRevisionUsedSha1Base36( $rev ? $rev->getSha1() : '' );
284 wfDebug( __METHOD__ . ": set vary-revision-sha1 for '$title'" );
285 }
286
287 return $rev ? $rev->getContent() : null;
288 }
289
296 public function getContent( $text ) {
297 $this->checkType( 'getContent', 1, $text, 'string' );
298 $content = $this->getContentInternal( $text );
299 return [ $content ? $content->serialize() : null ];
300 }
301
308 public function getFileInfo( $text ) {
309 $this->checkType( 'getFileInfo', 1, $text, 'string' );
310 $title = Title::newFromText( $text );
311 if ( !$title ) {
312 return [ false ];
313 }
314 $ns = $title->getNamespace();
315 if ( $ns !== NS_FILE && $ns !== NS_MEDIA ) {
316 return [ false ];
317 }
318
321 if ( !$file ) {
322 return [ [ 'exists' => false ] ];
323 }
324 $this->getParser()->getOutput()->addImage(
325 $file->getName(), $file->getTimestamp(), $file->getSha1()
326 );
327 if ( !$file->exists() ) {
328 return [ [ 'exists' => false ] ];
329 }
330 $pageCount = $file->pageCount();
331 if ( $pageCount === false ) {
332 $pages = null;
333 } else {
334 $pages = [];
335 for ( $i = 1; $i <= $pageCount; ++$i ) {
336 $pages[$i] = [
337 'width' => $file->getWidth( $i ),
338 'height' => $file->getHeight( $i )
339 ];
340 }
341 }
342 return [ [
343 'exists' => true,
344 'width' => $file->getWidth(),
345 'height' => $file->getHeight(),
346 'mimeType' => $file->getMimeType(),
347 'size' => $file->getSize(),
348 'pages' => $pages
349 ] ];
350 }
351
352 private static function makeArrayOneBased( $arr ) {
353 if ( empty( $arr ) ) {
354 return $arr;
355 }
356 return array_combine( range( 1, count( $arr ) ), array_values( $arr ) );
357 }
358
365 public function protectionLevels( $text ) {
366 $this->checkType( 'protectionLevels', 1, $text, 'string' );
367 $title = Title::newFromText( $text );
368 if ( !$title ) {
369 return [ null ];
370 }
371
372 if ( !$title->areRestrictionsLoaded() ) {
374 }
375 return [ array_map(
376 'Scribunto_LuaTitleLibrary::makeArrayOneBased', $title->getAllRestrictions()
377 ) ];
378 }
379
386 public function cascadingProtection( $text ) {
387 $this->checkType( 'cascadingProtection', 1, $text, 'string' );
388 $title = Title::newFromText( $text );
389 if ( !$title ) {
390 return [ null ];
391 }
392
393 if ( !$title->areCascadeProtectionSourcesLoaded() ) {
395 }
396 list( $sources, $restrictions ) = $title->getCascadeProtectionSources();
397 return [ [
398 'sources' => self::makeArrayOneBased( array_map(
399 function ( $t ) {
400 return $t->getPrefixedText();
401 },
402 $sources ) ),
403 'restrictions' => array_map( 'Scribunto_LuaTitleLibrary::makeArrayOneBased', $restrictions )
404 ] ];
405 }
406
413 public function redirectTarget( $text ) {
414 $this->checkType( 'redirectTarget', 1, $text, 'string' );
415 $content = $this->getContentInternal( $text );
416 $redirTitle = $content ? $content->getRedirectTarget() : null;
417 return [ $redirTitle ? $this->getInexpensiveTitleData( $redirTitle ) : null ];
418 }
419}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfFindFile( $title, $options=[])
Find a file.
$wgContLang
Definition Setup.php:800
if( $line===false) $args
Definition cdb.php:64
static exists( $index)
Returns whether the specified namespace exists.
This class provides some basic services that Lua libraries will probably need.
getLuaType( $var)
Get the Lua type corresponding to the type of the variable.
checkType( $name, $argIdx, $arg, $expectType)
Check the type of a variable.
getEngine()
Get the engine.
checkTypeOptional( $name, $argIdx, &$arg, $expectType, $default)
Check the type of a variable, with default if null.
getParser()
Get the parser.
incrementExpensiveFunctionCount()
Increment the expensive function count, and throw if limit exceeded.
getTitle()
Get the title.
newTitle( $text_or_id, $defaultNamespace=null)
Handler for title.new.
getContentInternal( $text)
Utility to get a Content object from a title.
static makeArrayOneBased( $arr)
getUrl( $text, $which, $query=null, $proto=null)
Get a URL referring to this title.
getInexpensiveTitleData(Title $title)
Extract inexpensive information from a Title object for return to Lua.
checkNamespace( $name, $argIdx, &$arg, $default=null)
getExpensiveData( $text)
Extract expensive information from a Title object for return to Lua.
makeTitle( $ns, $text, $fragment=null, $interwiki=null)
Handler for title.makeTitle.
getContent( $text)
Handler for getContent.
protectionLevels( $text)
Handler for protectionLevels.
getFileInfo( $text)
Handler for getFileInfo.
cascadingProtection( $text)
Handler for cascadingProtection.
redirectTarget( $text)
Handler for redirectTarget.
Represents a title within MediaWiki.
Definition Title.php:42
equals(LinkTarget $title)
Compare with another title.
Definition Title.php:4113
const PROTO_CANONICAL
Definition Defines.php:212
const PROTO_HTTPS
Definition Defines.php:209
const NS_FILE
Definition Defines.php:75
const NS_MAIN
Definition Defines.php:69
const NS_SPECIAL
Definition Defines.php:58
const PROTO_HTTP
Definition Defines.php:208
const NS_MEDIA
Definition Defines.php:57
const PROTO_RELATIVE
Definition Defines.php:210
$content
Definition router.php:78
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42