Failed Conditions
Pull Request — master (#4420)
by Owen
15:03
created

Helpers::forceArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 1
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 6
rs 10
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
4
5
use DateTime;
6
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
7
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
8
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
9
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
10
11
class Helpers
12
{
13
    /**
14
     * Identify if a year is a leap year or not.
15
     *
16
     * @param int|string $year The year to test
17
     *
18
     * @return bool TRUE if the year is a leap year, otherwise FALSE
19
     */
20 186
    public static function isLeapYear(int|string $year): bool
21
    {
22 186
        $year = (int) $year;
23
24 186
        return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
25
    }
26
27
    /**
28
     * getDateValue.
29
     *
30
     * @return float Excel date/time serial value
31
     */
32 2383
    public static function getDateValue(mixed $dateValue, bool $allowBool = true): float
33
    {
34 2383
        if (is_object($dateValue)) {
35 3
            $retval = SharedDateHelper::PHPToExcel($dateValue);
36 3
            if (is_bool($retval)) {
37
                throw new Exception(ExcelError::VALUE());
38
            }
39
40 3
            return $retval;
41
        }
42
43 2380
        self::nullFalseTrueToNumber($dateValue, $allowBool);
44 2380
        if (!is_numeric($dateValue)) {
45 2077
            $saveReturnDateType = Functions::getReturnDateType();
46 2077
            Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
47 2077
            if (is_string($dateValue)) {
48 2077
                $dateValue = DateValue::fromString($dateValue);
49 2077
            }
50 138
            Functions::setReturnDateType($saveReturnDateType);
51
            if (!is_numeric($dateValue)) {
52
                throw new Exception(ExcelError::VALUE());
53 2284
            }
54 20
        }
55
        if ($dateValue < 0 && Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
56
            throw new Exception(ExcelError::NAN());
57 2264
        }
58
59
        return (float) $dateValue;
60
    }
61
62
    /**
63
     * getTimeValue.
64
     *
65 54
     * @return float|string Excel date/time serial value, or string if error
66
     */
67 54
    public static function getTimeValue(string $timeValue): string|float
68 54
    {
69
        $saveReturnDateType = Functions::getReturnDateType();
70 54
        Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
71 54
        /** @var float|string $timeValue */
72
        $timeValue = TimeValue::fromString($timeValue);
73 54
        Functions::setReturnDateType($saveReturnDateType);
74
75
        return $timeValue;
76
    }
77
78
    /**
79 114
     * Adjust date by given months.
80
     *
81
     * @param float|int $dateValue date to be adjusted
82 114
     */
83 114
    public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime
84 114
    {
85
        // Execute function
86 114
        $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
87 114
        $oMonth = (int) $PHPDateObject->format('m');
88 68
        $oYear = (int) $PHPDateObject->format('Y');
89
90 114
        $adjustmentMonthsString = (string) $adjustmentMonths;
91 97
        if ($adjustmentMonths > 0) {
92
            $adjustmentMonthsString = '+' . $adjustmentMonths;
93 114
        }
94 114
        if ($adjustmentMonths != 0) {
95
            $PHPDateObject->modify($adjustmentMonthsString . ' months');
96 114
        }
97 114
        $nMonth = (int) $PHPDateObject->format('m');
98 16
        $nYear = (int) $PHPDateObject->format('Y');
99 16
100 16
        $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
101
        if ($monthDiff != $adjustmentMonths) {
102
            $adjustDays = (int) $PHPDateObject->format('d');
103 114
            $adjustDaysString = '-' . $adjustDays . ' days';
104
            $PHPDateObject->modify($adjustDaysString);
105
        }
106
107
        return $PHPDateObject;
108
    }
109 2231
110
    /**
111 2231
     * Help reduce perceived complexity of some tests.
112
     */
113
    public static function replaceIfEmpty(mixed &$value, mixed $altValue): void
114
    {
115
        $value = $value ?: $altValue;
116
    }
117 1639
118
    /**
119 1639
     * Adjust year in ambiguous situations.
120 133
     */
121 84
    public static function adjustYear(string $testVal1, string $testVal2, string &$testVal3): void
122 4
    {
123
        if (!is_numeric($testVal1) || $testVal1 < 31) {
124
            if (!is_numeric($testVal2) || $testVal2 < 12) {
125
                if (is_numeric($testVal3) && $testVal3 < 12) {
126
                    $testVal3 = (string) ($testVal3 + 2000);
127
                }
128
            }
129
        }
130
    }
131 2237
132
    /**
133 2237
     * Return result in one of three formats.
134 2237
     */
135 1
    public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false): DateTime|float|int
136 1
    {
137 1
        $retType = Functions::getReturnDateType();
138 1
        if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
139 1
            return new DateTime(
140 1
                $dateArray['year']
141 1
                . '-' . $dateArray['month']
142 1
                . '-' . $dateArray['day']
143
                . ' ' . $dateArray['hour']
144 2236
                . ':' . $dateArray['minute']
145 2236
                . ':' . $dateArray['second']
146 2236
            );
147 2236
        }
148 2236
        $excelDateValue
149 2236
            = SharedDateHelper::formattedPHPToExcel(
150 2236
                $dateArray['year'],
151 2236
                $dateArray['month'],
152 2236
                $dateArray['day'],
153 2236
                $dateArray['hour'],
154 2235
                $dateArray['minute'],
155
                $dateArray['second']
156
            );
157
        if ($retType === Functions::RETURNDATE_EXCEL) {
158 1
            return $noFrac ? floor($excelDateValue) : $excelDateValue;
159
        }
160
        // RETURNDATE_UNIX_TIMESTAMP)
161
162
        return SharedDateHelper::excelToTimestamp($excelDateValue);
163
    }
