WorkdayHelper   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 249
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 101
dl 0
loc 249
rs 9.84
c 3
b 0
f 0
wmc 32

9 Methods

Rating   Name   Duplication   Size   Complexity  
B addCustomClosing() 0 14 8
A __construct() 0 10 2
A getCalendar() 0 7 2
A addClosing() 0 9 1
A getWorkdays() 0 7 2
A run() 0 15 4
A addEasterDates() 0 14 3
B addPublicHolidays() 0 17 8
A getYearsInterval() 0 4 2
1
<?php
2
3
namespace letsjump\workdayHelper;
4
5
use yii\base\InvalidConfigException;
0 ignored issues
show
Bug introduced by
The type yii\base\InvalidConfigException 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...
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
Security Debugging Code introduced by
var_dump($e->getMessage()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
112
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
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
Documentation Bug introduced by
Are you sure the doc-type for parameter $options is correct as it would always require null to be passed?
Loading history...
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
Security Debugging Code introduced by
var_dump($e->getMessage()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
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
Security Debugging Code introduced by
var_dump($e->getMessage()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
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
Security Debugging Code introduced by
var_dump($e->getMessage()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
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
Bug introduced by
$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 ignore-type  annotation

257
            /** @scrutinizer ignore-type */ $unixDay)
Loading history...
258
        ) {
259
            $dayOfWeek = date("w", $unixDay);
0 ignored issues
show
Bug introduced by
$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 ignore-type  annotation

259
            $dayOfWeek = date("w", /** @scrutinizer ignore-type */ $unixDay);
Loading history...
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
}