|
1
|
|
|
<?php
|
|
2
|
|
|
namespace SKien\Sepa;
|
|
3
|
|
|
|
|
4
|
|
|
/**
|
|
5
|
|
|
* Helper trait containing some methods used by multiple classes in package
|
|
6
|
|
|
*
|
|
7
|
|
|
* @package Sepa
|
|
8
|
|
|
* @author Stefanius <[email protected]>
|
|
9
|
|
|
* @copyright MIT License - see the LICENSE file for details
|
|
10
|
|
|
* @internal
|
|
11
|
|
|
*/
|
|
12
|
|
|
trait SepaHelper
|
|
13
|
|
|
{
|
|
14
|
|
|
/**
|
|
15
|
|
|
* Check for valid type and trigger error in case of invalid type.
|
|
16
|
|
|
* @param string $type
|
|
17
|
|
|
* @return bool
|
|
18
|
|
|
*/
|
|
19
|
|
|
protected function isValidType(string $type) : bool
|
|
20
|
|
|
{
|
|
21
|
|
|
if ($type != Sepa::CCT && $type != Sepa::CDD) {
|
|
22
|
|
|
trigger_error('invalid type for ' . get_class($this), E_USER_ERROR);
|
|
23
|
|
|
}
|
|
24
|
|
|
return true;
|
|
25
|
|
|
}
|
|
26
|
|
|
|
|
27
|
|
|
/**
|
|
28
|
|
|
* Create unique ID.
|
|
29
|
|
|
* Format: 99999999-9999-9999-999999999999
|
|
30
|
|
|
* @return string
|
|
31
|
|
|
*/
|
|
32
|
|
|
public static function createUID() : string
|
|
33
|
|
|
{
|
|
34
|
|
|
mt_srand((int)microtime(true) * 10000);
|
|
35
|
|
|
$charid = strtoupper(md5(uniqid((string)rand(), true)));
|
|
36
|
|
|
$uuid = substr($charid, 0, 8) . chr(45) .
|
|
37
|
|
|
substr($charid, 8, 4) . chr(45) .
|
|
38
|
|
|
substr($charid, 12, 4) . chr(45) .
|
|
39
|
|
|
substr($charid, 16, 12);
|
|
40
|
|
|
|
|
41
|
|
|
return $uuid;
|
|
42
|
|
|
}
|
|
43
|
|
|
|
|
44
|
|
|
/**
|
|
45
|
|
|
* Convert input to valid SEPA string.
|
|
46
|
|
|
* <ol>
|
|
47
|
|
|
* <li>replacement of special chars</li>
|
|
48
|
|
|
* <li>limitation to supported chars dependend on validation type</li>
|
|
49
|
|
|
* <li>restriction to max length dependend on validation type</li>
|
|
50
|
|
|
* </ol>
|
|
51
|
|
|
*
|
|
52
|
|
|
* Validation Types: <ul>
|
|
53
|
|
|
* <li>SepaHelper::MAX35:</li>
|
|
54
|
|
|
* <li>SepaHelper::MAX70:</li>
|
|
55
|
|
|
* <li>SepaHelper::MAX140:</li>
|
|
56
|
|
|
* <li>SepaHelper::MAX1025:
|
|
57
|
|
|
* <ul>
|
|
58
|
|
|
* <li>max length = MAX[xxx]</li>
|
|
59
|
|
|
* <li>supported chars: A...Z, a...z, 0...9, blank, dot, comma, plus, minus, slash, questionmark, colon, open/closing bracket</li>
|
|
60
|
|
|
* </ul></li>
|
|
61
|
|
|
* <li>SepaHelper::ID1:
|
|
62
|
|
|
* <ul>
|
|
63
|
|
|
* <li>max length = 35</li>
|
|
64
|
|
|
* <li>supported chars: A...Z, a...z, 0...9, blank, dot, comma, plus, minus, slash</li>
|
|
65
|
|
|
* </ul></li>
|
|
66
|
|
|
* <li>SepaHelper::ID2:
|
|
67
|
|
|
* <ul>
|
|
68
|
|
|
* <li>max length = 35</li>
|
|
69
|
|
|
* <li>supported chars: ID1 without blank</li>
|
|
70
|
|
|
* </ul></li>
|
|
71
|
|
|
* </ul>
|
|
72
|
|
|
*
|
|
73
|
|
|
* @param string $str string to validate
|
|
74
|
|
|
* @param int $iType type of validation: one of SepaHelper::MAX35, SepaHelper::MAX70, SepaHelper::MAX140, SepaHelper::MAX1025, SepaHelper::ID1, SepaHelper::ID2
|
|
75
|
|
|
* @return string
|
|
76
|
|
|
*/
|
|
77
|
|
|
public static function validString(string $str, int $iType) : string
|
|
78
|
|
|
{
|
|
79
|
|
|
// replace specialchars...
|
|
80
|
|
|
$strValid = self::replaceSpecialChars($str);
|
|
81
|
|
|
|
|
82
|
|
|
// regular expresion for 'standard' types MAXxxx
|
|
83
|
|
|
$strRegEx = '/[^A-Za-z0-9 \.,\-\/\+():?]/'; // A...Z, a...z, 0...9, blank, dot, comma plus, minus, slash, questionmark, colon, open/closing bracket
|
|
84
|
|
|
$strReplace = ' ';
|
|
85
|
|
|
$iMaxLen = 1025;
|
|
86
|
|
|
switch ($iType) {
|
|
87
|
|
|
case Sepa::ID1:
|
|
88
|
|
|
$iMaxLen = 35;
|
|
89
|
|
|
$strRegEx = '/[^A-Za-z0-9 \.,\+\-\/]/'; // A...Z, a...z, 0...9, blank, dot, comma plus, minus, slash
|
|
90
|
|
|
$strReplace = '';
|
|
91
|
|
|
break;
|
|
92
|
|
|
case Sepa::ID2:
|
|
93
|
|
|
$iMaxLen = 35;
|
|
94
|
|
|
$strRegEx = '/[^A-Za-z0-9\.,\+\-\/]/'; // same as ID1 except blank...
|
|
95
|
|
|
$strReplace = '';
|
|
96
|
|
|
break;
|
|
97
|
|
|
case Sepa::MAX35:
|
|
98
|
|
|
$iMaxLen = 35;
|
|
99
|
|
|
break;
|
|
100
|
|
|
case Sepa::MAX70:
|
|
101
|
|
|
$iMaxLen = 70;
|
|
102
|
|
|
break;
|
|
103
|
|
|
case Sepa::MAX140:
|
|
104
|
|
|
$iMaxLen = 140;
|
|
105
|
|
|
break;
|
|
106
|
|
|
case Sepa::MAX1025:
|
|
107
|
|
|
default:
|
|
108
|
|
|
break;
|
|
109
|
|
|
}
|
|
110
|
|
|
|
|
111
|
|
|
$strValid = preg_replace($strRegEx, $strReplace, $strValid);
|
|
112
|
|
|
return substr($strValid ?? '', 0, $iMaxLen);
|
|
113
|
|
|
}
|
|
114
|
|
|
|
|
115
|
|
|
/**
|
|
116
|
|
|
* Replace some special chars with nearest equivalent.
|
|
117
|
|
|
* - umlauts, acute, circumflex, ...
|
|
118
|
|
|
* - square/curly brackets
|
|
119
|
|
|
* - underscore, at
|
|
120
|
|
|
* @param string $str text to process
|
|
121
|
|
|
* @return string
|
|
122
|
|
|
*/
|
|
123
|
|
|
public static function replaceSpecialChars(string $str) : string
|
|
124
|
|
|
{
|
|
125
|
|
|
$strReplaced = '';
|
|
126
|
|
|
if (strlen($str) > 0) {
|
|
127
|
|
|
// replace known special chars
|
|
128
|
|
|
$aSpecialChars = array(
|
|
129
|
|
|
'á' => 'a', 'à' => 'a', 'ä' => 'ae', 'â' => 'a', 'ã' => 'a', 'å' => 'a', 'æ' => 'ae',
|
|
130
|
|
|
'Á' => 'A', 'À' => 'A', 'Ä' => 'Ae', 'Â' => 'A', 'Ã' => 'A', 'Å' => 'A', 'Æ' => 'AE',
|
|
131
|
|
|
'ç' => 'c', 'Ç' => 'C',
|
|
132
|
|
|
'é' => 'e', 'è' => 'e', 'ê' => 'e', 'ë' => 'e', 'É' => 'E', 'È' => 'E', 'Ê' => 'E', 'Ë' => 'E',
|
|
133
|
|
|
'ì' => 'i', 'î' => 'i', 'ï' => 'i', 'Í' => 'I', 'Ì' => 'I', 'Î' => 'I', 'Ï' => 'I',
|
|
134
|
|
|
'ñ' => 'n', 'Ñ' => 'N',
|
|
135
|
|
|
'ó' => 'o', 'ò' => 'o', 'ö' => 'oe', 'ô' => 'o', 'õ' => 'o', 'ø' => 'o', 'œ' => 'oe',
|
|
136
|
|
|
'Ó' => 'O', 'Ò' => 'O', 'Ö' => 'Oe', 'Ô' => 'O', 'Õ' => 'O', 'Ø' => 'O', 'Œ' => 'OE',
|
|
137
|
|
|
'ß' => 'ss', 'š' => 's', 'Š' => 'S',
|
|
138
|
|
|
'ú' => 'u', 'ù' => 'u', 'ü' => 'ue', 'û' => 'u',
|
|
139
|
|
|
'Ú' => 'U', 'Ù' => 'U', 'Ü' => 'Ue', 'Û' => 'U',
|
|
140
|
|
|
'ý' => 'y', 'ÿ' => 'y', 'Ý' => 'Y', 'Ÿ' => 'Y',
|
|
141
|
|
|
'ž' => 'z', 'Ž' => 'Z',
|
|
142
|
|
|
'[' => '(', ']' => ')', '{' => '(', '}' => ')',
|
|
143
|
|
|
'_' => '-', '@' => '(at)', '€' => 'EUR'
|
|
144
|
|
|
);
|
|
145
|
|
|
|
|
146
|
|
|
$strReplaced = strtr($str, $aSpecialChars);
|
|
147
|
|
|
}
|
|
148
|
|
|
return $strReplaced;
|
|
149
|
|
|
}
|
|
150
|
|
|
|
|
151
|
|
|
/**
|
|
152
|
|
|
* Calculates valid delayed date from given date considering SEPA businessdays.
|
|
153
|
|
|
* @param int $iDaysDelay min count of days the payment delays
|
|
154
|
|
|
* @param int $dtRequested requested date (unix timestamp)
|
|
155
|
|
|
* @return int unix timestamp
|
|
156
|
|
|
*/
|
|
157
|
|
|
public static function calcDelayedDate(int $iDaysDelay, int $dtRequested = 0) : int
|
|
158
|
|
|
{
|
|
159
|
|
|
$dtEarliest = time();
|
|
160
|
|
|
|
|
161
|
|
|
// FORWARD: should daytime ( < 08:30 / < 18:30 ) bear in mind ?
|
|
162
|
|
|
$iBDays = 0;
|
|
163
|
|
|
while ($iBDays < $iDaysDelay) {
|
|
164
|
|
|
$dtEarliest += 86400; // add day ( 24 * 60 * 60 );
|
|
165
|
|
|
if (self::isTarget2Day($dtEarliest)) {
|
|
166
|
|
|
$iBDays++;
|
|
167
|
|
|
}
|
|
168
|
|
|
}
|
|
169
|
|
|
return $dtEarliest > $dtRequested ? $dtEarliest : $dtRequested;
|
|
170
|
|
|
}
|
|
171
|
|
|
|
|
172
|
|
|
/**
|
|
173
|
|
|
* Check for target2-Day (Sepa-Businessday).
|
|
174
|
|
|
* Mo...Fr and NOT TARGET1-Day
|
|
175
|
|
|
* TARGET1 Days:
|
|
176
|
|
|
* - New Year
|
|
177
|
|
|
* - Good Day
|
|
178
|
|
|
* - Easter Monday
|
|
179
|
|
|
* - 1'st May
|
|
180
|
|
|
* - 1.Christmas
|
|
181
|
|
|
* - 2.Christmas
|
|
182
|
|
|
* @todo change to dynamic calculation of eastern and remove $aTarget2 - array
|
|
183
|
|
|
* @param int $dt unix timestamp to check
|
|
184
|
|
|
* @return bool
|
|
185
|
|
|
*/
|
|
186
|
|
|
public static function isTarget2Day(int $dt) : bool
|
|
187
|
|
|
{
|
|
188
|
|
|
$iWeekDay = date('N', $dt);
|
|
189
|
|
|
|
|
190
|
|
|
// New Year Good Day Easter Monday 1'stMay 1.Christmas 2.Christmas
|
|
191
|
|
|
$aTarget2 = array(
|
|
192
|
|
|
'2019-01-01', '2019-04-18', '2019-04-21', '2019-05-01', '2019-12-25', '2019-12-26',
|
|
193
|
|
|
'2020-01-01', '2020-04-10', '2020-04-13', '2020-05-01', '2020-12-25', '2020-12-26',
|
|
194
|
|
|
'2021-01-01', '2021-04-02', '2021-04-05', '2021-05-01', '2021-12-25', '2021-12-26',
|
|
195
|
|
|
'2022-01-01', '2022-04-15', '2022-04-18', '2022-05-01', '2022-12-25', '2022-12-26',
|
|
196
|
|
|
'2023-01-01', '2023-04-07', '2023-04-10', '2023-05-01', '2023-12-25', '2023-12-26',
|
|
197
|
|
|
'2024-01-01', '2024-03-29', '2024-04-01', '2024-05-01', '2024-12-25', '2024-12-26',
|
|
198
|
|
|
'2025-01-01', '2025-04-18', '2025-04-21', '2025-05-01', '2025-12-25', '2025-12-26',
|
|
199
|
|
|
);
|
|
200
|
|
|
return !($iWeekDay > 5 || in_array(date('Y-m-d', $dt), $aTarget2));
|
|
201
|
|
|
}
|
|
202
|
|
|
}
|
|
203
|
|
|
|