ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
Title.php
Go to the documentation of this file.
1 <?php
2 
3 // patched: alex, 30.4.2019: Added missing defines
4 
5 define('NS_MAIN', "nsmain");
6 define('NS_SPECIAL', "nsspecial");
7 
8 define('GAID_FOR_UPDATE', 1);
9 
10 # Title::newFromTitle maintains a cache to avoid
11 # expensive re-normalization of commonly used titles.
12 # On a batch operation this can become a memory leak
13 # if not bounded. After hitting this many titles,
14 # reset the cache.
15 define('MW_TITLECACHE_MAX', 1000);
16 
17 # Constants for pr_cascade bitfield
18 define('CASCADE', 1);
19 
26 class Title
27 {
31  private static $titleCache = array();
32  private static $interwikiCache = array();
36  protected bool $mOldRestrictions;
37 
47  public $mTextform; # Text form (spaces not underscores) of the main part
48  public $mUrlform; # URL-encoded form of the main part
49  public $mDbkeyform; # Main part with underscores
50  public $mNamespace; # Namespace index, i.e. one of the NS_xxxx constants
51  public $mInterwiki; # Interwiki prefix (or null string)
52  public $mFragment; # Title fragment (i.e. the bit after the #)
53  public $mArticleID; # Article ID, fetched from the link cache on demand
54  public $mLatestID; # ID of most recent revision
55  public $mRestrictions; # Array of groups allowed to edit this article
56  public $mCascadeRestriction; # Cascade restrictions on this page to included templates and images?
57  public $mRestrictionsExpiry; # When do the restrictions on this page expire?
58  public $mHasCascadingRestrictions; # Are cascading restrictions in effect on this page?
59  public $mCascadeRestrictionSources;# Where are the cascading restrictions coming from on this page?
60  public $mRestrictionsLoaded; # Boolean for initialisation on demand
61  public $mPrefixedText; # Text form including namespace/interwiki, initialised on demand
62  public $mDefaultNamespace; # Namespace index when there is no namespace
63  # Zero except in {{transclusion}} tags
64  public $mWatched; # Is $wgUser watching this page? NULL if unfilled, accessed through userIsWatching()
72  /* private */ public function __construct()
73  {
74  $this->mInterwiki = $this->mUrlform =
75  $this->mTextform = $this->mDbkeyform = '';
76  $this->mArticleID = -1;
77  $this->mNamespace = NS_MAIN;
78  $this->mRestrictionsLoaded = false;
79  $this->mRestrictions = array();
80  # Dont change the following, NS_MAIN is hardcoded in several place
81  # See bug #696
82  $this->mDefaultNamespace = NS_MAIN;
83  $this->mWatched = null;
84  $this->mLatestID = false;
85  $this->mOldRestrictions = false;
86  }
87 
88 
100  public static function newFromText($text, $defaultNamespace = NS_MAIN)
101  {
110  if ($defaultNamespace == NS_MAIN && isset(Title::$titleCache[$text])) {
111  return Title::$titleCache[$text];
112  }
113 
117  $filteredText = Sanitizer::decodeCharReferences($text);
118 
119  $t = new Title();
120  $t->mDbkeyform = str_replace(' ', '_', $filteredText);
121  $t->mDefaultNamespace = $defaultNamespace;
122 
123  static $cachedcount = 0 ;
124  if ($t->secureAndSplit()) {
125  if ($defaultNamespace == NS_MAIN) {
126  if ($cachedcount >= MW_TITLECACHE_MAX) {
127  # Avoid memory leaks on mass operations...
128  Title::$titleCache = array();
129  $cachedcount = 0;
130  }
131  $cachedcount++;
132  Title::$titleCache[$text] = &$t;
133  }
134  return $t;
135  } else {
136  $ret = null;
137  return $ret;
138  }
139  }
140 
141 
142  #----------------------------------------------------------------------------
143  # Static functions
144  #----------------------------------------------------------------------------
145 
146 
151  public static function legalChars()
152  {
153  global $wgLegalTitleChars;
154 
155  $wgLegalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+";
156 
157  return $wgLegalTitleChars;
158  }
159 
160 
168  public function getInterwikiLink($key)
169  {
170  return "";
171  }
172 
173  #----------------------------------------------------------------------------
174  # Other stuff
175  #----------------------------------------------------------------------------
176 
182  public function getText()
183  {
184  return $this->mTextform;
185  }
186 
191  public function getDBkey()
192  {
193  return $this->mDbkeyform;
194  }
199  public function getNamespace()
200  {
201  return $this->mNamespace;
202  }
207  public function getNsText()
208  {
209  global $wgContLang, $wgCanonicalNamespaceNames;
210 
211  if ('' != $this->mInterwiki) {
212  // This probably shouldn't even happen. ohh man, oh yuck.
213  // But for interwiki transclusion it sometimes does.
214  // Shit. Shit shit shit.
215  //
216  // Use the canonical namespaces if possible to try to
217  // resolve a foreign namespace.
218  if (isset($wgCanonicalNamespaceNames[$this->mNamespace])) {
219  return $wgCanonicalNamespaceNames[$this->mNamespace];
220  }
221  }
222  return $wgContLang->getNsText($this->mNamespace);
223  }
228  public function getInterwiki()
229  {
230  return $this->mInterwiki;
231  }
236  public function getFragment()
237  {
238  return $this->mFragment;
239  }
240 
249  /* private */ public function prefix($name)
250  {
251  $p = '';
252  if ('' != $this->mInterwiki) {
253  $p = $this->mInterwiki . ':';
254  }
255  if (0 != $this->mNamespace) {
256  $p .= $this->getNsText() . ':';
257  }
258  return $p . $name;
259  }
260 
271  private function secureAndSplit()
272  {
273  global $wgContLang, $wgLocalInterwiki, $wgCapitalLinks;
274 
275  # Initialisation
276  static $rxTc = false;
277  if (!$rxTc) {
278  # % is needed as well
279  $rxTc = '/[^' . Title::legalChars() . ']|%[0-9A-Fa-f]{2}/S';
280  }
281 
282  $this->mInterwiki = $this->mFragment = '';
283  $this->mNamespace = $this->mDefaultNamespace; # Usually NS_MAIN
284 
285  $dbkey = $this->mDbkeyform;
286 
287  # Strip Unicode bidi override characters.
288  # Sometimes they slip into cut-n-pasted page titles, where the
289  # override chars get included in list displays.
290  $dbkey = str_replace("\xE2\x80\x8E", '', $dbkey); // 200E LEFT-TO-RIGHT MARK
291  $dbkey = str_replace("\xE2\x80\x8F", '', $dbkey); // 200F RIGHT-TO-LEFT MARK
292 
293  # Clean up whitespace
294  #
295  $dbkey = preg_replace('/[ _]+/', '_', $dbkey);
296  $dbkey = trim($dbkey, '_');
297 
298  if ('' == $dbkey) {
299  return false;
300  }
301 
302  if (false !== strpos($dbkey, UTF8_REPLACEMENT)) {
303  # Contained illegal UTF-8 sequences or forbidden Unicode chars.
304  return false;
305  }
306 
307  $this->mDbkeyform = $dbkey;
308 
309  # Initial colon indicates main namespace rather than specified default
310  # but should not create invalid {ns,title} pairs such as {0,Project:Foo}
311  if (':' == $dbkey[0]) {
312  $this->mNamespace = NS_MAIN;
313  $dbkey = substr($dbkey, 1); # remove the colon but continue processing
314  $dbkey = trim($dbkey, '_'); # remove any subsequent whitespace
315  }
316 
317  # Namespace or interwiki prefix
318  $firstPass = true;
319  do {
320  $m = array();
321  if (preg_match("/^(.+?)_*:_*(.*)$/S", $dbkey, $m)) {
322  $p = $m[1];
323  if ($ns = $wgContLang->getNsIndex($p)) {
324  # Ordinary namespace
325  $dbkey = $m[2];
326  $this->mNamespace = $ns;
327  } elseif ($this->getInterwikiLink($p)) {
328  if (!$firstPass) {
329  # Can't make a local interwiki link to an interwiki link.
330  # That's just crazy!
331  return false;
332  }
333 
334  # Interwiki link
335  $dbkey = $m[2];
336  $this->mInterwiki = $wgContLang->lc($p);
337 
338  # Redundant interwiki prefix to the local wiki
339  if (0 == strcasecmp($this->mInterwiki, $wgLocalInterwiki)) {
340  if ($dbkey == '') {
341  # Can't have an empty self-link
342  return false;
343  }
344  $this->mInterwiki = '';
345  $firstPass = false;
346  # Do another namespace split...
347  continue;
348  }
349 
350  # If there's an initial colon after the interwiki, that also
351  # resets the default namespace
352  if ($dbkey !== '' && $dbkey[0] == ':') {
353  $this->mNamespace = NS_MAIN;
354  $dbkey = substr($dbkey, 1);
355  }
356  }
357  # If there's no recognized interwiki or namespace,
358  # then let the colon expression be part of the title.
359  }
360  break;
361  } while (true);
362 
363  # We already know that some pages won't be in the database!
364  #
365  if ('' != $this->mInterwiki || NS_SPECIAL == $this->mNamespace) {
366  $this->mArticleID = 0;
367  }
368  $fragment = strstr($dbkey, '#');
369  if (false !== $fragment) {
370  $this->setFragment($fragment);
371  $dbkey = substr($dbkey, 0, strlen($dbkey) - strlen($fragment));
372  # remove whitespace again: prevents "Foo_bar_#"
373  # becoming "Foo_bar_"
374  $dbkey = preg_replace('/_*$/', '', $dbkey);
375  }
376 
377  # Reject illegal characters.
378  #
379  if (preg_match($rxTc, $dbkey)) {
380  return false;
381  }
382 
388  if (strpos($dbkey, '.') !== false &&
389  ($dbkey === '.' || $dbkey === '..' ||
390  strpos($dbkey, './') === 0 ||
391  strpos($dbkey, '../') === 0 ||
392  strpos($dbkey, '/./') !== false ||
393  strpos($dbkey, '/../') !== false)) {
394  return false;
395  }
396 
400  if (strpos($dbkey, '~~~') !== false) {
401  return false;
402  }
403 
411  if (($this->mNamespace != NS_SPECIAL && strlen($dbkey) > 255) ||
412  strlen($dbkey) > 512) {
413  return false;
414  }
415 
424  if ($wgCapitalLinks && $this->mInterwiki == '') {
425  $dbkey = $wgContLang->ucfirst($dbkey);
426  }
427 
433  if ($dbkey == '' &&
434  $this->mInterwiki == '' &&
435  $this->mNamespace != NS_MAIN) {
436  return false;
437  }
438 
439  // Any remaining initial :s are illegal.
440  if ($dbkey !== '' && ':' == $dbkey[0]) {
441  return false;
442  }
443 
444  # Fill fields
445  $this->mDbkeyform = $dbkey;
446  $this->mUrlform = ilWikiUtil::wfUrlencode($dbkey);
447 
448  $this->mTextform = str_replace('_', ' ', $dbkey);
449 
450  return true;
451  }
452 
462  public function setFragment($fragment)
463  {
464  $this->mFragment = str_replace('_', ' ', substr($fragment, 1));
465  }
466 
473  public function equals($title)
474  {
475  // Note: === is necessary for proper matching of number-like titles.
476  return $this->getInterwiki() === $title->getInterwiki()
477  && $this->getNamespace() == $title->getNamespace()
478  && $this->getDBkey() === $title->getDBkey();
479  }
480 }
setFragment($fragment)
Set the fragment for this title This is kind of bad, since except for this rarely-used function...
Definition: Title.php:462
getFragment()
Get the Title fragment (i.e.
Definition: Title.php:236
$mNamespace
Definition: Title.php:50
getText()
Simple accessors.
Definition: Title.php:182
$mArticleID
Definition: Title.php:53
$mRestrictionsExpiry
Definition: Title.php:57
$mInterwiki
Definition: Title.php:51
const NS_SPECIAL
Definition: Title.php:6
secureAndSplit()
Secure and split - main initialisation function for this object.
Definition: Title.php:271
getInterwikiLink($key)
Returns the URL associated with an interwiki prefix.
Definition: Title.php:168
prefix($name)
Prefix some arbitrary text with the namespace or interwiki prefix of this object. ...
Definition: Title.php:249
static $interwikiCache
Definition: Title.php:32
$mHasCascadingRestrictions
Definition: Title.php:58
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:100
$mRestrictionsLoaded
Definition: Title.php:60
Title class.
Definition: Title.php:26
const NS_MAIN
Definition: Title.php:5
$mCascadeRestriction
Definition: Title.php:56
$mDefaultNamespace
Definition: Title.php:62
static decodeCharReferences($text)
Decode any character references, numeric or named entities, in the text and return a UTF-8 string...
Definition: Sanitizer.php:381
getNsText()
Get the namespace text.
Definition: Title.php:207
__construct()
#-
Definition: Title.php:72
getDBkey()
Get the main part with underscores.
Definition: Title.php:191
$mFragment
Definition: Title.php:52
$mRestrictions
Definition: Title.php:55
static $titleCache
Static cache variables.
Definition: Title.php:31
$text
Definition: xapiexit.php:21
getNamespace()
Get the namespace index, i.e.
Definition: Title.php:199
bool $mOldRestrictions
Definition: Title.php:36
getInterwiki()
Get the interwiki prefix (or null string)
Definition: Title.php:228
to(\GdImage $image, int $quality=null)
Currently this is the only way to make a FileStream from a GD image resource.
$mDbkeyform
Definition: Title.php:49
$mLatestID
Definition: Title.php:54
form( $class_path, string $cmd, string $submit_caption="")
const MW_TITLECACHE_MAX
Definition: Title.php:15
$mTextform
All member variables should be considered private Please use the accessor functions.
Definition: Title.php:47
link(string $caption, string $href, bool $new_viewport=false)
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:151
$mPrefixedText
Definition: Title.php:61
$mWatched
Definition: Title.php:64
static wfUrlencode(string $s)
$mCascadeRestrictionSources
Definition: Title.php:59
$mUrlform
Definition: Title.php:48
equals($title)
Compare with another title.
Definition: Title.php:473