Passed
Push — master ( a7ac5a...a9e302 )
by Mark
12:16 queued 15s
created

Duration   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 9
eloc 60
c 1
b 0
f 0
dl 0
loc 147
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A mapFormatBlocks() 0 18 4
A format() 0 3 1
A __construct() 0 15 4
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Style\NumberFormat\Wizard;
4
5
class Duration extends DateTimeWizard
6
{
7
    public const DAYS_DURATION = 'd';
8
9
    /**
10
     * Hours as a duration (can exceed 24), e.g. 29.
11
     */
12
    public const HOURS_DURATION = '[h]';
13
14
    /**
15
     * Hours without a leading zero, e.g. 9.
16
     */
17
    public const HOURS_SHORT = 'h';
18
19
    /**
20
     * Hours with a leading zero, e.g. 09.
21
     */
22
    public const HOURS_LONG = 'hh';
23
24
    /**
25
     * Minutes as a duration (can exceed 60), e.g. 109.
26
     */
27
    public const MINUTES_DURATION = '[m]';
28
29
    /**
30
     * Minutes without a leading zero, e.g. 5.
31
     */
32
    public const MINUTES_SHORT = 'm';
33
34
    /**
35
     * Minutes with a leading zero, e.g. 05.
36
     */
37
    public const MINUTES_LONG = 'mm';
38
39
    /**
40
     * Seconds as a duration (can exceed 60), e.g. 129.
41
     */
42
    public const SECONDS_DURATION = '[s]';
43
44
    /**
45
     * Seconds without a leading zero, e.g. 2.
46
     */
47
    public const SECONDS_SHORT = 's';
48
49
    /**
50
     * Seconds with a leading zero, e.g. 02.
51
     */
52
    public const SECONDS_LONG = 'ss';
53
54
    protected const DURATION_BLOCKS = [
55
        self::DAYS_DURATION,
56
        self::HOURS_DURATION,
57
        self::HOURS_LONG,
58
        self::HOURS_SHORT,
59
        self::MINUTES_DURATION,
60
        self::MINUTES_LONG,
61
        self::MINUTES_SHORT,
62
        self::SECONDS_DURATION,
63
        self::SECONDS_LONG,
64
        self::SECONDS_SHORT,
65
    ];
66
67
    protected const DURATION_MASKS = [
68
        self::DAYS_DURATION => self::DAYS_DURATION,
69
        self::HOURS_DURATION => self::HOURS_SHORT,
70
        self::MINUTES_DURATION => self::MINUTES_LONG,
71
        self::SECONDS_DURATION => self::SECONDS_LONG,
72
    ];
73
74
    protected const DURATION_DEFAULTS = [
75
        self::HOURS_LONG => self::HOURS_DURATION,
76
        self::HOURS_SHORT => self::HOURS_DURATION,
77
        self::MINUTES_LONG => self::MINUTES_DURATION,
78
        self::MINUTES_SHORT => self::MINUTES_DURATION,
79
        self::SECONDS_LONG => self::SECONDS_DURATION,
80
        self::SECONDS_SHORT => self::SECONDS_DURATION,
81
    ];
82
83
    public const SEPARATOR_COLON = ':';
84
    public const SEPARATOR_SPACE_NONBREAKING = "\u{a0}";
85
    public const SEPARATOR_SPACE = ' ';
86
87
    public const DURATION_DEFAULT = [
88
        self::HOURS_DURATION,
89
        self::MINUTES_LONG,
90
        self::SECONDS_LONG,
91
    ];
92
93
    /**
94
     * @var string[]
95
     */
96
    protected array $separators;
97
98
    /**
99
     * @var string[]
100
     */
101
    protected array $formatBlocks;
102
103
    protected bool $durationIsSet = false;
104
105
    /**
106
     * @param null|string|string[] $separators
107
     *        If you want to use the same separator for all format blocks, then it can be passed as a string literal;
108
     *           if you wish to use different separators, then they should be passed as an array.
109
     *        If you want to use only a single format block, then pass a null as the separator argument
110
     */
111
    public function __construct($separators = self::SEPARATOR_COLON, string ...$formatBlocks)
112
    {
113
        $separators ??= self::SEPARATOR_COLON;
114
        $formatBlocks = (count($formatBlocks) === 0) ? self::DURATION_DEFAULT : $formatBlocks;
115
116
        $this->separators = $this->padSeparatorArray(
117
            is_array($separators) ? $separators : [$separators],
118
            count($formatBlocks) - 1
119
        );
120
        $this->formatBlocks = array_map([$this, 'mapFormatBlocks'], $formatBlocks);
121
122
        if ($this->durationIsSet === false) {
123
            // We need at least one duration mask, so if none has been set we change the first mask element
124
            //    to a duration.
125
            $this->formatBlocks[0] = self::DURATION_DEFAULTS[mb_strtolower($this->formatBlocks[0])];
126
        }
127
    }
128
129
    private function mapFormatBlocks(string $value): string
130
    {
131
        // Any duration masking codes are returned as lower case values
132
        if (in_array(mb_strtolower($value), self::DURATION_BLOCKS, true)) {
133
            if (array_key_exists(mb_strtolower($value), self::DURATION_MASKS)) {
134
                if ($this->durationIsSet) {
135
                    // We should only have a single duration mask, the first defined in the mask set,
136
                    //    so convert any additional duration masks to standard time masks.
137
                    $value = self::DURATION_MASKS[mb_strtolower($value)];
138
                }
139
                $this->durationIsSet = true;
140
            }
141
142
            return mb_strtolower($value);
143
        }
144
145
        // Wrap any string literals in quotes, so that they're clearly defined as string literals
146
        return $this->wrapLiteral($value);
147
    }
148
149
    public function format(): string
150
    {
151
        return implode('', array_map([$this, 'intersperse'], $this->formatBlocks, $this->separators));
152
    }
153
}
154