Completed
Push — master ( daefc0...7c7725 )
by MusikAnimal
43:00
created

AppExtension::numberFormat()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 2
1
<?php
2
/**
3
 * This file contains only the AppExtension class.
4
 */
5
6
namespace AppBundle\Twig;
7
8
use Xtools\ProjectRepository;
9
use Xtools\User;
10
use NumberFormatter;
11
use IntlDateFormatter;
12
use DateTime;
13
14
/**
15
 * Twig functions and filters for XTools.
16
 */
17
class AppExtension extends Extension
18
{
19
    /** @var NumberFormatter Instance of NumberFormatter class, used in localizing numbers. */
20
    protected $numFormatter;
21
22
    /** @var IntlDateFormatter Instance of IntlDateFormatter class, used in localizing dates. */
23
    protected $dateFormatter;
24
25
    /** @var float Duration of the current HTTP request in seconds. */
26
    protected $requestTime;
27
28
    /**
29
     * Get the name of this extension.
30
     * @return string
31
     */
32
    public function getName()
33
    {
34
        return 'app_extension';
35
    }
36
37
    /*********************************** FUNCTIONS ***********************************/
38
39
    /**
40
     * Get all functions that this class provides.
41
     * @return array
42
     */
43
    public function getFunctions()
44
    {
45
        $options = ['is_safe' => ['html']];
46
        return [
47
            new \Twig_SimpleFunction('request_time', [ $this, 'requestTime' ], $options),
48
            new \Twig_SimpleFunction('memory_usage', [ $this, 'requestMemory' ], $options),
49
            new \Twig_SimpleFunction('year', [ $this, 'generateYear' ], $options),
50
            new \Twig_SimpleFunction('msgPrintExists', [ $this, 'intuitionMessagePrintExists' ], $options),
51
            new \Twig_SimpleFunction('msgExists', [ $this, 'intuitionMessageExists' ], $options),
52
            new \Twig_SimpleFunction('msg', [ $this, 'intuitionMessage' ], $options),
53
            new \Twig_SimpleFunction('lang', [ $this, 'getLang' ], $options),
54
            new \Twig_SimpleFunction('langName', [ $this, 'getLangName' ], $options),
55
            new \Twig_SimpleFunction('allLangs', [ $this, 'getAllLangs' ]),
56
            new \Twig_SimpleFunction('isRTL', [ $this, 'intuitionIsRTL' ]),
57
            new \Twig_SimpleFunction('isRTLLang', [ $this, 'intuitionIsRTLLang' ]),
58
            new \Twig_SimpleFunction('shortHash', [ $this, 'gitShortHash' ]),
59
            new \Twig_SimpleFunction('hash', [ $this, 'gitHash' ]),
60
            new \Twig_SimpleFunction('releaseDate', [ $this, 'gitDate' ]),
61
            new \Twig_SimpleFunction('enabled', [ $this, 'tabEnabled' ]),
62
            new \Twig_SimpleFunction('tools', [ $this, 'allTools' ]),
63
            new \Twig_SimpleFunction('color', [ $this, 'getColorList' ]),
64
            new \Twig_SimpleFunction('chartColor', [ $this, 'chartColor' ]),
65
            new \Twig_SimpleFunction('isSingleWiki', [ $this, 'isSingleWiki' ]),
66
            new \Twig_SimpleFunction('getReplagThreshold', [ $this, 'getReplagThreshold' ]),
67
            new \Twig_SimpleFunction('loadStylesheetsFromCDN', [ $this, 'loadStylesheetsFromCDN' ]),
68
            new \Twig_SimpleFunction('isWMFLabs', [ $this, 'isWMFLabs' ]),
69
            new \Twig_SimpleFunction('replag', [ $this, 'replag' ]),
70
            new \Twig_SimpleFunction('link', [ $this, 'link' ]),
71
            new \Twig_SimpleFunction('quote', [ $this, 'quote' ]),
72
            new \Twig_SimpleFunction('bugReportURL', [ $this, 'bugReportURL' ]),
73
            new \Twig_SimpleFunction('logged_in_user', [$this, 'functionLoggedInUser']),
74
            new \Twig_SimpleFunction('isUserAnon', [$this, 'isUserAnon']),
75
            new \Twig_SimpleFunction('nsName', [$this, 'nsName']),
76
            new \Twig_SimpleFunction('formatDuration', [$this, 'formatDuration']),
77
            new \Twig_SimpleFunction('numberFormat', [$this, 'numberFormat']),
78
        ];
79
    }
80
81
    /**
82
     * Get the duration of the current HTTP request in seconds.
83
     * @return string
84
     */
85
    public function requestTime()
86
    {
87
        if (!isset($this->requestTime)) {
88
            $this->requestTime = microtime(true) - $this->getCurrentRequest()->server->get('REQUEST_TIME_FLOAT');
89
        }
90
91
        return $this->requestTime;
92
    }
93
94
    /**
95
     * Get the formatted real memory usage.
96
     * @return float
97
     */
98
    public function requestMemory()
99
    {
100
        $mem = memory_get_usage(false);
101
        $div = pow(1024, 2);
102
        return $mem / $div;
103
    }
104
105
    /**
106
     * Get the current year.
107
     * @return string
108
     */
109
    public function generateYear()
110
    {
111
        return date('Y');
112
    }
113
114
    /**
115
     * See if a given i18n message exists.
116
     * @TODO: refactor all intuition stuff so it can be used anywhere
117
     * @param string $message The message.
118
     * @param array $vars
119
     * @return bool
120
     */
121
    public function intuitionMessageExists($message = '', $vars = [])
122
    {
123
        return $this->getIntuition()->msgExists($message, array_merge(
124
            [
125
                'domain' => 'xtools'
126
            ],
127
            [
128
                'variables' => $vars
129
            ]
130
        ));
131
    }
132
133
    /**
134
     * Get an i18n message if it exists, otherwise just get the message key.
135
     * @param string $message
136
     * @param array $vars
137
     * @return mixed|null|string
138
     */
139
    public function intuitionMessagePrintExists($message = "", $vars = [])
140
    {
141
        if (is_array($message)) {
142
            $vars = $message;
143
            $message = $message[0];
144
            $vars = array_slice($vars, 1);
145
        }
146
        if ($this->intuitionMessageExists($message, $vars)) {
147
            return $this->intuitionMessage($message, $vars);
148
        } else {
149
            return $message;
150
        }
151
    }
152
153
    /**
154
     * Get an i18n message.
155
     * @param string $message
156
     * @param array $vars
157
     * @return mixed|null|string
158
     */
159
    public function intuitionMessage($message = "", $vars = [])
160
    {
161
        return $this->getIntuition()->msg($message, [ "domain" => "xtools", "variables" => $vars ]);
162
    }
163
164
    /**
165
     * Get the current language code.
166
     * @return string
167
     */
168
    public function getLang()
169
    {
170
        return $this->getIntuition()->getLang();
171
    }
172
173
    /**
174
     * Get the current language name (defaults to 'English').
175
     * @return string
176
     */
177
    public function getLangName()
178
    {
179
        return in_array(ucfirst($this->getIntuition()->getLangName()), $this->getAllLangs())
180
            ? $this->getIntuition()->getLangName()
181
            : 'English';
182
    }
183
184
    /**
185
     * Get all available languages in the i18n directory
186
     * @return array Associative array of langKey => langName
187
     */
188
    public function getAllLangs()
189
    {
190
        $messageFiles = glob($this->container->getParameter("kernel.root_dir") . '/../i18n/*.json');
191
192
        $languages = array_values(array_unique(array_map(
193
            function ($filename) {
194
                return basename($filename, '.json');
195
            },
196
            $messageFiles
197
        )));
198
199
        $availableLanguages = [];
200
201
        foreach ($languages as $lang) {
202
            $availableLanguages[$lang] = ucfirst($this->getIntuition()->getLangName($lang));
203
        }
204
        asort($availableLanguages);
205
206
        return $availableLanguages;
207
    }
208
209
    /**
210
     * Whether the current language is right-to-left.
211
     * @return bool
212
     */
213
    public function intuitionIsRTL()
214
    {
215
        return $this->getIntuition()->isRTL($this->getIntuition()->getLang());
216
    }
217
218
    /**
219
     * Whether the given language is right-to-left.
220
     * @param string $lang The language code.
221
     * @return bool
222
     */
223
    public function intuitionIsRTLLang($lang)
224
    {
225
        return $this->getIntuition()->isRTL($lang);
226
    }
227
228
    /**
229
     * Get the short hash of the currently checked-out Git commit.
230
     * @return string
231
     */
232
    public function gitShortHash()
233
    {
234
        return exec("git rev-parse --short HEAD");
235
    }
236
237
    /**
238
     * Get the full hash of the currently checkout-out Git commit.
239
     * @return string
240
     */
241
    public function gitHash()
242
    {
243
        return exec("git rev-parse HEAD");
244
    }
245
246
    /**
247
     * Get the date of the HEAD commit.
248
     * @return string
249
     */
250
    public function gitDate()
251
    {
252
        $date = new DateTime(exec('git show -s --format=%ci'));
253
        return $date->format('Y-m-d');
254
    }
255
256
    /**
257
     * Check whether a given tool is enabled.
258
     * @param string $tool The short name of the tool.
259
     * @return bool
260
     */
261 View Code Duplication
    public function tabEnabled($tool = "index")
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262
    {
263
        $param = false;
264
        if ($this->container->hasParameter("enable.$tool")) {
265
            $param = boolval($this->container->getParameter("enable.$tool"));
266
        }
267
        return $param;
268
    }
269
270
    /**
271
     * Get a list of the short names of all tools.
272
     * @return string[]
273
     */
274
    public function allTools()
275
    {
276
        $retVal = [];
277
        if ($this->container->hasParameter("tools")) {
278
            $retVal = $this->container->getParameter("tools");
279
        }
280
        return $retVal;
281
    }
282
283
    /**
284
     * Get a list of namespace colours (one or all).
285
     * @param bool $num The NS ID to get.
286
     * @return string[]|string Indexed by namespace ID.
287
     */
288
    public static function getColorList($num = false)
289
    {
290
        $colors = [
291
            0 => '#FF5555',
292
            1 => '#55FF55',
293
            2 => '#FFEE22',
294
            3 => '#FF55FF',
295
            4 => '#5555FF',
296
            5 => '#55FFFF',
297
            6 => '#C00000',
298
            7 => '#0000C0',
299
            8 => '#008800',
300
            9 => '#00C0C0',
301
            10 => '#FFAFAF',
302
            11 => '#808080',
303
            12 => '#00C000',
304
            13 => '#404040',
305
            14 => '#C0C000',
306
            15 => '#C000C0',
307
            90 => '#991100',
308
            91 => '#99FF00',
309
            92 => '#000000',
310
            93 => '#777777',
311
            100 => '#75A3D1',
312
            101 => '#A679D2',
313
            102 => '#660000',
314
            103 => '#000066',
315
            104 => '#FAFFAF',
316
            105 => '#408345',
317
            106 => '#5c8d20',
318
            107 => '#e1711d',
319
            108 => '#94ef2b',
320
            109 => '#756a4a',
321
            110 => '#6f1dab',
322
            111 => '#301e30',
323
            112 => '#5c9d96',
324
            113 => '#a8cd8c',
325
            114 => '#f2b3f1',
326
            115 => '#9b5828',
327
            116 => '#002288',
328
            117 => '#0000CC',
329
            118 => '#99FFFF',
330
            119 => '#99BBFF',
331
            120 => '#FF99FF',
332
            121 => '#CCFFFF',
333
            122 => '#CCFF00',
334
            123 => '#CCFFCC',
335
            200 => '#33FF00',
336
            201 => '#669900',
337
            202 => '#666666',
338
            203 => '#999999',
339
            204 => '#FFFFCC',
340
            205 => '#FF00CC',
341
            206 => '#FFFF00',
342
            207 => '#FFCC00',
343
            208 => '#FF0000',
344
            209 => '#FF6600',
345
            250 => '#6633CC',
346
            251 => '#6611AA',
347
            252 => '#66FF99',
348
            253 => '#66FF66',
349
            446 => '#06DCFB',
350
            447 => '#892EE4',
351
            460 => '#99FF66',
352
            461 => '#99CC66',
353
            470 => '#CCCC33',
354
            471 => '#CCFF33',
355
            480 => '#6699FF',
356
            481 => '#66FFFF',
357
            484 => '#07C8D6',
358
            485 => '#2AF1FF',
359
            486 => '#79CB21',
360
            487 => '#80D822',
361
            490 => '#995500',
362
            491 => '#998800',
363
            710 => '#FFCECE',
364
            711 => '#FFC8F2',
365
            828 => '#F7DE00',
366
            829 => '#BABA21',
367
            866 => '#FFFFFF',
368
            867 => '#FFCCFF',
369
            1198 => '#FF34B3',
370
            1199 => '#8B1C62',
371
            2300 => '#A900B8',
372
            2301 => '#C93ED6',
373
            2302 => '#8A09C1',
374
            2303 => '#974AB8',
375
            2600 => '#000000',
376
        ];
377
378
        if ($num === false) {
379
            return $colors;
380
        } elseif (isset($colors[$num])) {
381
            return $colors[$num];
382
        } else {
383
            // Default to grey.
384
            return '#CCC';
385
        }
386
    }
387
388
    /**
389
     * Get color-blind friendly colors for use in charts
390
     * @param  Integer $num Index of color
391
     * @return String RGBA color (so you can more easily adjust the opacity)
392
     */
393
    public function chartColor($num)
394
    {
395
        $colors = [
396
            'rgba(171, 212, 235, 1)',
397
            'rgba(178, 223, 138, 1)',
398
            'rgba(251, 154, 153, 1)',
399
            'rgba(253, 191, 111, 1)',
400
            'rgba(202, 178, 214, 1)',
401
            'rgba(207, 182, 128, 1)',
402
            'rgba(141, 211, 199, 1)',
403
            'rgba(252, 205, 229, 1)',
404
            'rgba(255, 247, 161, 1)',
405
            'rgba(217, 217, 217, 1)',
406
        ];
407
408
        return $colors[$num % count($colors)];
409
    }
410
411
    /**
412
     * Whether XTools is running in single-project mode.
413
     * @return bool
414
     */
415 View Code Duplication
    public function isSingleWiki()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
416
    {
417
        $param = true;
418
        if ($this->container->hasParameter('app.single_wiki')) {
419
            $param = boolval($this->container->getParameter('app.single_wiki'));
420
        }
421
        return $param;
422
    }
423
424
    /**
425
     * Get the database replication-lag threshold.
426
     * @return int
427
     */
428
    public function getReplagThreshold()
429
    {
430
        $param = 30;
431
        if ($this->container->hasParameter('app.replag_threshold')) {
432
            $param = $this->container->getParameter('app.replag_threshold');
433
        };
434
        return $param;
435
    }
436
437
    /**
438
     * Whether we should load stylesheets from external CDNs or not.
439
     * @return bool
440
     */
441 View Code Duplication
    public function loadStylesheetsFromCDN()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
442
    {
443
        $param = false;
444
        if ($this->container->hasParameter('app.load_stylesheets_from_cdn')) {
445
            $param = boolval($this->container->getParameter('app.load_stylesheets_from_cdn'));
446
        }
447
        return $param;
448
    }
449
450
    /**
451
     * Whether XTools is running in WMF Labs mode.
452
     * @return bool
453
     */
454 View Code Duplication
    public function isWMFLabs()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
455
    {
456
        $param = false;
457
        if ($this->container->hasParameter('app.is_labs')) {
458
            $param = boolval($this->container->getParameter('app.is_labs'));
459
        }
460
        return $param;
461
    }
462
463
    /**
464
     * The current replication lag.
465
     * @return int
466
     */
467
    public function replag()
468
    {
469
        $retVal = 0;
470
471
        if ($this->isWMFLabs()) {
472
            $project = $this->getCurrentRequest()->get('project');
473
474
            if (!isset($project)) {
475
                $project = 'enwiki';
476
            }
477
478
            $dbName = ProjectRepository::getProject($project, $this->container)
479
                ->getDatabaseName();
480
481
            $stmt = "SELECT lag FROM `heartbeat_p`.`heartbeat` h
482
            RIGHT JOIN `meta_p`.`wiki` w ON concat(h.shard, \".labsdb\")=w.slice
483
            WHERE dbname LIKE :project LIMIT 1";
484
485
            $conn = $this->container->get('doctrine')->getManager('replicas')->getConnection();
486
487
            // Prepare the query and execute
488
            $resultQuery = $conn->prepare($stmt);
489
            $resultQuery->bindParam('project', $dbName);
490
            $resultQuery->execute();
491
492
            if ($resultQuery->errorCode() == 0) {
493
                $results = $resultQuery->fetchAll();
494
495
                if (isset($results[0]['lag'])) {
496
                    $retVal = $results[0]['lag'];
497
                }
498
            }
499
        }
500
501
        return $retVal;
502
    }
503
504
    /**
505
     * Get a random quote for the footer
506
     * @return string
507
     */
508
    public function quote()
509
    {
510
        // Don't show if bash is turned off, but always show for Labs
511
        // (so quote is in footer but not in nav).
512
        $isLabs = $this->container->getParameter('app.is_labs');
513
        if (!$isLabs && !$this->container->getParameter('enable.bash')) {
514
            return '';
515
        }
516
        $quotes = $this->container->getParameter('quotes');
517
        $id = array_rand($quotes);
518
        return $quotes[$id];
519
    }
520
521
    /**
522
     * Get the currently logged in user's details.
523
     * @return string[]
524
     */
525
    public function functionLoggedInUser()
526
    {
527
        return $this->container->get('session')->get('logged_in_user');
528
    }
529
530
531
    /*********************************** FILTERS ***********************************/
532
533
    /**
534
     * Get all filters for this extension.
535
     * @return array
536
     */
537
    public function getFilters()
538
    {
539
        return [
540
            new \Twig_SimpleFilter('capitalize_first', [ $this, 'capitalizeFirst' ]),
541
            new \Twig_SimpleFilter('percent_format', [ $this, 'percentFormat' ]),
542
            new \Twig_SimpleFilter('diff_format', [ $this, 'diffFormat' ], [ 'is_safe' => [ 'html' ] ]),
543
            new \Twig_SimpleFilter('num_format', [$this, 'numberFormat']),
544
            new \Twig_SimpleFilter('date_format', [$this, 'dateFormat']),
545
        ];
546
    }
547
548
    /**
549
     * Format a number based on language settings.
550
     * @param  int|float $number
551
     * @param  int $decimals Number of decimals to format to.
552
     * @return string
553
     */
554
    public function numberFormat($number, $decimals = 0)
555
    {
556
        if (!isset($this->numFormatter)) {
557
            $lang = $this->getIntuition()->getLang();
558
            $this->numFormatter = new NumberFormatter($lang, NumberFormatter::DECIMAL);
559
        }
560
561
        // Get separator symbols.
562
        $decimal = $this->numFormatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
563
        $thousands = $this->numFormatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
564
565
        $formatted = number_format($number, $decimals, $decimal, $thousands);
566
567
        // Remove trailing .0's (e.g. 40.00 -> 40).
568
        return preg_replace("/\\".$decimal."0+$/", '', $formatted);
569
    }
570
571
    /**
572
     * Localize the given date based on language settings.
573
     * @param  string|DateTime $datetime
574
     * @return string
575
     */
576
    public function dateFormat($datetime)
577
    {
578
        if (!isset($this->dateFormatter)) {
579
            $this->dateFormatter = new IntlDateFormatter(
580
                $this->getIntuition()->getLang(),
581
                IntlDateFormatter::SHORT,
582
                IntlDateFormatter::SHORT
583
            );
584
        }
585
586
        if (is_string($datetime)) {
587
            $datetime = new DateTime($datetime);
588
        }
589
590
        return $this->dateFormatter->format($datetime);
591
    }
592
593
    /**
594
     * Mysteriously missing Twig helper to capitalize only the first character.
595
     * E.g. used for table headings for translated messages
596
     * @param  string $str The string
597
     * @return string      The string, capitalized
598
     */
599
    public function capitalizeFirst($str)
600
    {
601
        return ucfirst($str);
602
    }
603
604
    /**
605
     * Format a given number or fraction as a percentage.
606
     * @param  number  $numerator   Numerator or single fraction if denominator is ommitted.
607
     * @param  number  $denominator Denominator.
608
     * @param  integer $precision   Number of decimal places to show.
609
     * @return string               Formatted percentage.
610
     */
611
    public function percentFormat($numerator, $denominator = null, $precision = 1)
612
    {
613
        if (!$denominator) {
614
            $quotient = $numerator;
615
        } else {
616
            $quotient = ( $numerator / $denominator ) * 100;
617
        }
618
619
        return $this->numberFormat($quotient, $precision) . '%';
620
    }
621
622
    /**
623
     * Helper to return whether the given user is an anonymous (logged out) user.
624
     * @param  User|string $user User object or username as a string.
625
     * @return bool
626
     */
627
    public function isUserAnon($user)
628
    {
629
        if ($user instanceof User) {
630
            $username = $user.username;
631
        } else {
632
            $username = $user;
633
        }
634
635
        return filter_var($username, FILTER_VALIDATE_IP);
636
    }
637
638
    /**
639
     * Helper to properly translate a namespace name
640
     * @param  int|string $namespace Namespace key as a string or ID
641
     * @param  array      $namespaces List of available namespaces
642
     *                                as retrieved from Project::getNamespaces
643
     * @return string Namespace name
644
     */
645
    public function nsName($namespace, $namespaces)
646
    {
647
        if ($namespace === 'all') {
648
            return $this->getIntuition()->msg('all');
649
        } elseif ($namespace === '0' || $namespace === 0 || $namespace === 'Main') {
650
            return $this->getIntuition()->msg('mainspace');
651
        } else {
652
            return $namespaces[$namespace];
653
        }
654
    }
655
656
    /**
657
     * Format a given number as a diff, colouring it green if it's postive, red if negative, gary if zero
658
     * @param  number $size Diff size
659
     * @return string       Markup with formatted number
660
     */
661
    public function diffFormat($size)
662
    {
663
        if ($size < 0) {
664
            $class = 'diff-neg';
665
        } elseif ($size > 0) {
666
            $class = 'diff-pos';
667
        } else {
668
            $class = 'diff-zero';
669
        }
670
671
        $size = $this->numberFormat($size);
672
673
        return "<span class='$class'>$size</span>";
674
    }
675
676
    /**
677
     * Format a time duration as humanized string.
678
     * @param int $seconds Number of seconds
679
     * @param bool $translate Used for unit testing. Set to false to return
680
     *   the value and i18n key, instead of the actual translation.
681
     * @return string|array Examples: '30 seconds', '2 minutes', '15 hours', '500 days',
682
     *   or [30, 'num-seconds'] (etc.) if $translate is true
683
     */
684
    public function formatDuration($seconds, $translate = true)
685
    {
686
        /** @var int Value to show in message */
687
        $val = $seconds;
688
689
        /** @var string Unit of time, used in the key for the i18n message */
690
        $key = 'seconds';
691
692
        if ($seconds >= 86400) {
693
            // Over a day
694
            $val = (int) floor($seconds / 86400);
695
            $key = 'days';
696
        } elseif ($seconds >= 3600) {
697
            // Over an hour, less than a day
698
            $val = (int) floor($seconds / 3600);
699
            $key = 'hours';
700
        } elseif ($seconds >= 60) {
701
            // Over a minute, less than an hour
702
            $val = (int) floor($seconds / 60);
703
            $key = 'minutes';
704
        }
705
706
        if ($translate) {
707
            return $this->numberFormat($val) . ' ' . $this->intuitionMessage("num-$key", [$val]);
708
        } else {
709
            return [$this->numberFormat($val), "num-$key"];
710
        }
711
    }
712
}
713