Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
11.17% |
21 / 188 |
|
7.55% |
4 / 53 |
CRAP | |
0.00% |
0 / 1 |
MockSiteConfig | |
11.17% |
21 / 188 |
|
7.55% |
4 / 53 |
3912.30 | |
0.00% |
0 / 1 |
__construct | |
81.25% |
13 / 16 |
|
0.00% |
0 / 1 |
4.11 | |||
getLinterSiteConfig | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
allowedExternalImagePrefixes | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
baseURI | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
exportMetadataToHeadBcp47 | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
2 | |||
redirectRegexp | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
categoryRegexp | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
bswRegexp | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
canonicalNamespaceId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
namespaceId | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
namespaceName | |
66.67% |
4 / 6 |
|
0.00% |
0 / 1 |
3.33 | |||
namespaceHasSubpages | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
namespaceCase | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
specialPageLocalName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setInterwikiMagic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
interwikiMagic | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
interwikiMap | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
iwp | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
legalTitleChars | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
linkPrefixRegex | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
linkTrail | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
linkTrailRegex | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
langBcp47 | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
mainPageLinkTarget | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMWConfigValue | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
42 | |||
rtl | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
langConverterEnabledBcp47 | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
script | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
scriptpath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
server | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
timezoneOffset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
variantsFor | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
30 | |||
widthOption | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getVariableIDs | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
haveComputedFunctionSynonyms | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
updateFunctionSynonym | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMagicWords | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
2 | |||
getMagicWordMatcher | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getParameterizedAliasMatcher | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
30 | |||
getNonNativeExtensionTags | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
2 | |||
getMaxTemplateDepth | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSpecialPageAliases | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getSpecialNSAliases | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getProtocols | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
fakeTimestamp | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setFakeTimestamp | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setTimezoneOffset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
scrubBidiChars | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNoFollowConfig | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getExternalLinkTarget | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
metrics | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
incrementCounter | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
observeTiming | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | declare( strict_types = 1 ); |
3 | |
4 | namespace Wikimedia\Parsoid\Mocks; |
5 | |
6 | use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface; |
7 | use Monolog\Formatter\LineFormatter; |
8 | use Monolog\Handler\ErrorLogHandler; |
9 | use Monolog\Logger; |
10 | use Wikimedia\Bcp47Code\Bcp47Code; |
11 | use Wikimedia\Bcp47Code\Bcp47CodeValue; |
12 | use Wikimedia\Parsoid\Config\SiteConfig; |
13 | use Wikimedia\Parsoid\Config\StubMetadataCollector; |
14 | use Wikimedia\Parsoid\Core\ContentMetadataCollector; |
15 | use Wikimedia\Parsoid\Core\LinkTarget; |
16 | use Wikimedia\Parsoid\DOM\Document; |
17 | use Wikimedia\Parsoid\Utils\Title; |
18 | use Wikimedia\Parsoid\Utils\Utils; |
19 | |
20 | class MockSiteConfig extends SiteConfig { |
21 | |
22 | /** @var ?int Unix timestamp */ |
23 | private $fakeTimestamp = 946782245; // 2000-01-02T03:04:05Z |
24 | |
25 | /** @var int */ |
26 | private $timezoneOffset = 0; // UTC |
27 | |
28 | /** @var bool */ |
29 | private $interwikiMagic = true; |
30 | |
31 | /** @var array */ |
32 | private $linterOverrides = []; |
33 | |
34 | /** If set, generate experimental Parsoid HTML v3 parser function output */ |
35 | private bool $v3pf; |
36 | |
37 | private const NAMESPACE_MAP = [ |
38 | 'media' => -2, |
39 | 'special' => -1, |
40 | '' => 0, |
41 | 'talk' => 1, |
42 | 'user' => 2, |
43 | 'user_talk' => 3, |
44 | // Last one will be used by namespaceName |
45 | 'project' => 4, 'wp' => 4, 'wikipedia' => 4, |
46 | 'project_talk' => 5, 'wt' => 5, 'wikipedia_talk' => 5, |
47 | 'file' => 6, |
48 | 'file_talk' => 7, |
49 | 'template' => 10, |
50 | 'template_talk' => 11, |
51 | 'help' => 12, |
52 | 'category' => 14, |
53 | 'category_talk' => 15, |
54 | ]; |
55 | |
56 | /** @var array<int,bool> */ |
57 | protected $namespacesWithSubpages = []; |
58 | |
59 | /** @var array */ |
60 | protected $interwikiMap = []; |
61 | |
62 | /** @var int */ |
63 | private $maxDepth = 40; |
64 | |
65 | /** @var string|null */ |
66 | private $linkPrefixRegex = null; |
67 | |
68 | /** @var string|bool */ |
69 | private $externalLinkTarget; |
70 | |
71 | public function __construct( array $opts ) { |
72 | parent::__construct(); |
73 | |
74 | if ( isset( $opts['linting'] ) ) { |
75 | $this->linterEnabled = $opts['linting']; |
76 | } |
77 | if ( isset( $opts['maxDepth'] ) ) { |
78 | $this->maxDepth = $opts['maxDepth']; |
79 | } |
80 | if ( isset( $opts['linterOverrides'] ) ) { |
81 | $this->linterOverrides = $opts['linterOverrides']; |
82 | } |
83 | $this->linkPrefixRegex = $opts['linkPrefixRegex'] ?? null; |
84 | $this->linkTrailRegex = $opts['linkTrailRegex'] ?? '/^([a-z]+)/sD'; // enwiki default |
85 | $this->externalLinkTarget = $opts['externallinktarget'] ?? false; |
86 | $this->v3pf = $opts['v3pf'] ?? false; |
87 | |
88 | // Use Monolog's PHP console handler |
89 | $logger = new Logger( "Parsoid CLI" ); |
90 | $handler = new ErrorLogHandler(); |
91 | $handler->setFormatter( new LineFormatter( '%message%' ) ); |
92 | $logger->pushHandler( $handler ); |
93 | $this->setLogger( $logger ); |
94 | } |
95 | |
96 | public function getLinterSiteConfig(): array { |
97 | return $this->linterOverrides + parent::getLinterSiteConfig(); |
98 | } |
99 | |
100 | public function allowedExternalImagePrefixes(): array { |
101 | return []; |
102 | } |
103 | |
104 | public function baseURI(): string { |
105 | return '//my.wiki.example/wikix/'; |
106 | } |
107 | |
108 | /** |
109 | * @inheritDoc |
110 | */ |
111 | public function exportMetadataToHeadBcp47( |
112 | Document $document, |
113 | ContentMetadataCollector $metadata, |
114 | string $defaultTitle, |
115 | Bcp47Code $lang |
116 | ): void { |
117 | '@phan-var StubMetadataCollector $metadata'; // @var StubMetadataCollector $metadata |
118 | $moduleLoadURI = $this->server() . $this->scriptpath() . '/load.php'; |
119 | // Look for a displaytitle. |
120 | $displayTitle = $metadata->getPageProperty( 'displaytitle' ) ?? |
121 | // Use the default title, properly escaped |
122 | Utils::escapeHtml( $defaultTitle ); |
123 | $this->exportMetadataHelper( |
124 | $document, |
125 | $moduleLoadURI, |
126 | $metadata->getModules(), |
127 | $metadata->getModuleStyles(), |
128 | $metadata->getJsConfigVars(), |
129 | $displayTitle, |
130 | $lang |
131 | ); |
132 | } |
133 | |
134 | public function redirectRegexp(): string { |
135 | return '/(?i:#REDIRECT)/'; |
136 | } |
137 | |
138 | public function categoryRegexp(): string { |
139 | return '/Category/'; |
140 | } |
141 | |
142 | public function bswRegexp(): string { |
143 | return '/' . |
144 | 'NOGLOBAL|DISAMBIG|NOCOLLABORATIONHUBTOC|nocollaborationhubtoc|NOTOC|notoc|' . |
145 | 'NOGALLERY|nogallery|FORCETOC|forcetoc|TOC|toc|NOEDITSECTION|noeditsection|' . |
146 | 'NOTITLECONVERT|notitleconvert|NOTC|notc|NOCONTENTCONVERT|nocontentconvert|' . |
147 | 'NOCC|nocc|NEWSECTIONLINK|NONEWSECTIONLINK|HIDDENCAT|INDEX|NOINDEX|STATICREDIRECT' . |
148 | '/'; |
149 | } |
150 | |
151 | /** @inheritDoc */ |
152 | public function canonicalNamespaceId( string $name ): ?int { |
153 | return self::NAMESPACE_MAP[$name] ?? null; |
154 | } |
155 | |
156 | /** @inheritDoc */ |
157 | public function namespaceId( string $name ): ?int { |
158 | $name = Utils::normalizeNamespaceName( $name ); |
159 | return self::NAMESPACE_MAP[$name] ?? null; |
160 | } |
161 | |
162 | /** @inheritDoc */ |
163 | public function namespaceName( int $ns ): ?string { |
164 | static $map = null; |
165 | if ( $map === null ) { |
166 | $map = array_flip( self::NAMESPACE_MAP ); |
167 | } |
168 | if ( !isset( $map[$ns] ) ) { |
169 | return null; |
170 | } |
171 | return ucwords( strtr( $map[$ns], '_', ' ' ) ); |
172 | } |
173 | |
174 | /** @inheritDoc */ |
175 | public function namespaceHasSubpages( int $ns ): bool { |
176 | return !empty( $this->namespacesWithSubpages[$ns] ); |
177 | } |
178 | |
179 | /** @inheritDoc */ |
180 | public function namespaceCase( int $ns ): string { |
181 | return 'first-letter'; |
182 | } |
183 | |
184 | /** @inheritDoc */ |
185 | public function specialPageLocalName( string $alias ): ?string { |
186 | return null; |
187 | } |
188 | |
189 | public function setInterwikiMagic( bool $val ): void { |
190 | $this->interwikiMagic = $val; |
191 | } |
192 | |
193 | public function interwikiMagic(): bool { |
194 | return $this->interwikiMagic; |
195 | } |
196 | |
197 | public function interwikiMap(): array { |
198 | return $this->interwikiMap; |
199 | } |
200 | |
201 | public function iwp(): string { |
202 | return 'mywiki'; |
203 | } |
204 | |
205 | public function legalTitleChars(): string { |
206 | return ' %!"$&\'()*,\-.\/0-9:;=?@A-Z\\\\^_`a-z~\x80-\xFF+'; |
207 | } |
208 | |
209 | public function linkPrefixRegex(): ?string { |
210 | return $this->linkPrefixRegex; |
211 | } |
212 | |
213 | protected function linkTrail(): string { |
214 | // @phan-suppress-previous-line PhanPluginNeverReturnMethod |
215 | throw new \BadMethodCallException( |
216 | 'Should not be used. linkTrailRegex() is overridden here.' ); |
217 | } |
218 | |
219 | public function linkTrailRegex(): ?string { |
220 | return $this->linkTrailRegex; |
221 | } |
222 | |
223 | public function langBcp47(): Bcp47Code { |
224 | return new Bcp47CodeValue( 'en' ); |
225 | } |
226 | |
227 | public function mainPageLinkTarget(): LinkTarget { |
228 | return Title::newFromText( 'Main Page', $this ); |
229 | } |
230 | |
231 | /** @inheritDoc */ |
232 | public function getMWConfigValue( string $key ) { |
233 | switch ( $key ) { |
234 | case 'CiteResponsiveReferences': |
235 | return true; |
236 | case 'CiteResponsiveReferencesThreshold': |
237 | return 10; |
238 | case 'ParsoidFragmentInput': |
239 | return false; |
240 | case 'ParsoidExperimentalParserFunctionOutput': |
241 | return $this->v3pf; |
242 | default: |
243 | return null; |
244 | } |
245 | } |
246 | |
247 | public function rtl(): bool { |
248 | return false; |
249 | } |
250 | |
251 | /** @inheritDoc */ |
252 | public function langConverterEnabledBcp47( Bcp47Code $lang ): bool { |
253 | return $lang->toBcp47Code() === 'sr'; |
254 | } |
255 | |
256 | public function script(): string { |
257 | return '/wx/index.php'; |
258 | } |
259 | |
260 | public function scriptpath(): string { |
261 | return '/wx'; |
262 | } |
263 | |
264 | public function server(): string { |
265 | return '//my.wiki.example'; |
266 | } |
267 | |
268 | public function timezoneOffset(): int { |
269 | return $this->timezoneOffset; |
270 | } |
271 | |
272 | /** @inheritDoc */ |
273 | public function variantsFor( Bcp47Code $lang ): ?array { |
274 | switch ( $lang->toBcp47Code() ) { |
275 | case 'sr': |
276 | return [ |
277 | 'base' => new Bcp47CodeValue( 'sr' ), |
278 | 'fallbacks' => [ |
279 | new Bcp47CodeValue( 'sr-Cyrl' ) |
280 | ] |
281 | ]; |
282 | case 'sr-Cyrl': |
283 | return [ |
284 | 'base' => new Bcp47CodeValue( 'sr' ), |
285 | 'fallbacks' => [ |
286 | new Bcp47CodeValue( 'sr' ) |
287 | ] |
288 | ]; |
289 | case 'sr-Latn': |
290 | return [ |
291 | 'base' => new Bcp47CodeValue( 'sr' ), |
292 | 'fallbacks' => [ |
293 | new Bcp47CodeValue( 'sr' ) |
294 | ] |
295 | ]; |
296 | default: |
297 | return null; |
298 | } |
299 | } |
300 | |
301 | public function widthOption(): int { |
302 | return 220; |
303 | } |
304 | |
305 | /** @inheritDoc */ |
306 | protected function getVariableIDs(): array { |
307 | return []; // None for now |
308 | } |
309 | |
310 | /** @inheritDoc */ |
311 | protected function haveComputedFunctionSynonyms(): bool { |
312 | return false; |
313 | } |
314 | |
315 | /** @inheritDoc */ |
316 | protected function updateFunctionSynonym( string $func, string $magicword, bool $caseSensitive ): void { |
317 | /* Nothing for now. Look at src/Config/Api/SiteConfig when mocking is needed. */ |
318 | } |
319 | |
320 | /** @inheritDoc */ |
321 | protected function getMagicWords(): array { |
322 | return [ |
323 | 'toc' => [ 0, '__TOC__' ], |
324 | 'img_thumbnail' => [ 1, 'thumb' ], |
325 | 'img_framed' => [ 1, 'frame', 'framed' ], |
326 | 'img_frameless' => [ 1, 'frameless' ], |
327 | 'img_manualthumb' => [ 1, 'thumbnail=$1', 'thumb=$1' ], |
328 | 'img_none' => [ 1, 'none' ], |
329 | 'img_left' => [ 1, 'left' ], |
330 | 'img_right' => [ 1, 'right' ], |
331 | // T345026: 'sub' should follow 'img_sub' to match dewikivoyage |
332 | 'img_sub' => [ 1, 'sub' ], |
333 | 'sub' => [ 0, 'sub' ], |
334 | 'notoc' => [ 0, '__NOTOC__' ], |
335 | 'timedmedia_loop' => [ 0, 'loop' ], |
336 | 'timedmedia_muted' => [ 0, 'muted' ], |
337 | ]; |
338 | } |
339 | |
340 | /** @inheritDoc */ |
341 | public function getMagicWordMatcher( string $id ): string { |
342 | if ( $id === 'toc' ) { |
343 | return '/^TOC$/'; |
344 | } else { |
345 | return '/(?!)/'; |
346 | } |
347 | } |
348 | |
349 | /** @inheritDoc */ |
350 | public function getParameterizedAliasMatcher( array $words ): callable { |
351 | $paramMWs = [ |
352 | 'img_lossy' => "/^(?:(?i:lossy\=(.*?)))$/uS", |
353 | 'timedmedia_thumbtime' => "/^(?:(?i:thumbtime\=(.*?)))$/uS", |
354 | 'timedmedia_starttime' => "/^(?:(?i:start\=(.*?)))$/uS", |
355 | 'timedmedia_endtime' => "/^(?:(?i:end\=(.*?)))$/uS", |
356 | 'timedmedia_disablecontrols' => "/^(?:(?i:disablecontrols\=(.*?)))$/uS", |
357 | 'img_manualthumb' => "/^(?:(?:thumbnail\=(.*?)|thumb\=(.*?)))$/uS", |
358 | 'img_width' => "/^(?:(?:(.*?)px))$/uS", |
359 | 'img_lang' => "/^(?:(?:lang\=(.*?)))$/uS", |
360 | 'img_page' => "/^(?:(?:page\=(.*?)|page (.*?)))$/uS", |
361 | 'img_upright' => "/^(?:(?:upright\=(.*?)|upright (.*?)))$/uS", |
362 | 'img_link' => "/^(?:(?:link\=(.*?)))$/uS", |
363 | 'img_alt' => "/^(?:(?:alt\=(.*?)))$/uS", |
364 | 'img_class' => "/^(?:(?:class\=(.*?)))$/uS" |
365 | ]; |
366 | $regexes = array_intersect_key( $paramMWs, array_flip( $words ) ); |
367 | return static function ( $text ) use ( $regexes ) { |
368 | /** |
369 | * $name is the canonical magic word name |
370 | * $re has patterns for matching aliases |
371 | */ |
372 | foreach ( $regexes as $name => $re ) { |
373 | if ( preg_match( $re, $text, $m ) ) { |
374 | unset( $m[0] ); |
375 | |
376 | // Ex. regexp here is, /^(?:(?:|vinculo\=(.*?)|enlace\=(.*?)|link\=(.*?)))$/uS |
377 | // Check all the capture groups for a value, if not, it's safe to return an |
378 | // empty string since we did get a match. |
379 | foreach ( $m as $v ) { |
380 | if ( $v !== '' ) { |
381 | return [ 'k' => $name, 'v' => $v ]; |
382 | } |
383 | } |
384 | return [ 'k' => $name, 'v' => '' ]; |
385 | } |
386 | } |
387 | return null; |
388 | }; |
389 | } |
390 | |
391 | /** @inheritDoc */ |
392 | protected function getNonNativeExtensionTags(): array { |
393 | return [ |
394 | 'indicator' => true, |
395 | 'timeline' => true, |
396 | 'hiero' => true, |
397 | 'charinsert' => true, |
398 | 'inputbox' => true, |
399 | 'source' => true, |
400 | 'syntaxhighlight' => true, |
401 | 'section' => true, |
402 | 'score' => true, |
403 | 'templatedata' => true, |
404 | 'math' => true, |
405 | 'ce' => true, |
406 | 'chem' => true, |
407 | 'graph' => true, |
408 | 'maplink' => true, |
409 | 'categorytree' => true, |
410 | 'templatestyles' => true |
411 | ]; |
412 | } |
413 | |
414 | /** @inheritDoc */ |
415 | public function getMaxTemplateDepth(): int { |
416 | return $this->maxDepth; |
417 | } |
418 | |
419 | /** @inheritDoc */ |
420 | protected function getSpecialPageAliases( string $specialPage ): array { |
421 | if ( $specialPage === 'Booksources' ) { |
422 | return [ 'Booksources', 'BookSources' ]; // Mock value |
423 | } else { |
424 | throw new \BadMethodCallException( 'Not implemented' ); |
425 | } |
426 | } |
427 | |
428 | /** @inheritDoc */ |
429 | protected function getSpecialNSAliases(): array { |
430 | return [ "Special", "special" ]; // Mock value |
431 | } |
432 | |
433 | /** @inheritDoc */ |
434 | protected function getProtocols(): array { |
435 | return [ "http:", "https:", "irc:", "ircs:", "news:", "ftp:", "mailto:", "gopher:", "//" ]; |
436 | } |
437 | |
438 | public function fakeTimestamp(): ?int { |
439 | return $this->fakeTimestamp; |
440 | } |
441 | |
442 | /** |
443 | * Set the fake timestamp for testing |
444 | * @param ?int $ts Unix timestamp |
445 | */ |
446 | public function setFakeTimestamp( ?int $ts ): void { |
447 | $this->fakeTimestamp = $ts; |
448 | } |
449 | |
450 | /** |
451 | * Set the timezone offset for testing |
452 | * @param int $offset Offset from UTC |
453 | */ |
454 | public function setTimezoneOffset( int $offset ): void { |
455 | $this->timezoneOffset = $offset; |
456 | } |
457 | |
458 | public function scrubBidiChars(): bool { |
459 | return true; |
460 | } |
461 | |
462 | /** @inheritDoc */ |
463 | public function getNoFollowConfig(): array { |
464 | return [ |
465 | 'nofollow' => true, |
466 | 'nsexceptions' => [ 1 ], |
467 | 'domainexceptions' => [ 'www.example.com' ] |
468 | ]; |
469 | } |
470 | |
471 | /** @inheritDoc */ |
472 | public function getExternalLinkTarget() { |
473 | return $this->externalLinkTarget; |
474 | } |
475 | |
476 | /** @var ?MockMetrics */ |
477 | private $metrics; |
478 | |
479 | /** @inheritDoc */ |
480 | public function metrics(): ?StatsdDataFactoryInterface { |
481 | if ( $this->metrics === null ) { |
482 | $this->metrics = new MockMetrics(); |
483 | } |
484 | return $this->metrics; |
485 | } |
486 | |
487 | /** |
488 | * Increment a counter metric |
489 | * @param string $name |
490 | * @param array $labels |
491 | * @param float $amount |
492 | * @return void |
493 | */ |
494 | public function incrementCounter( string $name, array $labels, float $amount = 1 ): void { |
495 | // We don't use the labels for now, using MockMetrics instead |
496 | $this->metrics->increment( $name ); |
497 | } |
498 | |
499 | /** |
500 | * Record a timing metric |
501 | * @param string $name |
502 | * @param float $value |
503 | * @param array $labels |
504 | * @return void |
505 | */ |
506 | public function observeTiming( string $name, float $value, array $labels ): void { |
507 | // We don't use the labels for now, using MockMetrics instead |
508 | $this->metrics->timing( $name, $value ); |
509 | } |
510 | } |