LinkTitles extension for MediaWiki
Automatically add links to existing pages.
All Classes Namespaces Functions Variables Modules Pages
Linker.php
1 <?php
24 namespace LinkTitles;
25 
29 class Linker {
35  public $config;
36 
47  private $linkValue;
48 
49  private static $locked = 0;
50 
56  public function __construct( Config &$config ) {
57  $this->config = $config;
58  }
59 
72  public function linkContent( Source $source ) {
73  if ( self::$locked > 0 || !$source->canBeLinked() ) {
74  return;
75  }
76 
77  ( $this->config->firstOnly ) ? $limit = 1 : $limit = -1;
78  $limitReached = false;
79  $newLinks = false; // whether or not new links were added
80  $newText = $source->getText();
81  $splitter = Splitter::singleton( $this->config );
82  $targets = Targets::singleton( $source->getTitle(), $this->config );
83 
84  // Iterate through the target page titles
85  foreach( $targets->queryResult as $row ) {
86  $target = new Target( $row->page_namespace, $row->page_title, $this->config );
87 
88  // Don't link current page and don't link if the target page redirects
89  // to the current page or has the __NOAUTOLINKTARGET__ magic word
90  // (as required by the actual LinkTitles configuration).
91  if ( $target->isSameTitle( $source ) || !$target->mayLinkTo( $source ) ) {
92  continue;
93  }
94 
95  // Dealing with existing links if the firstOnly option is set:
96  // A link to the current page should only be recognized if it appears in
97  // clear text, i.e. we do not count piped links as existing links.
98  // (Similarly, by design, redirections should not be counted as existing links.)
99  if ( $limit == 1 && preg_match( '/\[\[' . $target->getCaseSensitiveLinkValueRegex() . ']]/' , $source->getText() ) ) {
100  continue;
101  }
102 
103  // Split the page content by non-linkable sections.
104  // Credits to inhan @ StackOverflow for suggesting preg_split.
105  // See http://stackoverflow.com/questions/10672286
106  $arr = $splitter->split( $newText );
107  $count = 0;
108 
109  // Cache the target title text for the regex callbacks
110  $this->linkValue = $target->getPrefixedTitleText();
111 
112  // Even indexes will point to sections of the text that may be linked
113  for ( $i = 0; $i < count( $arr ); $i += 2 ) {
114  $arr[$i] = preg_replace_callback( $target->getCaseSensitiveRegex(),
115  array( $this, 'simpleModeCallback'),
116  $arr[$i], $limit, $replacements );
117  $count += $replacements;
118  if ( $this->config->firstOnly && ( $count > 0 ) ) {
119  $limitReached = true;
120  break;
121  };
122  };
123  if ( $count > 0 ) {
124  $newLinks = true;
125  $newText = implode( '', $arr );
126  }
127 
128  // If smart mode is turned on, the extension will perform a second
129  // pass on the page and add links with aliases where the case does
130  // not match.
131  if ( $this->config->smartMode && !$limitReached ) {
132  if ( $count > 0 ) {
133  // Split the text again because it was changed in the first pass.
134  $arr = $splitter->split( $newText );
135  }
136 
137  for ( $i = 0; $i < count( $arr ); $i+=2 ) {
138  // even indexes will point to text that is not enclosed by brackets
139  $arr[$i] = preg_replace_callback( $target->getCaseInsensitiveRegex(),
140  array( $this, 'smartModeCallback'),
141  $arr[$i], $limit, $replacements );
142  $count += $replacements;
143  if ( $this->config->firstOnly && ( $count > 0 )) {
144  break;
145  };
146  };
147  if ( $count > 0 ) {
148  $newLinks = true;
149  $newText = implode( '', $arr );
150  }
151  } // $wgLinkTitlesSmartMode
152  }; // foreach $res as $row
153 
154  if ( $newLinks ) {
155  return $newText;
156  }
157  }
158 
165  private function simpleModeCallback( array $matches ) {
166  // If the link value is longer than the match, it must be prefixed with
167  // a namespace. In this case, we build a piped link.
168  if ( strlen( $this->linkValue ) > strlen( $matches[0] ) ) {
169  return '[[' . $this->linkValue . '|' . $matches[0] . ']]';
170  } else {
171  return '[[' . $matches[0] . ']]';
172  }
173  }
174 
187  private function smartModeCallback( array $matches ) {
188  // If cases of the target page title and the actual occurrence in the text
189  // are not identical, we need to build a piped link.
190  // How case-identity is determined depends on the $wgCapitalLinks setting:
191  // with $wgCapitalLinks = true, the case of first letter of the title is
192  // not significant.
193  if ( $this->config->capitalLinks ) {
194  $needPipe = strcmp( substr( $this->linkValue, 1 ), substr( $matches[ 0 ], 1 ) ) != 0;
195  } else {
196  $needPipe = strcmp( $this->linkValue, $matches[ 0 ] ) != 0;
197  }
198  if ( $needPipe ) {
199  return '[[' . $this->linkValue . '|' . $matches[ 0 ] . ']]';
200  } else {
201  return '[[' . $matches[ 0 ] . ']]';
202  }
203  }
204 
212  public static function lock() {
213  self::$locked += 1;
214  }
215 
223  public static function unlock() {
224  self::$locked -= 1;
225  }
226 }
227 
228 // vim: ts=2:sw=2:noet:comments^=\:///
__construct(Config &$config)
Constructs a new instance of the Linker class.
Definition: Linker.php:56
linkContent(Source $source)
Core function of the extension, performs the actual parsing of the content.
Definition: Linker.php:72
canBeLinked()
Determines whether or not this page may be linked to.
Definition: Source.php:133
getTitle()
Gets the title.
Definition: Source.php:160
smartModeCallback(array $matches)
Callback function for use with preg_replace_callback.
Definition: Linker.php:187
$config
LinkTitles configuration.
Definition: Linker.php:35
static lock()
Increases an internal static lock counter by 1.
Definition: Linker.php:212
getText()
Gets the text of the corresponding Wiki page.
Definition: Source.php:210
Represents a page that is a potential link target.
Definition: Target.php:29
simpleModeCallback(array $matches)
Callback for preg_replace_callback in simple mode.
Definition: Linker.php:165
$linkValue
The link value of the target page that is currently being evaluated.
Definition: Linker.php:47
static unlock()
Decreases an internal static lock counter by 1.
Definition: Linker.php:223
Holds LinkTitles configuration.
Definition: Config.php:37
The LinkTitles class holds configuration for the LinkTitles extension.
Definition: Config.php:23
Performs the actual linking of content to existing pages.
Definition: Linker.php:29
Represents a page that is a potential link target.
Definition: Source.php:29