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.

Element::buildFormattedTerm()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 5
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 8
rs 10
ccs 5
cts 5
cp 1
crap 4
1
<?php
2
3
/**
4
 * File holding the Lingo\Element class.
5
 *
6
 * This file is part of the MediaWiki extension Lingo.
7
 *
8
 * @copyright 2011 - 2018, Stephan Gambke
9
 * @license GPL-2.0-or-later
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
 *
26
 * @file
27
 * @ingroup Lingo
28
 */
29
30
namespace Lingo;
31
32
use DOMDocument;
33
use DOMElement;
34
use DOMText;
35
use Title;
0 ignored issues
show
Bug introduced by
The type Title was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36
37
/**
38
 * This class represents a term-definition pair.
39
 * One term may be related to several definitions.
40
 *
41
 * @ingroup Lingo
42
 */
43
class Element {
44
	const ELEMENT_TERM = 0;
45
	const ELEMENT_DEFINITION = 1;
46
	const ELEMENT_SOURCE = 2;
47
	const ELEMENT_LINK = 3;
48
	const ELEMENT_STYLE = 4;
49
50
	const ELEMENT_FIELDCOUNT = 5;  // number of fields stored for each element; (last field's index) + 1
51
52
	const LINK_TEMPLATE_ID = 'LingoLink';
53
54
	private $formattedTerm = null;
55
	private $formattedDefinitions = null;
56
57
	private $mDefinitions = [];
58
	private $mTerm = null;
59
60
	private $hasBeenDisplayed = false;
61
62
	/**
63
	 * Lingo\Element constructor.
64
	 *
65
	 * @param string &$term
66
	 * @param string[] &$definition
67
	 */
68 12
	public function __construct( &$term, &$definition ) {
69 12
		$this->mTerm = $term;
70 12
		$this->addDefinition( $definition );
71 12
	}
72
73
	/**
74
	 * @param array &$definition
75
	 */
76 11
	public function addDefinition( &$definition ) {
77 11
		$this->mDefinitions[] = $definition + array_fill( 0, self::ELEMENT_FIELDCOUNT, null );
78 11
	}
79
80
	/**
81
	 * @param DOMDocument &$doc
82
	 *
83
	 * @return DOMElement|DOMText
84
	 */
85 11
	public function getFormattedTerm( DOMDocument &$doc ) {
86 11
		global $wgexLingoDisplayOnce;
87
88 11
		if ( $wgexLingoDisplayOnce && $this->hasBeenDisplayed ) {
89 2
			return $doc->createTextNode( $this->mTerm );
90
		}
91
92 11
		$this->hasBeenDisplayed = true;
93
94 11
		$this->buildFormattedTerm( $doc );
95
96 11
		return $this->formattedTerm->cloneNode( true );
97
	}
98
99
	/**
100
	 * @param DOMDocument &$doc
101
	 */
102 11
	private function buildFormattedTerm( DOMDocument &$doc ) {
103
		// only create if not yet created
104 11
		if ( $this->formattedTerm === null || $this->formattedTerm->ownerDocument !== $doc ) {
105
106 11
			if ( $this->isSimpleLink() ) {
107 7
				$this->formattedTerm = $this->buildFormattedTermAsLink( $doc );
108
			} else {
109 4
				$this->formattedTerm = $this->buildFormattedTermAsTooltip( $doc );
110
			}
111
		}
112 11
	}
113
114
	/**
115
	 * @return bool
116
	 */
117 11
	private function isSimpleLink() {
118 11
		return count( $this->mDefinitions ) === 1 &&
119 11
			!is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_DEFINITION ] ) &&
120 11
			is_string( $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ] );
121
	}
122
123
	/**
124
	 * @param DOMDocument &$doc
125
	 * @return DOMElement
126
	 */
