MediaWiki master
TitleMatcher.php
Go to the documentation of this file.
1<?php
2namespace MediaWiki\Search;
3
10use MediaWiki\Languages\LanguageConverterFactory;
15use UtfNormal\Validator;
16
21
22 private Language $language;
23 private ILanguageConverter $languageConverter;
24 private HookRunner $hookRunner;
25 private WikiPageFactory $wikiPageFactory;
26 private RepoGroup $repoGroup;
27 private TitleFactory $titleFactory;
28
29 public function __construct(
30 Language $contentLanguage,
31 LanguageConverterFactory $languageConverterFactory,
32 HookContainer $hookContainer,
33 WikiPageFactory $wikiPageFactory,
34 RepoGroup $repoGroup,
35 TitleFactory $titleFactory
36 ) {
37 $this->language = $contentLanguage;
38 $this->languageConverter = $languageConverterFactory->getLanguageConverter( $contentLanguage );
39 $this->hookRunner = new HookRunner( $hookContainer );
40 $this->wikiPageFactory = $wikiPageFactory;
41 $this->repoGroup = $repoGroup;
42 $this->titleFactory = $titleFactory;
43 }
44
52 public function getNearMatch( $searchterm ) {
53 $title = $this->getNearMatchInternal( $searchterm );
54
55 $this->hookRunner->onSearchGetNearMatchComplete( $searchterm, $title );
56 return $title;
57 }
58
66 public function getNearMatchResultSet( $searchterm ) {
67 return new SearchNearMatchResultSet( $this->getNearMatch( $searchterm ) );
68 }
69
75 protected function getNearMatchInternal( $searchterm ) {
76 $allSearchTerms = [ $searchterm ];
77
78 if ( $this->languageConverter->hasVariants() ) {
79 $allSearchTerms = array_unique( array_merge(
80 $allSearchTerms,
81 $this->languageConverter->autoConvertToAllVariants( $searchterm )
82 ) );
83 }
84
85 $titleResult = null;
86 if ( !$this->hookRunner->onSearchGetNearMatchBefore( $allSearchTerms, $titleResult ) ) {
87 return $titleResult;
88 }
89
90 // Most of our handling here deals with finding a valid title for the search term,
91 // but almost anything starting with '#' is "valid" and points to Main_Page#searchterm.
92 // Rather than doing something completely wrong, do nothing.
93 if ( $searchterm === '' || $searchterm[0] === '#' ) {
94 return null;
95 }
96
97 foreach ( $allSearchTerms as $term ) {
98 # Exact match? No need to look further.
99 $title = $this->titleFactory->newFromText( $term );
100 if ( $title === null ) {
101 return null;
102 }
103
104 # Try files if searching in the Media: namespace
105 if ( $title->getNamespace() === NS_MEDIA ) {
106 $title = Title::makeTitle( NS_FILE, $title->getText() );
107 }
108
109 if ( $title->isSpecialPage() || $title->isExternal() || $title->exists() ) {
110 return $title;
111 }
112
113 # See if it still otherwise has content is some sensible sense
114 if ( $title->canExist() ) {
115 $page = $this->wikiPageFactory->newFromTitle( $title );
116 if ( $page->hasViewableContent() ) {
117 return $title;
118 }
119 }
120
121 if ( !$this->hookRunner->onSearchAfterNoDirectMatch( $term, $title ) ) {
122 return $title;
123 }
124
125 # Now try all lower case (=> first letter capitalized on some wikis)
126 $title = $this->titleFactory->newFromText( $this->language->lc( $term ) );
127 if ( $title && $title->exists() ) {
128 return $title;
129 }
130
131 # Now try normalized lowercase (if it's different)
132 $normTerm = Validator::toNFKC( $term );
133 $normDiff = $normTerm !== $term;
134 if ( $normDiff ) {
135 $title = $this->titleFactory->newFromText( $this->language->lc( $normTerm ) );
136 if ( $title && $title->exists() ) {
137 return $title;
138 }
139 }
140
141 # Now try capitalized string
142 $title = $this->titleFactory->newFromText( $this->language->ucwords( $term ) );
143 if ( $title && $title->exists() ) {
144 return $title;
145 }
146
147 # Now try normalized capitalized (if it's different)
148 if ( $normDiff ) {
149 $title = $this->titleFactory->newFromText( $this->language->ucwords( $normTerm ) );
150 if ( $title && $title->exists() ) {
151 return $title;
152 }
153 }
154
155 # Now try all upper case
156 $title = $this->titleFactory->newFromText( $this->language->uc( $term ) );
157 if ( $title && $title->exists() ) {
158 return $title;
159 }
160
161 # Now try Word-Caps-Breaking-At-Word-Breaks, for hyphenated names etc
162 $title = $this->titleFactory->newFromText( $this->language->ucwordbreaks( $term ) );
163 if ( $title && $title->exists() ) {
164 return $title;
165 }
166
167 // Give hooks a chance at better match variants
168 $title = null;
169 // @phan-suppress-next-line PhanTypeMismatchArgument Type mismatch on pass-by-ref args
170 if ( !$this->hookRunner->onSearchGetNearMatch( $term, $title ) ) {
171 return $title;
172 }
173 }
174
175 $title = $this->titleFactory->newFromTextThrow( $searchterm );
176
177 # Entering a user goes to the user page whether it's there or not
178 if ( $title->getNamespace() === NS_USER ) {
179 return $title;
180 }
181
182 # Go to images that exist even if there's no local page.
183 # There may have been a funny upload, or it may be on a shared
184 # file repository such as Wikimedia Commons.
185 if ( $title->getNamespace() === NS_FILE ) {
186 $image = $this->repoGroup->findFile( $title );
187 if ( $image ) {
188 return $title;
189 }
190 }
191
192 # MediaWiki namespace? Page may be "implied" if not customized.
193 # Just return it, with caps forced as the message system likes it.
194 if ( $title->getNamespace() === NS_MEDIAWIKI ) {
195 return Title::makeTitle( NS_MEDIAWIKI, $this->language->ucfirst( $title->getText() ) );
196 }
197
198 # Quoted term? Try without the quotes...
199 $matches = [];
200 if ( preg_match( '/^"([^"]+)"$/', $searchterm, $matches ) ) {
201 return $this->getNearMatch( $matches[1] );
202 }
203
204 return null;
205 }
206}
const NS_USER
Definition Defines.php:53
const NS_FILE
Definition Defines.php:57
const NS_MEDIAWIKI
Definition Defines.php:59
const NS_MEDIA
Definition Defines.php:39
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
Prioritized list of file repositories.
Definition RepoGroup.php:24
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Base class for language-specific code.
Definition Language.php:68
Service for creating WikiPage objects.
Service implementation of near match title search.
getNearMatch( $searchterm)
If an exact title match can be found, or a very slightly close match, return the title.
getNearMatchResultSet( $searchterm)
Do a near match (see SearchEngine::getNearMatch) and wrap it into a ISearchResultSet.
__construct(Language $contentLanguage, LanguageConverterFactory $languageConverterFactory, HookContainer $hookContainer, WikiPageFactory $wikiPageFactory, RepoGroup $repoGroup, TitleFactory $titleFactory)
getNearMatchInternal( $searchterm)
Really find the title match.
Creates Title objects.
Represents a title within MediaWiki.
Definition Title.php:69
A ISearchResultSet wrapper for TitleMatcher.
A set of SearchEngine results.
The shared interface for all language converters.