DateTypeTrait::extractDateParts()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 11
nc 2
nop 3
dl 0
loc 20
ccs 9
cts 9
cp 1
crap 2
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
namespace Mbright\Validation\Rule\Validate\MySql;
4
5
/**
6
 * Functions to parse a given string to extract year, month, and day parts.
7
 *
8
 * A regex pattern (excluding the surrounding /'s) must be provided that should match all valid delimiters that can
9
 * separate year, month, and day parts. Optionally, YYMMDD strings be sanitized to have a 4 digit year YYYYMMDD format.
10
 *
11
 * A null return indicates that the string could not be parsed into YYMMDD or YYYYMMDD.
12
 */
13
trait DateTypeTrait
14
{
15
    /**
16
     * Extract date parts from the given string.
17
     *
18
     * Will return a stdClass with 3 properties: year, month, day. Each property defaults to null. If the string cannot
19
     * be parsed, return null.
20
     *
21
     * @param string $dateString
22
     * @param string $delimiterPattern regex pattern to use when searching for delimiters (excluding the surrounding /'s
23
     * @param bool normalizeYear indicates if a two digit year should get normalized to a 4 digit year
0 ignored issues
show
Bug introduced by
The type Mbright\Validation\Rule\...ate\MySql\normalizeYear was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
     *
25
     * @return null|\stdClass
26
     */
27 63
    protected function extractDateParts(string $dateString, string $delimiterPattern, bool $normalizeYear): ?\stdClass
28
    {
29
        $extractedParts = (object) [
30 63
            'year' => null,
31
            'month' => null,
32
            'day' => null
33
        ];
34
35 63
        $sanitizedDateString = $this->sanitizeDateString($dateString, $delimiterPattern, $normalizeYear);
36
37 63
        if (is_null($sanitizedDateString)) {
38 9
            return null;
39
        }
40
41
        // extract the individual date parts for checkdate
42 54
        $extractedParts->year = substr($sanitizedDateString, 0, 4);
43 54
        $extractedParts->month = substr($sanitizedDateString, 4, 2);
44 54
        $extractedParts->day = substr($sanitizedDateString, 6, 2);
45
46 54
        return $extractedParts;
47
    }
48
49
    /**
50
     * Sanitizes the given dateString to YYYYMMDD.
51
     *
52
     * Returns null if the string cannot be sanitized.
53
     *
54
     * @param string $dateString
55
     * @param string $delimiterPattern regex pattern to use when searching for delimiters (excluding the surrounding /'s
56
     * @param bool normalizeYear indicates if a two digit year should get normalized to a 4 digit year
57
     *
58
     * @return null|string
59
     */
60 63
    private function sanitizeDateString(string $dateString, string $delimiterPattern, bool $normalizeYear): ?string
61
    {
62 63
        $sanitizedString = $this->handleDelimiters($dateString, $delimiterPattern);
63
64 63
        if ($sanitizedString && $normalizeYear) {
65 60
            $sanitizedString = $this->normalizeYear($sanitizedString);
66
67
            // More than 8 characters here means we don't have the format YYYYMMDD
68 60
            if (strlen($sanitizedString) !== 8) {
69 6
                return null;
70
            }
71
        }
72
73 57
        return $sanitizedString;
74
    }
75
76
    /**
77
     * Strips delimiters from the given date string and returns the sanitized string.
78
     *
79
     * The returned string will be either
80
     *
81
     * @param string $dateString
82
     * @param string $delimiterPattern regex pattern to use when searching for delimiters (excluding the surrounding /'s
83
     *
84
     * @return null|string
85
     */
86 63
    private function handleDelimiters(string $dateString, string $delimiterPattern): ?string
87
    {
88
        // Grab everything that isn't an int, these things must be delimiters
89 63
        $delimiters = [];
90 63
        preg_match_all("/\D/", $dateString, $delimiters);
91
92 63
        if (count($delimiters[0]) > 1) {
93
            // ensure that all delimiters are valid delimiters accepted by mysql
94 42
            $delimiterString = implode('', $delimiters[0]);
95
96 42
            if (preg_match("/$delimiterPattern/", $delimiterString) > 0) {
97 3
                return null;
98
            }
99
100
            // sanitize valid punctuation characters that are being used as delimiters
101 39
            $delimiterString = preg_quote($delimiterString, '/');
102
            // Remove delimiters to make parsing date segments easier
103 39
            $dateString = preg_replace("/[$delimiterString]/", '', $dateString);
104
        }
105
106 60
        return $dateString;
107
    }
108
109
    /**
110
     * Converts YYMMDD to YYYYMMDD.
111
     *
112
     * @param string $dateString
113
     *
114
     * @return null|string
115
     */
116 60
    private function normalizeYear(string $dateString): ?string
117
    {
118
        // if we have a two digit year, convert it to a 4 digit year for the sake of comparison
119 60
        if (strlen($dateString) === 6) {
120 9
            $twoDigitYear = substr($dateString, 0, 2);
121 9
            $yearPrefix = 0 < $twoDigitYear && $twoDigitYear < 69 ? '20' : '19';
122 9
            $dateString = $yearPrefix . $dateString;
123
        }
124
125 60
        return $dateString;
126
    }
127
}
128