127 7
	protected function buildFormattedTermAsLink( DOMDocument &$doc ) {
128 7
		$linkTarget = $this->mDefinitions[ 0 ][ self::ELEMENT_LINK ];
129 7
		$descriptor = $this->getDescriptorFromLinkTarget( $linkTarget );
130
131 7
		if ( $descriptor === null ) {
0 ignored issues
show
introduced by
The condition $descriptor === null is always false.
Loading history...
132 2
			$this->mDefinitions = [];
133 2
			$this->addErrorMessageForInvalidLink( $linkTarget );
134 2
			return $this->buildFormattedTermAsTooltip( $doc );
135
		}
136
137
		// create link element
138 5
		$link = $doc->createElement( 'a', htmlentities( $this->mDefinitions[ 0 ][ self::ELEMENT_TERM ] ) );
139
140
		// set the link target
141 5
		$link->setAttribute( 'href', $descriptor[ 'url' ] );
142 5
		$link->setAttribute( 'class', implode( ' ', $this->getClassesForLink( $descriptor ) ) );
143
144 5
		$title = $this->getTitleForLink( $descriptor );
145 5
		if ( $title !== null ) {
0 ignored issues
show
introduced by
The condition $title !== null is always true.
Loading history...
146 4
			$link->setAttribute( 'title', $title );
147
		}
148
149 5
		return $link;
150
	}
151
152
	/**
153
	 * @param DOMDocument &$doc
154
	 *
155
	 * @return DOMElement
156
	 */
157 6
	protected function buildFormattedTermAsTooltip( DOMDocument &$doc ) {
158
		// Wrap term and definition in <span> tags
159 6
		$span = $doc->createElement( 'span', htmlentities( $this->mTerm ) );
160 6
		$span->setAttribute( 'class', 'mw-lingo-term' );
161 6
		$span->setAttribute( 'data-lingo-term-id', $this->getId() );
162
163 6
		return $span;
164
	}
165
166
	/**
167
	 * @param array $descriptor
168
	 *
169
	 * @return string[]
170
	 */
171 5
	protected function getClassesForLink( $descriptor ) {
172
		// TODO: should this be more elaborate?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
173
		// Cleanest would probably be to use Linker::link and parse it
174
		// back into a DOMElement, but we are in a somewhat time-critical
175
		// part here.
176
177
		// set style
178 5
		$classes = [ 'mw-lingo-term' ];
179
180 5
		$classes[] = $this->mDefinitions[ 0 ][ self::ELEMENT_STYLE ];
181
182 5
		if ( array_key_exists( 'title', $descriptor ) && $descriptor[ 'title' ] instanceof Title ) {
183
184 4
			if ( !$descriptor['title']->isKnown() ) {
185 2
				$classes[] = 'new';
186
			}
187
188 4
			if ( $descriptor['title']->isExternal() ) {
189 4
				$classes[] = 'extiw';
190
			}
191
192
		} else {
193 1
			$classes[] = 'ext';
194
		}
195
196 5
		return array_filter( $classes );
197
	}
198
199
	/**
200
	 * @param array $descriptor
201
	 * @return string
202
	 */
203
	protected function getTitleForLink( $descriptor ) {
204
		/** @var \Title $target */
205 5
		$target = $descriptor[ 'title' ];
206
207 5
		if ( is_string( $target ) ) {
0 ignored issues
show
introduced by
The condition is_string($target) is always false.
Loading history...
208
			return $target;
209 5
		}
210 1
211
		if ( $target->getPrefixedText() === '' ) {
212
			return null;
213 4
		}
214 1
215
		if ( $target->isKnown() ) {
216
			return $target->getPrefixedText();
217 3
		}
218 1
219
		return wfMessage( 'red-link-title', $target->getPrefixedText() )->text();
0 ignored issues
show
Bug introduced by
The function wfMessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

219
		return /** @scrutinizer ignore-call */ wfMessage( 'red-link-title', $target->getPrefixedText() )->text();
Loading history...
220
	}
221 2
222
	/**
223
	 * @return string[]
224
	 */
