Passed
Push — master ( 72b2a9...63e81e )
by MusikAnimal
06:33
created

AppExtension::loggedInUser()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 5
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the AppExtension class.
4
 */
5
6
declare(strict_types = 1);
7
8
namespace AppBundle\Twig;
9
10
use AppBundle\Helper\I18nHelper;
11
use AppBundle\Model\Edit;
12
use AppBundle\Model\Project;
13
use AppBundle\Model\User;
14
use AppBundle\Repository\ProjectRepository;
15
use DateTime;
16
use Doctrine\DBAL\Connection;
17
use Symfony\Component\DependencyInjection\ContainerInterface;
18
use Symfony\Component\HttpFoundation\RequestStack;
19
use Symfony\Component\HttpFoundation\Session\SessionInterface;
20
use Twig_Extension;
21
22
/**
23
 * Twig functions and filters for XTools.
24
 */
25
class AppExtension extends Twig_Extension
26
{
27
    /** @var ContainerInterface The application's container interface. */
28
    protected $container;
29
30
    /** @var RequestStack The request stack. */
31
    protected $requestStack;
32
33
    /** @var SessionInterface User's current session. */
34
    protected $session;
35
36
    /** @var I18nHelper For i18n and l10n. */
37
    protected $i18n;
38
39
    /** @var float Duration of the current HTTP request in seconds. */
40
    protected $requestTime;
41
42
    /**
43
     * Constructor, with the I18nHelper through dependency injection.
44
     * @param ContainerInterface $container
45
     * @param RequestStack $requestStack
46
     * @param SessionInterface $session
47
     * @param I18nHelper $i18n
48
     */
49 27
    public function __construct(
50
        ContainerInterface $container,
51
        RequestStack $requestStack,
52
        SessionInterface $session,
53
        I18nHelper $i18n
54
    ) {
55 27
        $this->container = $container;
56 27
        $this->requestStack = $requestStack;
57 27
        $this->session = $session;
58 27
        $this->i18n = $i18n;
59 27
    }
60
61
    /*********************************** FUNCTIONS ***********************************/
62
63
    /**
64
     * Get all functions that this class provides.
65
     * @return \Twig_SimpleFunction[]
66
     */
67 11
    public function getFunctions(): array
68
    {
69 11
        $options = ['is_safe' => ['html']];
70
        return [
71 11
            new \Twig_SimpleFunction('request_time', [$this, 'requestTime'], $options),
72 11
            new \Twig_SimpleFunction('memory_usage', [$this, 'requestMemory'], $options),
73 11
            new \Twig_SimpleFunction('msgIfExists', [$this, 'msgIfExists'], $options),
74 11
            new \Twig_SimpleFunction('msgExists', [$this, 'msgExists'], $options),
75 11
            new \Twig_SimpleFunction('msg', [$this, 'msg'], $options),
76 11
            new \Twig_SimpleFunction('lang', [$this, 'getLang'], $options),
77 11
            new \Twig_SimpleFunction('langName', [$this, 'getLangName'], $options),
78 11
            new \Twig_SimpleFunction('fallbackLangs', [$this, 'getFallbackLangs', $options]),
79 11
            new \Twig_SimpleFunction('allLangs', [$this, 'getAllLangs']),
80 11
            new \Twig_SimpleFunction('isRTL', [$this, 'isRTL']),
81 11
            new \Twig_SimpleFunction('shortHash', [$this, 'gitShortHash']),
82 11
            new \Twig_SimpleFunction('hash', [$this, 'gitHash']),
83 11
            new \Twig_SimpleFunction('releaseDate', [$this, 'gitDate']),
84 11
            new \Twig_SimpleFunction('enabled', [$this, 'tabEnabled']),
85 11
            new \Twig_SimpleFunction('tools', [$this, 'tools']),
86 11
            new \Twig_SimpleFunction('color', [$this, 'getColorList']),
87 11
            new \Twig_SimpleFunction('chartColor', [$this, 'chartColor']),
88 11
            new \Twig_SimpleFunction('isSingleWiki', [$this, 'isSingleWiki']),
89 11
            new \Twig_SimpleFunction('getReplagThreshold', [$this, 'getReplagThreshold']),
90 11
            new \Twig_SimpleFunction('isWMFLabs', [$this, 'isWMFLabs']),
91 11
            new \Twig_SimpleFunction('replag', [$this, 'replag']),
92 11
            new \Twig_SimpleFunction('quote', [$this, 'quote']),
93 11
            new \Twig_SimpleFunction('bugReportURL', [$this, 'bugReportURL']),
94 11
            new \Twig_SimpleFunction('logged_in_user', [$this, 'loggedInUser']),
95 11
            new \Twig_SimpleFunction('isUserAnon', [$this, 'isUserAnon']),
96 11
            new \Twig_SimpleFunction('nsName', [$this, 'nsName']),
97 11
            new \Twig_SimpleFunction('titleWithNs', [$this, 'titleWithNs']),
98 11
            new \Twig_SimpleFunction('formatDuration', [$this, 'formatDuration']),
99 11
            new \Twig_SimpleFunction('numberFormat', [$this, 'numberFormat']),
100 11
            new \Twig_SimpleFunction('buildQuery', [$this, 'buildQuery']),
101
        ];
102
    }
103
104
    /**
105
     * Get the duration of the current HTTP request in seconds.
106
     * @return float
107
     * Untestable since there is no request stack in the tests.
108
     * @codeCoverageIgnore
109
     */
110
    public function requestTime(): float
111
    {
112
        if (!isset($this->requestTime)) {
113
            $this->requestTime = microtime(true) - $this->getRequest()->server->get('REQUEST_TIME_FLOAT');
114
        }
115
116
        return $this->requestTime;
117
    }
118
119
    /**
120
     * Get the formatted real memory usage.
121
     * @return float
122
     */
123 12
    public function requestMemory(): float
124
    {
125 12
        $mem = memory_get_usage(false);
126 12
        $div = pow(1024, 2);
127 12
        return $mem / $div;
128
    }
129
130
    /**
131
     * Get an i18n message.
132
     * @param string $message
133
     * @param string[] $vars
134
     * @return string|null
135
     */
136 11
    public function msg(string $message = '', array $vars = []): ?string
137
    {
138 11
        return $this->i18n->msg($message, $vars);
139
    }
140
141
    /**
142
     * See if a given i18n message exists.
143
     * @param string $message The message.
144
     * @param string[] $vars
145
     * @return bool
146
     */
147
    public function msgExists(?string $message, array $vars = []): bool
148
    {
149
        return $this->i18n->msgExists($message, $vars);
150
    }
151
152
    /**
153
     * Get an i18n message if it exists, otherwise just get the message key.
154
     * @param string $message
155
     * @param string[] $vars
156
     * @return string
157
     */
158
    public function msgIfExists(?string $message, array $vars = []): string
159
    {
160
        return $this->i18n->msgIfExists($message, $vars);
161
    }
162
163
    /**
164
     * Get the current language code.
165
     * @return string
166
     */
167 12
    public function getLang(): string
168
    {
169 12
        return $this->i18n->getLang();
170
    }
171
172
    /**
173
     * Get the current language name (defaults to 'English').
174
     * @return string
175
     */
176 12
    public function getLangName(): string
177
    {
178 12
        return $this->i18n->getLangName();
179
    }
180
181
    /**
182
     * Get the fallback languages for the current language, so we know what to load with jQuery.i18n.
183
     * @return string[]
184
     */
185 11
    public function getFallbackLangs(): array
186
    {
187 11
        return $this->i18n->getFallbacks();
188
    }
189
190
    /**
191
     * Get all available languages in the i18n directory
192
     * @return string[] Associative array of langKey => langName
193
     */
194 12
    public function getAllLangs(): array
195
    {
196 12
        return $this->i18n->getAllLangs();
197
    }
198
199
    /**
200
     * Whether the current language is right-to-left.
201
     * @param string|null $lang Optionally provide a specific lanuage code.
202
     * @return bool
203
     */
204 12
    public function isRTL(?string $lang = null): bool
205
    {
206 12
        return $this->i18n->isRTL($lang);
207
    }
208
209
    /**
210
     * Get the short hash of the currently checked-out Git commit.
211
     * @return string
212
     */
213 11
    public function gitShortHash(): string
214
    {
215 11
        return exec('git rev-parse --short HEAD');
216
    }
217
218
    /**
219
     * Get the full hash of the currently checkout-out Git commit.
220
     * @return string
221
     */
222 12
    public function gitHash(): string
223
    {
224 12
        return exec('git rev-parse HEAD');
225
    }
226
227
    /**
228
     * Get the date of the HEAD commit.
229
     * @return string
230
     */
231 2
    public function gitDate(): string
232
    {
233 2
        $date = new DateTime(exec('git show -s --format=%ci'));
234 2
        return $this->dateFormat($date, 'yyyy-MM-dd');
235
    }
236
237
    /**
238
     * Check whether a given tool is enabled.
239
     * @param string $tool The short name of the tool.
240
     * @return bool
241
     */
242 11
    public function tabEnabled(string $tool = 'index'): bool
243
    {
244 11
        $param = false;
245 11
        if ($this->container->hasParameter("enable.$tool")) {
246 11
            $param = boolval($this->container->getParameter("enable.$tool"));
247
        }
248 11
        return $param;
249
    }
250
251
    /**
252
     * Get a list of the short names of all tools.
253
     * @return string[]
254
     */
255 11
    public function tools(): array
256
    {
257 11
        $retVal = [];
258 11
        if ($this->container->hasParameter('tools')) {
259 11
            $retVal = $this->container->getParameter('tools');
260
        }
261 11
        return $retVal;
262
    }
263
264
    /**
265
     * Get a list of namespace colours (one or all).
266
     * @param int|false $num The NS ID to get. False to get the full list.
267
     * @return string|string[] Color or all all colors indexed by namespace ID.
268
     */
269
    public static function getColorList($num = false)
270
    {
271
        $colors = [
272
            0 => '#FF5555',
273
            1 => '#55FF55',
274
            2 => '#FFEE22',
275
            3 => '#FF55FF',
276
            4 => '#5555FF',
277
            5 => '#55FFFF',
278
            6 => '#C00000',
279
            7 => '#0000C0',
280
            8 => '#008800',
281
            9 => '#00C0C0',
282
            10 => '#FFAFAF',
283
            11 => '#808080',
284
            12 => '#00C000',
285
            13 => '#404040',
286
            14 => '#C0C000',
287
            15 => '#C000C0',
288
            90 => '#991100',
289
            91 => '#99FF00',
290
            92 => '#000000',
291
            93 => '#777777',
292
            100 => '#75A3D1',
293
            101 => '#A679D2',
294
            102 => '#660000',
295
            103 => '#000066',
296
            104 => '#FAFFAF',
297
            105 => '#408345',
298
            106 => '#5c8d20',
299
            107 => '#e1711d',
300
            108 => '#94ef2b',
301
            109 => '#756a4a',
302
            110 => '#6f1dab',
303
            111 => '#301e30',
304
            112 => '#5c9d96',
305
            113 => '#a8cd8c',
306
            114 => '#f2b3f1',
307
            115 => '#9b5828',
308
            116 => '#002288',
309
            117 => '#0000CC',
310
            118 => '#99FFFF',
311
            119 => '#99BBFF',
312
            120 => '#FF99FF',
313
            121 => '#CCFFFF',
314
            122 => '#CCFF00',
315
            123 => '#CCFFCC',
316
            200 => '#33FF00',
317
            201 => '#669900',
318
            202 => '#666666',
319
            203 => '#999999',
320
            204 => '#FFFFCC',
321
            205 => '#FF00CC',
322
            206 => '#FFFF00',
323
            207 => '#FFCC00',
324
            208 => '#FF0000',
325
            209 => '#FF6600',
326
            250 => '#6633CC',
327
            251 => '#6611AA',
328
            252 => '#66FF99',
329
            253 => '#66FF66',
330
            446 => '#06DCFB',
331
            447 => '#892EE4',
332
            460 => '#99FF66',
333
            461 => '#99CC66',
334
            470 => '#CCCC33',
335
            471 => '#CCFF33',
336
            480 => '#6699FF',
337
            481 => '#66FFFF',
338
            484 => '#07C8D6',
339
            485 => '#2AF1FF',
340
            486 => '#79CB21',
341
            487 => '#80D822',
342
            490 => '#995500',
343
            491 => '#998800',
344
            710 => '#FFCECE',
345
            711 => '#FFC8F2',
346
            828 => '#F7DE00',
347
            829 => '#BABA21',
348
            866 => '#FFFFFF',
349
            867 => '#FFCCFF',
350
            1198 => '#FF34B3',
351
            1199 => '#8B1C62',
352
            2300 => '#A900B8',
353
            2301 => '#C93ED6',
354
            2302 => '#8A09C1',
355
            2303 => '#974AB8',
356
            2600 => '#000000',
357
        ];
358
359
        if (false === $num) {
360
            return $colors;
361
        } elseif (isset($colors[$num])) {
362
            return $colors[$num];
363
        } else {
364
            // Default to grey.
365
            return '#CCC';
366
        }
367
    }
368
369
    /**
370
     * Get color-blind friendly colors for use in charts
371
     * @param int $num Index of color
372
     * @return string RGBA color (so you can more easily adjust the opacity)
373
     */
374
    public function chartColor(int $num): string
375
    {
376
        $colors = [
377
            'rgba(171, 212, 235, 1)',
378
            'rgba(178, 223, 138, 1)',
379
            'rgba(251, 154, 153, 1)',
380
            'rgba(253, 191, 111, 1)',
381
            'rgba(202, 178, 214, 1)',
382
            'rgba(207, 182, 128, 1)',
383
            'rgba(141, 211, 199, 1)',
384
            'rgba(252, 205, 229, 1)',
385
            'rgba(255, 247, 161, 1)',
386
            'rgba(217, 217, 217, 1)',
387
        ];
388
389
        return $colors[$num % count($colors)];
390
    }
391
392
    /**
393
     * Whether XTools is running in single-project mode.
394
     * @return bool
395
     */
396 8
    public function isSingleWiki(): bool
397
    {
398 8
        $param = true;
399 8
        if ($this->container->hasParameter('app.single_wiki')) {
400 8
            $param = boolval($this->container->getParameter('app.single_wiki'));
401
        }
402 8
        return $param;
403
    }
404
405
    /**
406
     * Get the database replication-lag threshold.
407
     * @return int
408
     */
409
    public function getReplagThreshold(): int
410
    {
411
        $param = 30;
412
        if ($this->container->hasParameter('app.replag_threshold')) {
413
            $param = $this->container->getParameter('app.replag_threshold');
414
        }
415
        return $param;
416
    }
417
418
    /**
419
     * Whether XTools is running in WMF Labs mode.
420
     * @return bool
421
     */
422 11
    public function isWMFLabs(): bool
423
    {
424 11
        $param = false;
425 11
        if ($this->container->hasParameter('app.is_labs')) {
426 11
            $param = boolval($this->container->getParameter('app.is_labs'));
427
        }
428 11
        return $param;
429
    }
430
431
    /**
432
     * The current replication lag.
433
     * @return int
434
     * @codeCoverageIgnore
435
     */
436
    public function replag(): int
437
    {
438
        $retVal = 0;
439
440
        if ($this->isWMFLabs()) {
441
            $project = $this->getRequest()->get('project');
442
443
            if (!isset($project)) {
444
                $project = 'enwiki';
445
            }
446
447
            $dbName = ProjectRepository::getProject($project, $this->container)
448
                ->getDatabaseName();
449
450
            $sql = "SELECT lag FROM `heartbeat_p`.`heartbeat` h
451
                    RIGHT JOIN `meta_p`.`wiki` w ON concat(h.shard, \".labsdb\")=w.slice
452
                    WHERE dbname LIKE :project LIMIT 1";
453
454
            /** @var Connection $conn */
455
            $conn = $this->container->get('doctrine')->getManager('replicas')->getConnection();
456
457
            // Prepare the query and execute
458
            $resultQuery = $conn->prepare($sql);
459
            $resultQuery->bindParam('project', $dbName);
460
            $resultQuery->execute();
461
462
            if (0 == $resultQuery->errorCode()) {
463
                $results = $resultQuery->fetchAll();
464
465
                if (isset($results[0]['lag'])) {
466
                    $retVal = $results[0]['lag'];
467
                }
468
            }
469
        }
470
471
        return (int)$retVal;
472
    }
473
474
    /**
475
     * Get a random quote for the footer
476
     * @return string
477
     */
478 11
    public function quote(): string
479
    {
480
        // Don't show if Quote is turned off, but always show for Labs
481
        // (so quote is in footer but not in nav).
482 11
        $isLabs = $this->container->getParameter('app.is_labs');
483 11
        if (!$isLabs && !$this->container->getParameter('enable.Quote')) {
484 11
            return '';
485
        }
486
        $quotes = $this->container->getParameter('quotes');
487
        $id = array_rand($quotes);
488
        return $quotes[$id];
489
    }
490
491
    /**
492
     * Get the currently logged in user's details.
493
     * @return string[]|null
494
     */
495 11
    public function loggedInUser(): ?array
496
    {
497 11
        return null !== $this->container->get('session')->get('logged_in_user')
498
            ? (array)$this->container->get('session')->get('logged_in_user')
499 11
            : null;
500
    }
501
502
    /*********************************** FILTERS ***********************************/
503
504
    /**
505
     * Get all filters for this extension.
506
     * @return \Twig_SimpleFilter[]
507
     */
508 11
    public function getFilters(): array
509
    {
510
        return [
511 11
            new \Twig_SimpleFilter('capitalize_first', [$this, 'capitalizeFirst']),
512 11
            new \Twig_SimpleFilter('percent_format', [$this, 'percentFormat']),
513 11
            new \Twig_SimpleFilter('diff_format', [$this, 'diffFormat'], ['is_safe' => ['html']]),
514 11
            new \Twig_SimpleFilter('num_format', [$this, 'numberFormat']),
515 11
            new \Twig_SimpleFilter('date_format', [$this, 'dateFormat']),
516 11
            new \Twig_SimpleFilter('wikify', [$this, 'wikify']),
517
        ];
518
    }
519
520
    /**
521
     * Format a number based on language settings.
522
     * @param int|float $number
523
     * @param int $decimals Number of decimals to format to.
524
     * @return string
525
     */
526 14
    public function numberFormat($number, $decimals = 0): string
527
    {
528 14
        return $this->i18n->numberFormat($number, $decimals);
529
    }
530
531
    /**
532
     * Localize the given date based on language settings.
533
     * @param string|DateTime $datetime
534
     * @param string $pattern Format according to this ICU date format.
535
     * @see http://userguide.icu-project.org/formatparse/datetime
536
     * @return string
537
     */
538 3
    public function dateFormat($datetime, $pattern = 'yyyy-MM-dd HH:mm'): string
539
    {
540 3
        return $this->i18n->dateFormat($datetime, $pattern);
541
    }
542
543
    /**
544
     * Convert raw wikitext to HTML-formatted string.
545
     * @param string $str
546
     * @param Project $project
547
     * @return string
548
     */
549 1
    public function wikify(string $str, Project $project): string
550
    {
551 1
        return Edit::wikifyString($str, $project);
552
    }
553
554
    /**
555
     * Mysteriously missing Twig helper to capitalize only the first character.
556
     * E.g. used for table headings for translated messages
557
     * @param string $str The string
558
     * @return string The string, capitalized
559
     */
560 6
    public function capitalizeFirst(string $str): string
561
    {
562 6
        return ucfirst($str);
563
    }
564
565
    /**
566
     * Format a given number or fraction as a percentage.
567
     * @param int|float $numerator Numerator or single fraction if denominator is ommitted.
568
     * @param int $denominator Denominator.
569
     * @param integer $precision Number of decimal places to show.
570
     * @return string Formatted percentage.
571
     */
572 1
    public function percentFormat($numerator, ?int $denominator = null, int $precision = 1): string
573
    {
574 1
        return $this->i18n->percentFormat($numerator, $denominator, $precision);
575
    }
576
577
    /**
578
     * Helper to return whether the given user is an anonymous (logged out) user.
579
     * @param User|string $user User object or username as a string.
580
     * @return bool
581
     */
582 1
    public function isUserAnon($user): bool
583
    {
584 1
        if ($user instanceof User) {
585 1
            $username = $user->getUsername();
586
        } else {
587 1
            $username = $user;
588
        }
589
590 1
        return (bool)filter_var($username, FILTER_VALIDATE_IP);
591
    }
592
593
    /**
594
     * Helper to properly translate a namespace name.
595
     * @param int|string $namespace Namespace key as a string or ID.
596
     * @param string[] $namespaces List of available namespaces as retrieved from Project::getNamespaces().
597
     * @return string Namespace name
598
     */
599
    public function nsName($namespace, array $namespaces): string
600
    {
601
        if ('all' === $namespace) {
602
            return $this->i18n->msg('all');
603
        } elseif ('0' === $namespace || 0 === $namespace || 'Main' === $namespace) {
604
            return $this->i18n->msg('mainspace');
605
        } else {
606
            return $namespaces[$namespace] ?? $this->i18n->msg('unknown');
607
        }
608
    }
609
610
    /**
611
     * Given a page title and namespace, generate the full page title.
612
     * @param string $title
613
     * @param int $namespace
614
     * @param array $namespaces
615
     * @return string
616
     */
617
    public function titleWithNs(string $title, int $namespace, array $namespaces): string
618
    {
619
        if (0 === $namespace) {
620
            return $title;
621
        }
622
        return $this->nsName($namespace, $namespaces).':'.$title;
623
    }
624
625
    /**
626
     * Format a given number as a diff, colouring it green if it's positive, red if negative, gary if zero
627
     * @param int $size Diff size
628
     * @return string Markup with formatted number
629
     */
630 1
    public function diffFormat(int $size): string
631
    {
632 1
        if ($size < 0) {
633 1
            $class = 'diff-neg';
634 1
        } elseif ($size > 0) {
635 1
            $class = 'diff-pos';
636
        } else {
637 1
            $class = 'diff-zero';
638
        }
639
640 1
        $size = $this->numberFormat($size);
641
642 1
        return "<span class='$class'".
643 1
            ($this->i18n->isRTL() ? " dir='rtl'" : '').
644 1
            ">$size</span>";
645
    }
646
647
    /**
648
     * Format a time duration as humanized string.
649
     * @param int $seconds Number of seconds.
650
     * @param bool $translate Used for unit testing. Set to false to return
651
     *   the value and i18n key, instead of the actual translation.
652
     * @return string|mixed[] Examples: '30 seconds', '2 minutes', '15 hours', '500 days',
653
     *   or [30, 'num-seconds'] (etc.) if $translate is false.
654
     */
655 1
    public function formatDuration(int $seconds, bool $translate = true)
656
    {
657 1
        [$val, $key] = $this->getDurationMessageKey($seconds);
658
659 1
        if ($translate) {
660
            return $this->numberFormat($val).' '.$this->i18n->msg("num-$key", [$val]);
661
        } else {
662 1
            return [$this->numberFormat($val), "num-$key"];
663
        }
664
    }
665
666
    /**
667
     * Given a time duration in seconds, generate a i18n message key and value.
668
     * @param int $seconds Number of seconds.
669
     * @return array<integer|string> [int - message value, string - message key]
670
     */
671 1
    private function getDurationMessageKey(int $seconds)
672
    {
673
        /** @var int $val Value to show in message */
674 1
        $val = $seconds;
675
676
        /** @var string $key Unit of time, used in the key for the i18n message */
677 1
        $key = 'seconds';
678
679 1
        if ($seconds >= 86400) {
680
            // Over a day
681 1
            $val = (int) floor($seconds / 86400);
682 1
            $key = 'days';
683 1
        } elseif ($seconds >= 3600) {
684
            // Over an hour, less than a day
685 1
            $val = (int) floor($seconds / 3600);
686 1
            $key = 'hours';
687 1
        } elseif ($seconds >= 60) {
688
            // Over a minute, less than an hour
689 1
            $val = (int) floor($seconds / 60);
690 1
            $key = 'minutes';
691
        }
692
693 1
        return [$val, $key];
694
    }
695
696
    /**
697
     * Build URL query string from given params.
698
     * @param string[] $params
699
     * @return string
700
     */
701 1
    public function buildQuery(array $params): string
702
    {
703 1
        return is_array($params) ? http_build_query($params) : '';
0 ignored issues
show
introduced by
The condition is_array($params) is always true.
Loading history...
704
    }
705
706
    /**
707
     * Shorthand to get the current request from the request stack.
708
     * @return \Symfony\Component\HttpFoundation\Request
709
     * There is no request stack in the tests.
710
     * @codeCoverageIgnore
711
     */
712
    private function getRequest(): \Symfony\Component\HttpFoundation\Request
713
    {
714
        return $this->container->get('request_stack')->getCurrentRequest();
715
    }
716
}
717