GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 841b7d...3ed1d3 )
by
unknown
13:44 queued 04:45
created

src/BasicBackend.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * File holding the Lingo\Backend class
5
 *
6
 * This file is part of the MediaWiki extension Lingo.
7
 *
8
 * @copyright 2011 - 2018, Stephan Gambke
9
 * @license   GNU General Public License, version 2 (or any later version)
10
 *
11
 * The Lingo extension is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by the Free
13
 * Software Foundation; either version 2 of the License, or (at your option) any
14
 * later version.
15
 *
16
 * The Lingo extension is distributed in the hope that it will be useful, but
17
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
19
 * details.
20
 *
21
 * You should have received a copy of the GNU General Public License along
22
 * with this program. If not, see <http://www.gnu.org/licenses/>.
23
 *
24
 * @author Stephan Gambke
25
 * @file
26
 * @ingroup Lingo
27
 */
28
namespace Lingo;
29
30
use ApprovedRevs;
31
use Hooks;
32
use Parser;
33
use ParserOptions;
34
use Revision;
35
use TextContent;
36
use Title;
37
use User;
38
use WikiPage;
39
40
/**
41
 * The Lingo\BasicBackend class.
42
 *
43
 * @ingroup Lingo
44
 */
45
class BasicBackend extends Backend {
46
47
	protected $mArticleLines = null;
48
49
	/**
50
	 * Lingo\BasicBackend constructor.
51
	 * @param MessageLog|null $messages
52
	 */
53 1
	public function __construct( MessageLog &$messages = null ) {
54
55 1
		parent::__construct( $messages );
56
57 1
		$this->registerHooks();
58
59 1
	}
60
61 17
	protected function registerHooks() {
62 17
		Hooks::register( 'ArticlePurge', [ $this, 'purgeCache' ] );
63 17
		Hooks::register( 'PageContentSave', [ $this, 'purgeCache' ] );
64 17
	}
65
66
	/**
67
	 * This function returns the next element. The element is an array of four
68
	 * strings: Term, Definition, Link, Source. For the Lingo\BasicBackend Link
69
	 * and Source are set to null. If there is no next element the function
70
	 * returns null.
71
	 *
72
	 * @return array | null
73
	 * @throws \MWException
74
	 */
75 7
	public function next() {
76
77 7
		static $term = null;
78 7
		static $definitions = [];
79 7
		static $ret = [];
80
81 7
		$this->collectDictionaryLines();
82
83
		// loop backwards: accumulate definitions until term found
84 7
		while ( ( count( $ret ) === 0 ) && ( $this->mArticleLines ) ) {
85
86 7
			$line = array_pop( $this->mArticleLines );
87
88 7
			if ( $this->isValidGlossaryLine( $line ) ) {
89
90 6
				list( $term, $definitions ) = $this->processNextGlossaryLine( $line, $term, $definitions );
91
92 6
				if ( $term !== null ) {
93 6
					$ret = $this->queueDefinitions( $definitions, $term );
94
				}
95
			}
96
		}
97
98 7
		return array_pop( $ret );
99
	}
100
101
	/**
102
	 * @param string $line
103
	 * @param string $term
104
	 * @param string[] $definitions
105
	 * @return array
106
	 */
107 9
	protected function processNextGlossaryLine( $line, $term, $definitions ) {
108
109 9
		$chunks = explode( ':', $line, 2 );
110
111
		// found a new definition?
112 9
		if ( count( $chunks ) === 2 ) {
113
114
			// wipe the data if it's a totally new term definition
115 9
			if ( !empty( $term ) && count( $definitions ) > 0 ) {
116 9
				$definitions = [];
117 9
				$term = null;
118
			}
119
120 9
			$definitions[] = trim( $chunks[ 1 ] );
121
		}
122
123
		// found a new term?
124 9
		if ( strlen( trim( $chunks[ 0 ] ) ) > 1 ) {
125 9
			$term = trim( substr( $chunks[ 0 ], 1 ) );
126
		}
127
128 9
		return [ $term, $definitions ];
129
	}
130
131
	/**
132
	 * @param $definitions
133
	 * @param $term
134
	 * @return array
135
	 */
136 9
	protected function queueDefinitions( $definitions, $term ) {
137 9
		$ret = [];
138
139 9
		foreach ( $definitions as $definition ) {
140 9
			$ret[] = [
141 9
				Element::ELEMENT_TERM       => $term,
142 9
				Element::ELEMENT_DEFINITION => $definition,
143 9
				Element::ELEMENT_LINK       => null,
144 9
				Element::ELEMENT_SOURCE     => null
145
			];
146
		}
147
148 9
		return $ret;
149
	}
150
151
	/**
152
	 * @throws \MWException
153
	 */
154 14
	protected function collectDictionaryLines() {
155
156 14
		if ( $this->mArticleLines !== null ) {
157 6
			return;
158
		}
159
160
		// Get Terminology page
161 14
		$dictionaryPageName = $this->getLingoPageName();
162 14
		$dictionaryTitle = $this->getTitleFromText( $dictionaryPageName );
163
164 14
		if ( $dictionaryTitle->getInterwiki() !== '' ) {
165 1
			$this->getMessageLog()->addError( wfMessage( 'lingo-terminologypagenotlocal', $dictionaryPageName )->inContentLanguage()->text() );
166 1
			return;
167
		}
168
169 13
		$rawContent = $this->getRawDictionaryContent( $dictionaryTitle );
170
171
		// Expand templates and variables in the text, producing valid, static
172
		// wikitext. Have to use a new anonymous user to avoid any leakage as
173
		// Lingo is caching only one user-independent glossary
174 13
		$parser = new Parser;
175 13
		$content = $parser->preprocess( $rawContent, $dictionaryTitle, new ParserOptions( new User() ) );
176
177 13
		$this->mArticleLines = explode( "\n", $content );
178 13
	}
179
180
	/**
181
	 * @return string
182
	 */
183 15
	private function getLingoPageName() {
184 15
		global $wgexLingoPage;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
185 15
		return $wgexLingoPage ? $wgexLingoPage : wfMessage( 'lingo-terminologypagename' )->inContentLanguage()->text();
186
	}
187
188
	/**
189
	 * @param Title $dictionaryTitle
190
	 *
191
	 * @return null|string
192
	 */
193 13
	protected function getRawDictionaryContent( Title $dictionaryTitle ) {
194
195 13
		global $wgRequest;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
196
197
		// This is a hack special-casing the submitting of the terminology page
198
		// itself. In this case the Revision is not up to date when we get here,
199
		// i.e. $revision->getText() would return outdated Text. This hack takes the
200
		// text directly out of the data from the web request.
201 13
		if ( $wgRequest->getVal( 'action', 'view' ) === 'submit' &&
202 13
			$this->getTitleFromText( $wgRequest->getVal( 'title' ) )->getArticleID() === $dictionaryTitle->getArticleID()
203
		) {
204
205 1
			return $wgRequest->getVal( 'wpTextbox1' );
206
		}
207
208 12
		$revision = $this->getRevisionFromTitle( $dictionaryTitle );
209
210 12
		if ( $revision !== null ) {
211
212 11
			$content = $revision->getContent();
213
214 11
			if ( is_null( $content ) ) {
215 1
				return '';
216
			}
217
218 10
			if ( $content instanceof TextContent ) {
219 9
				return $content->getNativeData();
220
			}
221
222 1
			$this->getMessageLog()->addError( wfMessage( 'lingo-notatextpage', $dictionaryTitle->getFullText() )->inContentLanguage()->text() );
223
224
		} else {
225
226 1
			$this->getMessageLog()->addWarning( wfMessage( 'lingo-noterminologypage', $dictionaryTitle->getFullText() )->inContentLanguage()->text() );
227
		}
228
229 2
		return '';
230
	}
231
232
	/**
233
	 * Returns revision of the terms page.
234
	 *
235
	 * @param Title $title
236
	 * @return null|Revision
237
	 */
238 12
	protected function getRevisionFromTitle( Title $title ) {
239 12
		global $wgexLingoEnableApprovedRevs;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
240
241 12
		if ( $wgexLingoEnableApprovedRevs ) {
242
243 2
			if ( defined( 'APPROVED_REVS_VERSION' ) ) {
244 1
				return $this->getApprovedRevisionFromTitle( $title );
245
			}
246
247 1
			$this->getMessageLog()->addWarning( wfMessage( 'lingo-noapprovedrevs' )->inContentLanguage()->text() );
248
		}
249
250 11
		return $this->getLatestRevisionFromTitle( $title );
251
	}
252
253
	/**
254
	 * Initiates the purging of the cache when the Terminology page was saved or purged.
255
	 *
256
	 * @param WikiPage $wikipage
257
	 * @return Bool
258
	 */
259 1
	public function purgeCache( WikiPage &$wikipage ) {
260
261 1
		if ( !is_null( $wikipage ) && ( $wikipage->getTitle()->getText() === $this->getLingoPageName() ) ) {
262
263 1
			$this->getLingoParser()->purgeGlossaryFromCache();
264
		}
265
266 1
		return true;
267
	}
268
269
	/**
270
	 * The basic backend is cache-enabled so this function returns true.
271
	 *
272
	 * Actual caching is done by the parser, the backend just calls
273
	 * Lingo\LingoParser::purgeCache when necessary.
274
	 *
275
	 * @return boolean
276
	 */
277 1
	public function useCache() {
278 1
		return true;
279
	}
280
281
	/**
282
	 * @codeCoverageIgnore
283
	 * @param $dictionaryPage
284
	 * @return Title
285
	 */
286
	protected function getTitleFromText( $dictionaryPage ) {
287
		return Title::newFromTextThrow( $dictionaryPage );
288
	}
289
290
	/**
291
	 * @codeCoverageIgnore
292
	 * @param Title $title
293
	 * @return null|Revision
294
	 */
295
	protected function getApprovedRevisionFromTitle( Title $title ) {
296
		return Revision::newFromId( ApprovedRevs::getApprovedRevID( $title ) );
297
	}
298
299
	/**
300
	 * @codeCoverageIgnore
301
	 * @param Title $title
302
	 * @return null|Revision
303
	 */
304
	protected function getLatestRevisionFromTitle( Title $title ) {
305
		return Revision::newFromTitle( $title );
306
	}
307
308
	/**
309
	 * @param $line
310
	 * @return bool
311
	 */
312 13
	protected function isValidGlossaryLine( $line ) {
313 13
		return !empty( $line ) && ( $line[ 0 ] === ';' || $line[ 0 ] === ':' );
314
	}
315
316
}
317