225
	public function getFormattedDefinitions() {
226
		if ( $this->formattedDefinitions === null ) {
227 4
			$this->buildFormattedDefinitions();
228 4
		}
229 4
230
		return $this->formattedDefinitions;
231
	}
232 4
233
	/**
234
	 */
235
	protected function buildFormattedDefinitions() {
236
		if ( $this->isSimpleLink() ) {
237 4
			$this->formattedDefinitions = '';
238 4
			return;
239 1
		}
240 1
241
		$divDefinitions = "<div class='mw-lingo-tooltip' id='{$this->getId()}'>";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
242
243 3
		$definition = reset( $this->mDefinitions );
244
		while ( $definition !== false ) {
245 3
246 3
			$text = $definition[ self::ELEMENT_DEFINITION ];
247
			$link = $definition[ self::ELEMENT_LINK ];
248 3
			$style = $definition[ self::ELEMENT_STYLE ];
249 3
250 3
			// navigation-not-searchable removes definition from CirrusSearch index
251
			$divDefinitions .= "<div class='mw-lingo-definition navigation-not-searchable {$style}'>"
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $style instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
252 3
				. "<div class='mw-lingo-definition-text'>\n{$text}\n</div>";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $text instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
253
254 3
			if ( $link !== null ) {
255
256 2
				$descriptor = $this->getDescriptorFromLinkTarget( $link );
257
258 2
				if ( $descriptor === null ) {
259 1
					$this->addErrorMessageForInvalidLink( $link );
260
				} else {
261 1
					$divDefinitions .= "<div class='mw-lingo-definition-link'>[{$descriptor[ 'url' ]} <nowiki/>]</div>";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $descriptor instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
262
				}
263
			}
264
265 3
			$divDefinitions .= "</div>";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal </div> does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
266
267 3
			$definition = next( $this->mDefinitions );
268
		}
269
270 3
		$divDefinitions .= "\n</div>";
271
272 3
		$this->formattedDefinitions = $divDefinitions;
273 3
	}
274
275
	/**
276
	 * @return string
277
	 */
278 6
	public function getId() {
279 6
		return md5( $this->mTerm );
280
	}
281
282
	/**
283
	 * @param string $linkTarget
284
	 *
285
	 * @return string[]
286
	 */
287 9
	protected function getDescriptorFromLinkTarget( $linkTarget ) {
288 9
		if ( $this->isValidLinkTarget( $linkTarget ) ) {
289 1
			return [ 'url' => $linkTarget, 'title' => $this->mTerm ];
290
		}
291
292 8
		$title = Title::newFromText( $linkTarget );
293
294 8
		if ( $title !== null ) {
295 5
			return [ 'url' => $title->getFullURL(), 'title' => $title ];
296
		}
297
298 3
		return null;
299
	}
300
301
	/**
302
	 * @param string $linkTarget
303
	 *
304
	 * @return bool
305
	 */
306 9
	protected function isValidLinkTarget( $linkTarget ) {
307 9
		return wfParseUrl( $linkTarget ) !== false;
0 ignored issues
show
Bug introduced by
The function wfParseUrl was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

307
		return /** @scrutinizer ignore-call */ wfParseUrl( $linkTarget ) !== false;
Loading history...
308
	}
309
310
	/**
311
	 * @param string $link
312
	 */
313 3
	protected function addErrorMessageForInvalidLink( $link ) {
314 3
		$errorMessage = wfMessage( 'lingo-invalidlinktarget', $this->mTerm, $link )->text();
0 ignored issues
show
Bug introduced by
The function wfMessage was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

314
		$errorMessage = /** @scrutinizer ignore-call */ wfMessage( 'lingo-invalidlinktarget', $this->mTerm, $link )->text();
Loading history...
315 3
		$errorDefinition = [ self::ELEMENT_DEFINITION => $errorMessage, self::ELEMENT_STYLE => 'invalid-link-target' ];
316
317 3
		$this->addDefinition( $errorDefinition );
318 3
	}
319
320
}
321