164 366
165
    /**
166 366
     * Return result in one of three formats.
167 366
     */
168 364
    public static function returnIn3FormatsFloat(float $excelDateValue): float|int|DateTime
169
    {
170 2
        $retType = Functions::getReturnDateType();
171 1
        if ($retType === Functions::RETURNDATE_EXCEL) {
172
            return $excelDateValue;
173
        }
174
        if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
175 1
            return SharedDateHelper::excelToTimestamp($excelDateValue);
176
        }
177
        // RETURNDATE_PHP_DATETIME_OBJECT
178
179
        return SharedDateHelper::excelToDateTimeObject($excelDateValue);
180
    }
181 114
182
    /**
183 114
     * Return result in one of three formats.
184 114
     */
185 2
    public static function returnIn3FormatsObject(DateTime $PHPDateObject): DateTime|float|int
186
    {
187 112
        $retType = Functions::getReturnDateType();
188 110
        if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
189
            return $PHPDateObject;
190
        }
191 2
        if ($retType === Functions::RETURNDATE_EXCEL) {
192 2
            return (float) SharedDateHelper::PHPToExcel($PHPDateObject);
193
        }
194 2
        // RETURNDATE_UNIX_TIMESTAMP
195
        $stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
196
        $stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
197 2527
198
        return SharedDateHelper::excelToTimestamp($stamp);
199 2527
    }
200 26
201
    private static function baseDate(): int
202 2501
    {
203 102
        if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
204
            return 0;
205
        }
206 2399
        if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904) {
207
            return 0;
208
        }
209
210
        return 1;
211
    }
212 2527
213
    /**
214 2527
     * Many functions accept null/false/true argument treated as 0/0/1.
215 2527
     */
216 2527
    public static function nullFalseTrueToNumber(mixed &$number, bool $allowBool = true): void
217 104
    {
218 2526
        $number = Functions::flattenSingleValue($number);
219 42
        $nullVal = self::baseDate();
220
        if ($number === null) {
221
            $number = $nullVal;
222
        } elseif ($allowBool && is_bool($number)) {
223
            $number = $nullVal + (int) $number;
224
        }
225
    }
226 783
227
    /**
228 783
     * Many functions accept null argument treated as 0.
229 783
     */
230 2
    public static function validateNumericNull(mixed $number): int|float
231
    {
232 781
        $number = Functions::flattenSingleValue($number);
233 755
        if ($number === null) {
234
            return 0;
235 26
        }
236 5
        if (is_int($number)) {
237
            return $number;
238
        }
239 21
        if (is_numeric($number)) {
240
            return (float) $number;
241
        }
242
243
        throw new Exception(ExcelError::VALUE());
244
    }
245
246
    /**
247 152
     * Many functions accept null/false/true argument treated as 0/0/1.
248
     *
249 152
     * @phpstan-assert float $number
250 18
     */
251
    public static function validateNotNegative(mixed $number): float
252 134
    {
253 125
        if (!is_numeric($number)) {
254
            throw new Exception(ExcelError::VALUE());
255
        }
256 9
        if ($number >= 0) {
257
            return (float) $number;
258
        }
259 621
260
        throw new Exception(ExcelError::NAN());
261 621
    }
262 621
263 86
    public static function silly1900(DateTime $PHPDateObject, string $mod = '-1 day'): void
264
    {
265
        $isoDate = $PHPDateObject->format('c');
266
        if ($isoDate < '1900-03-01') {
267 2418
            $PHPDateObject->modify($mod);
268
        }
269 2418
    }
270
271
    public static function dateParse(string $string): array
272 2418
    {
273
        return self::forceArray(date_parse($string));
274 2418
    }
275
276
    public static function dateParseSucceeded(array $dateArray): bool
277
    {
278
        return $dateArray['error_count'] === 0;
279
    }
280
281
    /**
282
     * Despite documentation, date_parse probably never returns false.
283 2418
     * Just in case, this routine helps guarantee it.
284
     *
285 2418
     * @param array|false $dateArray
286
     */
287
    private static function forceArray(array|bool $dateArray): array
288
    {
289
        return is_array($dateArray) ? $dateArray : ['error_count' => 1];
290
    }
291
292
    public static function floatOrInt(mixed $value): float|int
293
    {
294
        $result = Functions::scalar($value);
295
296
        return is_numeric($result) ? ($result + 0) : 0;
297
    }
298
}
299