MediaWiki  1.34.0
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 
320  $file = wfFindFile( $title );
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 }
Scribunto_LuaTitleLibrary
Definition: TitleLibrary.php:3
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:316
PROTO_CANONICAL
const PROTO_CANONICAL
Definition: Defines.php:203
Scribunto_LuaError
Definition: LuaCommon.php:992
Scribunto_LuaTitleLibrary\getContent
getContent( $text)
Handler for getContent.
Definition: TitleLibrary.php:296
Scribunto_LuaTitleLibrary\$idCache
$idCache
Definition: TitleLibrary.php:9
Scribunto_LuaLibraryBase\getTitle
getTitle()
Get the title.
Definition: LibraryBase.php:83
NS_FILE
const NS_FILE
Definition: Defines.php:66
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
Scribunto_LuaTitleLibrary\getUrl
getUrl( $text, $which, $query=null, $proto=null)
Get a URL referring to this title.
Definition: TitleLibrary.php:222
Title\equals
equals(LinkTarget $title)
Compare with another title.
Definition: Title.php:4113
Scribunto_LuaLibraryBase\getEngine
getEngine()
Get the engine.
Definition: LibraryBase.php:56
Scribunto_LuaTitleLibrary\newTitle
newTitle( $text_or_id, $defaultNamespace=null)
Handler for title.new.
Definition: TitleLibrary.php:148
Scribunto_LuaLibraryBase\getParser
getParser()
Get the parser.
Definition: LibraryBase.php:74
NS_MAIN
const NS_MAIN
Definition: Defines.php:60
Scribunto_LuaTitleLibrary\checkNamespace
checkNamespace( $name, $argIdx, &$arg, $default=null)
Definition: TitleLibrary.php:29
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:49
Scribunto_LuaLibraryBase\checkType
checkType( $name, $argIdx, $arg, $expectType)
Check the type of a variable.
Definition: LibraryBase.php:141
Scribunto_LuaTitleLibrary\makeArrayOneBased
static makeArrayOneBased( $arr)
Definition: TitleLibrary.php:352
Scribunto_LuaTitleLibrary\getInexpensiveTitleData
getInexpensiveTitleData(Title $title)
Extract inexpensive information from a Title object for return to Lua.
Definition: TitleLibrary.php:60
Scribunto_LuaTitleLibrary\cascadingProtection
cascadingProtection( $text)
Handler for cascadingProtection.
Definition: TitleLibrary.php:386
Scribunto_LuaTitleLibrary\protectionLevels
protectionLevels( $text)
Handler for protectionLevels.
Definition: TitleLibrary.php:365
$t
$t
Definition: make-normalization-table.php:143
$title
$title
Definition: testCompression.php:34
SpecialPageFactory\exists
static exists( $name)
Definition: SpecialPageFactory_deprecated.php:43
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:913
Scribunto_LuaLibraryBase\checkTypeOptional
checkTypeOptional( $name, $argIdx, &$arg, $expectType, $default)
Check the type of a variable, with default if null.
Definition: LibraryBase.php:164
PROTO_HTTPS
const PROTO_HTTPS
Definition: Defines.php:200
Scribunto_LuaLibraryBase
This class provides some basic services that Lua libraries will probably need.
Definition: LibraryBase.php:27
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:613
$content
$content
Definition: router.php:78
NS_MEDIA
const NS_MEDIA
Definition: Defines.php:48
PROTO_HTTP
const PROTO_HTTP
Definition: Defines.php:199
PROTO_RELATIVE
const PROTO_RELATIVE
Definition: Defines.php:201
MWNamespace\exists
static exists( $index)
Returns whether the specified namespace exists.
Definition: MWNamespace.php:100
Scribunto_LuaTitleLibrary\$titleCache
$titleCache
Definition: TitleLibrary.php:8
Scribunto_LuaTitleLibrary\getContentInternal
getContentInternal( $text)
Utility to get a Content object from a title.
Definition: TitleLibrary.php:267
wfFindFile
wfFindFile( $title, $options=[])
Find a file.
Definition: GlobalFunctions.php:2604
$args
if( $line===false) $args
Definition: cdb.php:64
Title
Represents a title within MediaWiki.
Definition: Title.php:42
Scribunto_LuaLibraryBase\incrementExpensiveFunctionCount
incrementExpensiveFunctionCount()
Increment the expensive function count, and throw if limit exceeded.
Definition: LibraryBase.php:177
Scribunto_LuaTitleLibrary\getExpensiveData
getExpensiveData( $text)
Extract expensive information from a Title object for return to Lua.
Definition: TitleLibrary.php:95
Scribunto_LuaTitleLibrary\makeTitle
makeTitle( $ns, $text, $fragment=null, $interwiki=null)
Handler for title.makeTitle.
Definition: TitleLibrary.php:197
Scribunto_LuaTitleLibrary\redirectTarget
redirectTarget( $text)
Handler for redirectTarget.
Definition: TitleLibrary.php:413
Title\newFromID
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition: Title.php:467
$wgContLang
$wgContLang
Definition: Setup.php:801
Scribunto_LuaLibraryBase\getLuaType
getLuaType( $var)
Get the Lua type corresponding to the type of the variable.
Definition: LibraryBase.php:106
Scribunto_LuaTitleLibrary\getFileInfo
getFileInfo( $text)
Handler for getFileInfo.
Definition: TitleLibrary.php:308
$type
$type
Definition: testCompression.php:48