Passed
Push — master ( 7f6d09...5bcfa0 )
by MusikAnimal
10:58
created

AppExtension::formatDuration()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

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