1 | <?php |
||||
2 | |||||
3 | namespace letsjump\workdayHelper; |
||||
4 | |||||
5 | use yii\base\InvalidConfigException; |
||||
0 ignored issues
–
show
|
|||||
6 | |||||
7 | /** |
||||
8 | * Class WorkdayHelper |
||||
9 | * |
||||
10 | * Count work days and list holiday events in a range of dates with PHP taking care of public holidays and other custom |
||||
11 | * closing days. |
||||
12 | * Inspired by Massimo Simonini getWorkdays() Function. See https://gist.github.com/massiws/9593008 |
||||
13 | * |
||||
14 | * @author Gianpaolo Scrigna <[email protected]> |
||||
15 | */ |
||||
16 | class WorkdayHelper |
||||
17 | { |
||||
18 | public const TYPE_PUBLIC = 'public'; |
||||
19 | public const TYPE_CUSTOM = 'custom'; |
||||
20 | |||||
21 | /** |
||||
22 | * @var int[] days to consider as worked |
||||
23 | * in a week, where sunday == 0 and saturday == 6 |
||||
24 | * @see |
||||
25 | */ |
||||
26 | public $workingDays = [1, 2, 3, 4, 5]; |
||||
27 | |||||
28 | /** |
||||
29 | * @var string date format for the closing days output list |
||||
30 | */ |
||||
31 | public $outputFormat = 'Y-m-d'; |
||||
32 | |||||
33 | /** |
||||
34 | * @var bool calculate and add the easter dates |
||||
35 | * to the closing days output list |
||||
36 | */ |
||||
37 | public $calculateEaster = true; |
||||
38 | |||||
39 | /** |
||||
40 | * @var array[] of custom closures. |
||||
41 | * Any custom closure array need at least the keys: |
||||
42 | * - date ([date] a date in the Y-m-d format) |
||||
43 | * - event ([string] name of the event) |
||||
44 | * The optional array key `options` can contain any custom variable you need |
||||
45 | * and it will be passed as is to the closing days output list |
||||
46 | */ |
||||
47 | public $customClosing = []; |
||||
48 | |||||
49 | /** |
||||
50 | * @var array[] array of public holiday dates where: |
||||
51 | * key: [date] date in m-d format |
||||
52 | * value: [string] name of the event |
||||
53 | * |
||||
54 | */ |
||||
55 | public $publicHolidays = [ |
||||
56 | [ |
||||
57 | 'm-d' => '01-01', |
||||
58 | 'event' => 'Capodanno', |
||||
59 | ], |
||||
60 | [ |
||||
61 | 'm-d' => '01-06', |
||||
62 | 'event' => 'Epifania' |
||||
63 | ], |
||||
64 | [ |
||||
65 | 'm-d' => '04-25', |
||||
66 | 'event' => 'Festa della Liberazione' |
||||
67 | ], |
||||
68 | [ |
||||
69 | 'm-d' => '05-01', |
||||
70 | 'event' => 'Festa del Lavoro' |
||||
71 | ], |
||||
72 | [ |
||||
73 | 'm-d' => '06-02', |
||||
74 | 'event' => 'Festa della Repubblica' |
||||
75 | ], |
||||
76 | [ |
||||
77 | 'm-d' => '08-15', |
||||
78 | 'event' => 'Ferragosto' |
||||
79 | ], |
||||
80 | [ |
||||
81 | 'm-d' => '11-01', |
||||
82 | 'event' => 'Ognissanti' |
||||
83 | ], |
||||
84 | [ |
||||
85 | 'm-d' => '12-08', |
||||
86 | 'event' => 'Immacolata' |
||||
87 | ], |
||||
88 | [ |
||||
89 | 'm-d' => '12-25', |
||||
90 | 'event' => 'Natale' |
||||
91 | ], |
||||
92 | [ |
||||
93 | 'm-d' => '12-26', |
||||
94 | 'event' => 'Santo Stefano' |
||||
95 | ], |
||||
96 | ]; |
||||
97 | |||||
98 | private $startDateObject; |
||||
99 | private $endDateObject; |
||||
100 | private $years = []; |
||||
101 | private $closing = []; |
||||
102 | private $workdays = null; |
||||
103 | private $holidays = []; |
||||
104 | |||||
105 | public function __construct($startDate, $endDate) |
||||
106 | { |
||||
107 | try { |
||||
108 | $this->startDateObject = new \DateTime($startDate); |
||||
109 | $this->endDateObject = new \DateTime($endDate); |
||||
110 | } catch (\Exception $e) { |
||||
111 | var_dump($e->getMessage()); |
||||
0 ignored issues
–
show
|
|||||
112 | exit; |
||||
0 ignored issues
–
show
|
|||||
113 | } |
||||
114 | $this->getYearsInterval(); |
||||
115 | |||||
116 | } |
||||
117 | |||||
118 | /** |
||||
119 | * @return integer the number of the worked days between the interval of dates. |
||||
120 | */ |
||||
121 | public function getWorkdays() |
||||
122 | { |
||||
123 | if ($this->workdays === null) { |
||||
124 | $this->run(); |
||||
125 | } |
||||
126 | |||||
127 | return $this->workdays; |
||||
128 | } |
||||
129 | |||||
130 | /** |
||||
131 | * @return array the array of all the closing days between the interval of dates. |
||||
132 | * @throws \InvalidArgumentException |
||||
133 | */ |
||||
134 | public function getCalendar() |
||||
135 | { |
||||
136 | if ($this->workdays === null) { |
||||
137 | $this->run(); |
||||
138 | } |
||||
139 | |||||
140 | return $this->holidays; |
||||
141 | } |
||||
142 | |||||
143 | /** |
||||
144 | * Fill the array $this->years with every year from the date interval passed |
||||
145 | */ |
||||
146 | private function getYearsInterval() |
||||
147 | { |
||||
148 | for ($year = $this->startDateObject->format('Y'); $year <= $this->endDateObject->format('Y'); $year++) { |
||||
149 | $this->years[] = $year; |
||||
150 | } |
||||
151 | } |
||||
152 | |||||
153 | /** |
||||
154 | * @param \DateTime $dateObject |
||||
155 | * @param string $description |
||||
156 | * @param string $type |
||||
157 | * @param null $options |
||||
0 ignored issues
–
show
|
|||||
158 | * |
||||
159 | * Add an item to $this->closing array |
||||
160 | */ |
||||
161 | private function addClosing($dateObject, $description, $type, $options = null) |
||||
162 | { |
||||
163 | $unixTimestamp = $dateObject->format('U'); |
||||
164 | $this->closing[$unixTimestamp] = [ |
||||
165 | 'unixTimestamp' => $unixTimestamp, |
||||
166 | 'date' => $dateObject->format($this->outputFormat), |
||||
167 | 'event' => $description, |
||||
168 | 'type' => $type, |
||||
169 | 'options' => $options |
||||
170 | ]; |
||||
171 | } |
||||
172 | |||||
173 | /** |
||||
174 | * Add the public holidays to the closing Array |
||||
175 | * |
||||
176 | * @throws \InvalidArgumentException |
||||
177 | */ |
||||
178 | private function addPublicHolidays() |
||||
179 | { |
||||
180 | foreach ($this->years as $year) { |
||||
181 | foreach ($this->publicHolidays as $holiday) { |
||||
182 | try { |
||||
183 | if ( ! array_key_exists('m-d', $holiday) || ! array_key_exists('event', $holiday)) { |
||||
184 | throw new \InvalidArgumentException('Malformed PublicHoliday array. m-d or event key doesn\'t exists'); |
||||
185 | } |
||||
186 | $dateObject = new \DateTime($year . '-' . $holiday['m-d']); |
||||
187 | $options = isset($holiday['options']) ? $holiday['options'] : null; |
||||
188 | $this->addClosing($dateObject, $holiday['event'], self::TYPE_PUBLIC, $options); |
||||
189 | } catch (\Exception $e) { |
||||
190 | var_dump($e->getMessage()); |
||||
0 ignored issues
–
show
|
|||||
191 | } |
||||
192 | } |
||||
193 | if ($this->calculateEaster === true) { |
||||
194 | $this->addEasterDates($year); |
||||
195 | } |
||||
196 | } |
||||
197 | } |
||||
198 | |||||
199 | /** |
||||
200 | * @param integer $year |
||||
201 | * |
||||
202 | * @throws \InvalidArgumentException |
||||
203 | * |
||||
204 | * Calculate the easter days for the year passed |
||||
205 | */ |
||||
206 | private function addEasterDates($year) |
||||
207 | { |
||||
208 | try { |
||||
209 | if(function_exists('easter_days')) { |
||||
210 | $equinox = new \DateTime($year . "-03-21"); |
||||
211 | $easterObject = $equinox->add(new \DateInterval('P' . easter_days($year) . 'D')); |
||||
212 | $this->addClosing($easterObject, 'Pasqua', self::TYPE_PUBLIC); |
||||
213 | $easterMondayObject = $easterObject->add(new \DateInterval('P1D')); |
||||
214 | $this->addClosing($easterMondayObject, 'Lunedì dell\'Angelo', self::TYPE_PUBLIC); |
||||
215 | } else { |
||||
216 | throw new InvalidConfigException("ext-calendar not found in your PHP installation"); |
||||
217 | } |
||||
218 | } catch (\Exception $e) { |
||||
219 | var_dump($e->getMessage()); |
||||
0 ignored issues
–
show
|
|||||
220 | } |
||||
221 | } |
||||
222 | |||||
223 | /** |
||||
224 | * Add the custom closing day to the closing Array |
||||
225 | */ |
||||
226 | private function addCustomClosing() |
||||
227 | { |
||||
228 | if ( ! empty($this->customClosing)) { |
||||
229 | foreach ($this->customClosing as $closure) { |
||||
230 | try { |
||||
231 | if ( ! array_key_exists('date', $closure) || ! array_key_exists('event', $closure)) { |
||||
232 | throw new \InvalidArgumentException('Malformed CustomClosure array. Date or event key doesn\'t exists'); |
||||
233 | } |
||||
234 | if (($dateObject = new \DateTime($closure['date'])) !== false) { |
||||
235 | $options = isset($closure['options']) ? $closure['options'] : null; |
||||
236 | $this->addClosing($dateObject, $closure['event'], self::TYPE_CUSTOM, $options); |
||||
237 | } |
||||
238 | } catch (\Exception $e) { |
||||
239 | var_dump($e->getMessage()); |
||||
0 ignored issues
–
show
|
|||||
240 | } |
||||
241 | } |
||||
242 | } |
||||
243 | } |
||||
244 | |||||
245 | /** |
||||
246 | * Calculate the closing days, the number of days worked and the closing days calendar. |
||||
247 | * |
||||
248 | * @throws \InvalidArgumentException |
||||
249 | */ |
||||
250 | private function run() |
||||
251 | { |
||||
252 | $this->addPublicHolidays(); |
||||
253 | $this->addCustomClosing(); |
||||
254 | $this->workdays = 0; |
||||
255 | for ( |
||||
256 | $unixDay = $this->startDateObject->format('U'); $unixDay <= $this->endDateObject->format('U'); $unixDay = strtotime("+1 day", |
||||
257 | $unixDay) |
||||
0 ignored issues
–
show
$unixDay of type string is incompatible with the type integer|null expected by parameter $baseTimestamp of strtotime() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
258 | ) { |
||||
259 | $dayOfWeek = date("w", $unixDay); |
||||
0 ignored issues
–
show
$unixDay of type string is incompatible with the type integer|null expected by parameter $timestamp of date() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
260 | if (in_array((int)$dayOfWeek, $this->workingDays, true)) { |
||||
261 | if ( ! array_key_exists($unixDay, $this->closing)) { |
||||
262 | $this->workdays++; |
||||
263 | } else { |
||||
264 | $this->holidays[$unixDay] = $this->closing[$unixDay]; |
||||
265 | } |
||||
266 | } |
||||
267 | } |
||||
268 | } |
||||
269 | } |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths