absences_Entry::loadDefaultValues()   F
last analyzed

Complexity

Conditions 10
Paths 512

Size

Total Lines 40
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 10.8856

Importance

Changes 0
Metric Value
cc 10
eloc 19
nc 512
nop 0
dl 0
loc 40
rs 3.2187
c 0
b 0
f 0
ccs 23
cts 29
cp 0.7931
crap 10.8856

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/************************************************************************
3
 * OVIDENTIA http://www.ovidentia.org                                   *
4
 ************************************************************************
5
 * Copyright (c) 2003 by CANTICO ( http://www.cantico.fr )              *
6
 *                                                                      *
7
 * This file is part of Ovidentia.                                      *
8
 *                                                                      *
9
 * Ovidentia is free software; you can redistribute it and/or modify    *
10
 * it under the terms of the GNU General Public License as published by *
11
 * the Free Software Foundation; either version 2, or (at your option)  *
12
 * any later version.													*
13
 *																		*
14
 * This program is distributed in the hope that it will be useful, but  *
15
 * WITHOUT ANY WARRANTY; without even the implied warranty of			*
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.					*
17
 * See the  GNU General Public License for more details.				*
18
 *																		*
19
 * You should have received a copy of the GNU General Public License	*
20
 * along with this program; if not, write to the Free Software			*
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,*
22
 * USA.																	*
23
************************************************************************/
24
25
26
require_once dirname(__FILE__).'/vacincl.php';
27
require_once dirname(__FILE__).'/request.class.php';
28
require_once $GLOBALS['babInstallPath'].'utilit/urlincl.php';
29
30
31
/**
32
 * Vacation request
33
 *
34
 *
35
 * @property int	$id_user
36
 * @property string $createdOn
37
 * @property int	$createdBy
38
 * @property string	$date_begin
39
 * @property string	$date_end
40
 * @property int	$idfai
41
 * @property string	$comment
42
 * @property string	$date			Last modification date or approbation date (last action)
43
 * @property string	$status			'' : unconfirmed ; 'Y' : confirmed ; 'P' : previsional
44
 * @property string	$comment2		Commentaire approbateur
45
 * @property int	$id_approver
46
 * @property int	$folder
47
 * @property int	$creation_type
48
 * @property int	$archived
49
 * 
50
 *
51
 */
52
class absences_Entry extends absences_Request
53
{
54
55
	const CREATION_USER = 0;
56
	const CREATION_FIXED = 1;
57
58
59
60
61
	/**
62
	 * Memory for saved or unsaved elements of a vacation request
63
	 * @var array
64
	 */
65
	private $elements = array();
66
67
68
	/**
69
	 * Memory for working periods of a vacation request
70
	 * they are the working periods saved with the entry at the creation of the request
71
	 * the periods will remain event after a workschedule modification for the user
72
	 * @var array
73
	 */
74
	private $plannedPeriods = array();
75
76
	/**
77
	 * @var float
78
	 */
79
	private $duration_days = null;
80
81
	/**
82
	 * @var float
83
	 */
84
	private $duration_hours = null;
85
86
87
	/**
88
	 * Cache for actual working periods from calendar
89
	 * @var bab_CalendarPeriod[]
90
	 */
91
	private $working_periods;
92
93
94
	/**
95
	 * cache for total quantity
96
	 * @var float
97
	 */
98
	private $total_days = null;
99
100
	/**
101
	 * cache for total quantity
102
	 * @var float
103
	 */
104
	private $total_hours = null;
105
106
107
	/**
108
	 * cache for total quantity per type
109
	 * @var array
110
	 */
111
	private $total_type_days = null;
112
113
	/**
114
	 * cache for total quantity per type
115
	 * @var float
116
	 */
117
	private $total_type_hours = null;
118
119
120
121
	/**
122
	 * @var array
123
	 */
124
	private $workingPeriodIndex = null;
125
	
126
	
127
	/**
128
	 * @see absences_EntryPeriod::getDurationDays
129
	 * @var array
130
	 */
131
	public $_getDurationDays_halfDays = array();
132
133
134
135
	/**
136
	 * @return absences_Entry
137
	 */
138
	public static function getById($id)
139
	{
140
		$request = new absences_Entry();
141
		$request->id = $id;
142
143
		return $request;
144
	}
145
146
147
	/**
148
	 * (non-PHPdoc)
149
	 * @see absences_Record::getRow()
150
	 */
151 41
	public function getRow()
152
	{
153 41
		if (null === $this->row)
154 41
		{
155 11
			if (!isset($this->id))
156 11
			{
157
				throw new Exception('Failed to load entry, missing entry id');
158
			}
159
160 11
			global $babDB;
161 11
			$res = $babDB->db_query('SELECT * FROM absences_entries WHERE id='.$babDB->quote($this->id));
162 11
			$this->setRow($babDB->db_fetch_assoc($res));
163 11
		}
164
165 41
		return $this->row;
166
	}
167
168
169
	/**
170
	 * (non-PHPdoc)
171
	 * @see absences_Request::getRequestType()
172
	 *
173
	 * @return string
174
	 */
175
	public function getRequestType()
176
	{
177
		switch($this->creation_type)
178
		{
179
			case '1':
180
				return absences_translate('Fixed vacation');
181
182
			default:
183
			case '0':
0 ignored issues
show
Unused Code introduced by
case '0': return abs...('Requested vacation'); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
184
				return absences_translate('Requested vacation');
185
		}
186
187
188
189
	}
190
191
192
193
194
	public function getApprobationId()
195
	{
196
		if ($agent = $this->getAgent())
197
		{
198
			return $agent->getApprobationId();
199
		}
200
201
		return null;
202
	}
203
204
205
206
207
208
	/**
209
	 * Get elements stored in database
210
	 * @return absences_EntryElemIterator
211
	 */
212
	public function getElementsIterator()
213
	{
214
		$I = new absences_EntryElemIterator;
215
		$I->entry = $this;
216
217
		return $I;
218
	}
219
220
221 28
	public function getPlannedPeriodsIterator()
222
	{
223 28
	    $I = new absences_EntryPeriodIterator;
224 28
	    $I->entry = $this;
225
226 28
	    return $I;
227
	}
228
229
230
231
	/**
232
	 * Get entries within the same folder
233
	 * @return absences_EntryIterator
234
	 */
235 View Code Duplication
	public function getFolderEntriesIterator()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
236
	{
237
		if (!$this->folder)
238
		{
239
			return null;
240
		}
241
242
		$I = new absences_EntryIterator;
243
		$I->folder = $this->folder;
0 ignored issues
show
Documentation Bug introduced by
The property $folder was declared of type string, but $this->folder is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
244
245
		return $I;
246
	}
247
248
249
250
251
252
253
254
	/**
255
	 *
256
	 * @param string $message	Generated message
257
	 * @param string $comment	Author comment
258
	 */
259
	public function addElementsMovements($message, $comment = '')
260
	{
261
		foreach($this->elements as $elem)
262
		{
263
			/*@var $elem absences_EntryElem */
264
			$elem->addMovement($message, $comment);
265
		}
266
	}
267
268
269
270
271
	/**
272
	 * Load elements from database
273
	 * @throws Exception
274
	 */
275
	public function loadElements()
276
	{
277
		if (!isset($this->id))
278
		{
279
			throw new Exception('Failed to load entry elements, id missing');
280
		}
281
282
		$this->elements = array();
283
284
		foreach($this->getElementsIterator() as $entryElem)
285
		{
286
			/*@var $entryElem absences_EntryElem */
287
			$this->addElement($entryElem);
288
		}
289
	}
290
291
	/**
292
	 * Get the loaded elements
293
	 * @return array
294
	 */
295 15
	public function getElements()
296
	{
297 15
		return $this->elements;
298
	}
299
300
	/**
301
	 * Get element by right from the loaded elements
302
	 * @param int|absences_Right $right
303
	 * @return absences_EntryElem
304
	 */
305 19
	public function getElement($right)
306
	{
307 19
	    $id_right = $right;
308 19
	    if ($right instanceof absences_Right) {
309
	        $id_right = $right->id; 
310
	    }
311
	    
312
	    
313 19
	    if (empty($this->elements)) {
314
	        // not loaded
315
	        
316 5
	        require_once dirname(__FILE__).'/entry_elem.class.php';
317
	        
318 5
	        global $babDB;
319
	        
320 5
	        $res = $babDB->db_query('SELECT * 
321
	            FROM absences_entries_elem 
322
	            WHERE 
323 5
	               id_entry='.$babDB->quote($this->id).' 
324 5
	               AND id_right='.$babDB->quote($id_right));
325
	        
326 5
	        $row = $babDB->db_fetch_assoc($res);
327
	        
328 5
	        if (!$row) {
329
	            return null;
330
	        }
331
	        
332 5
	        $elem = new absences_EntryElem();
333 5
	        $elem->setRow($row);
334
	        
335 5
	        if ($right instanceof absences_Right) {
336
	           $elem->setRight($right);
337
	        }
338
	        
339 5
	        return $elem;
340
	    }
341
342
	    
343
	    // already loaded
344
	    
345 14
	    foreach($this->elements as $element) {
346 14
	        if ($element->id_right == $id_right) {
347 14
	            return $element;
348
	        }
349 11
	    }
350
351
	    return null;
352
	}
353
	
354
	
355
	
356
	/**
357
	 * Add a planned period
358
	 * @param absences_EntryPeriod $plannedPeriod
359
	 */
360
	public function addPeriod(absences_EntryPeriod $plannedPeriod)
361
	{
362
	    $this->plannedPeriods[] = $plannedPeriod;
363
	}
364
365
366
367
368
	/**
369
	 * Load the planned working periods from database
370
	 * @throws Exception
371
	 */
372 29
	public function loadPlannedPeriods()
373
	{
374 29
	    if (!isset($this->id))
375 29
	    {
376
	        throw new Exception('Failed to load entry periods, id missing');
377
	    }
378
379 28
	    $this->plannedPeriods = array();
380
381 28
	    $res = $this->getPlannedPeriodsIterator();
382
383 28
	    if (0 === $res->count()) {
384 28
	        return $this->createPlannedPeriods();
385
	    }
386
387
	    foreach($res as $plannedPeriod)
388
	    {
389
	        /*@var $plannedPeriod absences_EntryPeriod */
390
	        $this->addPeriod($plannedPeriod);
391
	    }
392
	}
393
394
	/**
395
	 * Get the loaded planned working periods
396
	 * @return absences_EntryPeriod[]
397
	 */
398 33
	public function getPlannedPeriods()
399
	{
400 33
	    return $this->plannedPeriods;
401
	}
402
403
404
	/**
405
	 * Get planned duration
406
	 *
407
	 * @param string $begin    Optional limit to use for duration
408
	 * @param string $end      Optional limit to use for duration
409
	 *
410
	 * @return float
411
	 */
412 28 View Code Duplication
	public function getPlannedDurationDays($begin = null, $end = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
413
	{
414 28
	    if (empty($this->plannedPeriods)) {
415 16
	        $this->loadPlannedPeriods();
416 16
	    }
417
418 28
	    $total = 0.0;
419
420 28
	    foreach ($this->getPlannedPeriods() as $entryPeriod) {
421 28
	        $total += $entryPeriod->getDurationDays($begin, $end);
422 28
	    }
423
	    
424
425
	    
426
	     
427
428 28
	    return $total;
429
	}
430
431
432
	/**
433
	 * Get planned duration
434
	 *
435
	 * @param string $begin    Optional limit to use for duration
436
	 * @param string $end      Optional limit to use for duration
437
	 *
438
	 * @return float
439
	 */
440 27 View Code Duplication
	public function getPlannedDurationHours($begin = null, $end = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
441
	{
442 27
	    if (empty($this->plannedPeriods)) {
443 1
	        $this->loadPlannedPeriods();
444 1
	    }
445
446 27
	    $total = 0.0;
447
448 27
	    foreach ($this->getPlannedPeriods() as $entryPeriod) {
449 27
	        $total += $entryPeriod->getDurationHours($begin, $end);
450 27
	    }
451
452 27
	    return $total;
453
	}
454
455
	
456 7
	public function loadDefaultValues()
457
	{
458
	    
459 7
	    if (!isset($this->createdOn)) {
460
	        $this->createdOn = date('Y-m-d H:i:s');
461
	    }
462
	    
463 7
	    if (!isset($this->date)) {
464
	        $this->date = date('Y-m-d H:i:s');
465
	    }
466
	    
467 7
	    if (!isset($this->comment)) {
468 7
	        $this->comment = '';
469 7
	    }
470
	    
471 7
	    if (!isset($this->comment2)) {
472 7
	        $this->comment2 = '';
473 7
	    }
474
	    
475 7
	    if (!isset($this->idfai)) {
476 7
	        $this->idfai = '0';
477 7
	    }
478
	    
479 7
	    if (!isset($this->status)) {
480
	        $this->status = 'N';
481
	    }
482
	    
483 7
	    if (!isset($this->id_approver)) {
484 7
	        $this->id_approver = '0';
485 7
	    }
486
	    
487 7
	    if (!isset($this->folder)) {
488 7
	        $this->folder = '0';
489 7
	    }
490
	    
491 7
	    if (!isset($this->creation_type)) {
492 7
	        $this->creation_type = '0';
493 7
	    }
494
	    
495 7
	}
496
497
498
	/**
499
	 * Save entry to database
500
	 * without validity checking
501
	 *
502
	 * @return bool
503
	 */
504 7
	public function save()
505
	{
506
		// save entry
507
508 7
		global $babDB;
509
510 7
        $this->loadDefaultValues();
511
512 7
		if (isset($this->id))
513 7
		{
514
			$req = "
515
				UPDATE absences_entries
516
				SET
517 2
					`date`			=".$babDB->quote($this->date).",
518 2
					date_begin		=".$babDB->quote($this->date_begin).",
519 2
					date_end		=".$babDB->quote($this->date_end).",
520 2
					comment			=".$babDB->quote($this->comment).",
521 2
					idfai			=".$babDB->quote($this->idfai).",
522 2
					status			=".$babDB->quote($this->status).",
523 2
					comment2		=".$babDB->quote($this->comment2).",
524 2
					id_approver		=".$babDB->quote($this->id_approver).",
525 2
					folder			=".$babDB->quote($this->folder).",
526 2
					creation_type	=".$babDB->quote($this->creation_type)."
527 2
			";
528
			
529 2
			if (isset($this->todelete))
530 2
			{
531
			    $req .= ", todelete=".$babDB->quote($this->todelete);
532
			}
533
534 2
			if (isset($this->appr_notified))
535 2
			{
536
				$req .= ", appr_notified=".$babDB->quote($this->appr_notified);
537
			}
538
539
540
			$req .= " WHERE
541 2
					id=".$babDB->quote($this->id)." ";
542
543 2
			$babDB->db_query($req);
544
545
546 2 View Code Duplication
		} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
547
548 7
			$babDB->db_query("
549
				INSERT INTO absences_entries
550
				(
551
					id_user,
552
					date_begin,
553
					date_end,
554
					comment,
555
					`createdOn`,
556
					`date`,
557
					idfai,
558
					status,
559
					comment2,
560
					id_approver,
561
					folder,
562
					creation_type
563
				)
564
				VALUES
565
				(
566 7
					".$babDB->quote($this->id_user).",
567 7
					".$babDB->quote($this->date_begin).",
568 7
					".$babDB->quote($this->date_end).",
569 7
					".$babDB->quote($this->comment).",
570 7
					".$babDB->quote($this->createdOn).",
571 7
					".$babDB->quote($this->date).",
572 7
					".$babDB->quote($this->idfai).",
573 7
					".$babDB->quote($this->status).",
574 7
					".$babDB->quote($this->comment2).",
575 7
					".$babDB->quote($this->id_approver).",
576 7
					".$babDB->quote($this->folder).",
577 7
					".$babDB->quote($this->creation_type)."
578
				)
579 7
			");
580
581 7
			$this->id = $babDB->db_insert_id();
582
		}
583
584
585 7
	}
586
587
588
	/**
589
	 * Sort the loaded element by right order, type name
590
	 */
591 27
	public function sortElements()
592
	{
593 27
	    usort($this->elements, array('absences_Entry', 'sortElem'));
594 27
	}
595
596 27
	private static function sortElem(absences_EntryElem $elem1, absences_EntryElem $elem2)
597
	{
598 27
	    $right1 = $elem1->getRight();
599 27
	    $right2 = $elem2->getRight();
600
601 27
	    if ($right1->sortkey > $right2->sortkey) {
602 26
	        return 1;
603
	    }
604
605 2
	    if ($right1->sortkey < $right2->sortkey) {
606 2
	        return -1;
607
	    }
608
609 1
	    $type1 = $right1->getType();
610 1
	    $type2 = $right2->getType();
611
612 1
	    return bab_compare(mb_strtolower($type1->name), mb_strtolower($type2->name));
613
	}
614
	
615
	
616
	/**
617
	 * Verifier si les dates des elements sont correctement parametres
618
	 * en cas de modificiation de la quantite, toujours mettre les dates a zero
619
	 * @return bool
620
	 */
621 33
	public function checkElementsDates()
622
	{
623 33
	    foreach($this->elements as $elem) {
624 33
	        if (!isset($elem->date_begin) || '0000-00-00 00:00:00' === $elem->date_begin) {
625 33
	            return false;
626
	        }
627
	        
628 10
	        if (!isset($elem->date_end) || '0000-00-00 00:00:00' === $elem->date_end) {
629
	            return false;
630
	        }
631 10
	    }
632
	    
633 10
	    return true;
634
	}
635
	
636
	
637
638
639
	/**
640
	 * Add dates to loaded elements using the user calendar
641
	 */
642 33
	public function setElementsDates()
643
	{
644 33
	    include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
645
646 33
	    if (0 === count($this->elements)) {
647
	        throw new absences_EntryException('No elements to set dates on, id_entry='.$this->id.', owner='.$this->getUserName());
648
	    }
649
	    
650 33
	    if ($this->checkElementsDates()) {
651
	        // dates allready set
652
	        // calling getFutureDate twice does not work
653 10
	        return;
654
	    }
655
656 33
	    if (1 === count($this->elements)) {
657 6
	        $element = reset($this->elements);
658
	        /*@var $element absences_EntryElem */
659 6
	        $element->date_begin = $this->date_begin;
660 6
	        $element->date_end = $this->date_end;
661 6
	        return;
662
	    }
663
	    
664 27
	    $this->sortElements();
665
        
666 27
	    $loop = BAB_DateTime::fromIsoDateTime($this->date_begin);
667
	    
668
	    
669
670 27
	    foreach($this->elements as $element) {
671
	        /*@var $element absences_EntryElem */
672 27
	        $element->date_begin = $loop->getIsoDateTime();
673
	        
674 27
	        $loop = $this->getFutureDate($loop, (float) $element->quantity, $element->getRight()->quantity_unit);
675 27
	        $element->date_end = $loop->getIsoDateTime();
676
	        
677 27
	        if ($element->date_end > $this->date_end) {
678 1
	            $element->date_end = $this->date_end;
679 1
	        }
680
	        
681 27
	        $loop = $this->getNextValidDate($loop);
682 27
	    }
683
	    
684
	    // round the last half day to the request period end
685 27
	    if ($this->getElementEndGap() <= 3600) {
686 23
	        $this->elements[count($this->elements)-1]->date_end = $this->date_end;
687 23
	    }
688 27
	}
689
	
690
	/**
691
	 * Get the gap beetween the last element end date and the period end date
692
	 * @return int
693
	 */
694 27
	protected function getElementEndGap()
695
	{
696 27
	    $computedLastDate = bab_mktime($this->elements[count($this->elements)-1]->date_end);
697 27
	    $periodEnd = bab_mktime($this->date_end);
698
	     
699 27
	    if ($periodEnd > $computedLastDate) {
700 6
	         return ($periodEnd - $computedLastDate);
701
	    }
702
	    
703 21
	    if ($computedLastDate > $periodEnd) {
704
	        return ($computedLastDate - $periodEnd);
705
	    }
706
	    
707 21
	    return 0;
708
	}
709
710
711
	/**
712
	 * set the date to the next valid date
713
	 * @param BAB_DateTime $date
714
	 * @return BAB_DateTime
715
	 */
716 27
	protected function getNextValidDate(BAB_DateTime $date)
717
	{
718 27
	    $moment = $date->getTimeStamp();
719
720 27
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
721
722 27
	        if ($moment >= $period->ts_end) {
723 27
	            continue;
724
	        }
725
726
	        // if the future date (end date of the element) is in a worked period
727
	        // the future date is valid as a next date
728 27
	        if ($moment < $period->ts_end && $moment > $period->ts_begin) {
729 15
	            return $date;
730
	        }
731
732
733 13
	        return BAB_DateTime::fromTimeStamp($period->ts_begin);
734 23
	    }
735
736
737 23
	    return $date;
738
	}
739
740
741
742
	/**
743
	 * Add quantity to startdate only on working periods
744
	 *
745
	 * @param BAB_DateTime $startdate
746
	 * @param float $quantity
747
	 * @param string $quantity_unit D|H
748
	 *
749
	 * @return BAB_DateTime
750
	 */
751 27
	protected function getFutureDate(BAB_DateTime $startdate, $quantity, $quantity_unit)
752
	{
753 27
	    if ('H' === $quantity_unit) {
754 4
	        return $this->getFutureDate_Hours($startdate, $quantity);
755
	    }
756
757 24
	    return $this->getFutureDate_Days($startdate, $quantity);
758
	}
759
760
761
	/**
762
	 * Split working periods
763
	 * @return array
764
	 */
765 27
	protected function getWorkingPeriodsFromDate(BAB_DateTime $startdate)
766
	{
767 27
	    $periods = array();
768 27
	    $start = $startdate->getTimeStamp();
769
770 27
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
771
	        
772 27
	        $period = clone $period; // do not overwrite the working periods
773
	                                 // create array of new objects
774
775 27
	        if ($start >= $period->ts_end) {
776
	            // continue to the next worked period
777 21
	            continue;
778
	        }
779
780 27
	        if ($start > $period->ts_begin && $start < $period->ts_end) {
781 15
	            $period->setBeginDate($startdate);
782 15
	            $periods[] = $period;
783 15
	            continue;
784
	        }
785
786 27
	        $periods[] = $period;
787 27
	    }
788
789 27
	    return $periods;
790
	}
791
792
793
794
	/**
795
	 * Add quantity to startdate only on working periods
796
	 *
797
	 * @param BAB_DateTime $startdate
798
	 * @param float $seconds_to_add
799
	 *
800
	 * @return BAB_DateTime
801
	 */
802 27
	protected function getFutureDate_Seconds(BAB_DateTime $startdate, $seconds_to_add)
803
	{
804 27
	    $worked_total = 0; //seconds
805
806
807 27
	    foreach ($this->getWorkingPeriodsFromDate($startdate) as $period) {
808
809 27
	        $add_in_period = ($seconds_to_add - $worked_total);
810 27
	        $worked_total += $period->getDuration();
811
812 27
	        if ((int) $worked_total === (int) $seconds_to_add) {
813
	            // la duree de la periode de travail est egale a duree demandee
814
	            // en tenant compte des periodes de travail precedentes
815
816 25
	            return BAB_DateTime::fromTimeStamp($period->ts_end);
817
	        }
818
819
820 27
            if ($worked_total > $seconds_to_add) {
821
                // la date future se trouve a l'interieur d'une periode travaillee
822 15
                $futureDate = $period->ts_begin + $add_in_period;
823 15
                return BAB_DateTime::fromTimeStamp($futureDate);
824
            }
825
826
            // continue to the next worked period
827
828 27
	    }
829
830
	    return BAB_DateTime::fromIsoDateTime($this->date_end);
831
	}
832
833
834
835
836
837
838
	/**
839
	 * Add quantity to startdate only on working periods
840
	 *
841
	 * @param BAB_DateTime $startdate
842
	 * @param float $quantity  hours
843
	 *
844
	 * @return BAB_DateTime
845
	 */
846 4
	protected function getFutureDate_Hours(BAB_DateTime $startdate, $quantity)
847
	{
848 4
	    $seconds_to_add = $quantity * 3600;
849
850 4
	    return $this->getFutureDate_Seconds($startdate, $seconds_to_add);
851
	}
852
	
853
	
854
	
855
	
856
	
857
	
858
	
859
	
860
	
861
    
862
	
863
	
864
	
865
	/**
866
	 * Add quantity to startdate only on working periods
867
	 *
868
	 * @param BAB_DateTime $startdate
0 ignored issues
show
Documentation introduced by
There is no parameter named $startdate. Did you maybe mean $startDate?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
869
	 * @param float $quantity  days
870
	 *
871
	 * @return BAB_DateTime
872
	 */
873 24
	protected function getFutureDate_Days(BAB_DateTime $startDate, $quantity)
874
	{
875 24
	    $total_seconds = $this->getTotalHours() * 3600;
876 24
	    $total_days = $this->getTotalDays();
877 24
	    $seconds_to_add = $quantity * $total_seconds / $total_days;
878
	    
879 24
	    return $this->getFutureDate_Seconds($startDate, $seconds_to_add);
880
	}
881
	
882
	
883
	
884
	
885
886
887
888
	/**
889
	 * Create the planned periods in the entry boundaries
890
	 * from the calendar working periods
891
	 */
892 36
	public function createPlannedPeriods()
893
	{
894 36
	    require_once dirname(__FILE__).'/entry_period.class.php';
895 36
	    $this->plannedPeriods = array();
896
897 36
	    foreach ($this->getWorkingPeriods() as $workingPeriod) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
898
899
	        /*@var $workingPeriod bab_CalendarPeriod */
900
901 36
	        $plannedPeriod = new absences_EntryPeriod();
902 36
	        $plannedPeriod->setEntry($this);
903 36
	        $plannedPeriod->date_begin = date('Y-m-d H:i:s', $workingPeriod->ts_begin);
904 36
	        $plannedPeriod->date_end = date('Y-m-d H:i:s', $workingPeriod->ts_end);
905
906 36
	        $this->plannedPeriods[] = $plannedPeriod;
907 36
	    }
908 36
	}
909
910
911
	
912
913
914
915
	/**
916
	 * Save elements of entry to database
917
	 *
918
	 */
919
	public function saveElements()
920
	{
921
		$processed_ids = array();
922
923
		foreach ($this->elements as $elem)
924
		{
925
			/*@var $elem absences_EntryElem */
926
		    
927
		    try {
928
		    
929
    			$elem->save();
930
    			$processed_ids[] = $elem->id;
931
    			
932
		    } catch(Exception $e) {
933
		        // fail to save one element, it will be deleted or not created
934
		        bab_debug($e->getMessage());
935
		    }
936
		}
937
938
		// delete removed elements
939
940
		global $babDB;
941
942
		$babDB->db_query('DELETE FROM absences_entries_elem WHERE id_entry='.$babDB->quote($this->id).' AND id NOT IN('.$babDB->quote($processed_ids).')');
943
944
	}
945
946
947
948
949
	public function savePlannedPeriods()
950
	{
951
	    if (empty($this->plannedPeriods)) {
952
	        throw new Exception('Planned periods where not loaded or set');
953
	    }
954
955
	    $processed_ids = array();
956
957
	    foreach ($this->plannedPeriods as $period)
958
	    {
959
	        /*@var $elem absences_EntryPeriod */
960
	        $period->save();
961
962
	        $processed_ids[] = $period->id;
963
	    }
964
965
	    global $babDB;
966
967
	    $babDB->db_query('DELETE FROM absences_entries_periods WHERE id_entry='.$babDB->quote($this->id).' AND id NOT IN('.$babDB->quote($processed_ids).')');
968
969
	}
970
971
972
973
	/**
974
	 * Apply dynamic rights for all right involved in the entry
975
	 */
976
	public function applyDynamicRight()
977
	{
978
		require_once dirname(__FILE__).'/agent_right.class.php';
979
980
		$I = new absences_AgentRightManagerIterator();
981
		$I->setAgent($this->getAgent());
982
983
		foreach ($I as $agentRight)
984
		{
985
			/*@var $agentRight absences_AgentRight */
986
			$agentRight->applyDynamicRight();
987
		}
988
	}
989
990
991
992
993
	/**
994
	 * Add element to list
995
	 * @param absences_EntryElem $elem
996
	 */
997 43
	public function addElement(absences_EntryElem $elem)
998
	{
999 43
		$this->elements[] = $elem;
1000 43
	}
1001
1002
	/**
1003
	 * Remove an element in the list
1004
	 * @param int $id_right
1005
	 *
1006
	 * @return bool
1007
	 */
1008
	public function removeElement($id_right)
1009
	{
1010
		foreach ($this->elements as $key => $elem)
1011
		{
1012
			if ($elem->id_right == $id_right)
1013
			{
1014
				unset($this->elements[$key]);
1015
				return true;
1016
			}
1017
		}
1018
1019
		return false;
1020
	}
1021
1022
1023
	/**
1024
	 * Set the working period index for the entry
1025
	 * Used in unit tests to set differents working periods
1026
	 * if not used, the workingPeriodIndex will be generated from the calendar API
1027
	 *
1028
	 */
1029 42
	public function setWorkingPeriodIndex(Array $index_working)
1030
	{
1031 42
	    $this->workingPeriodIndex = $index_working;
1032 42
	    return $this;
1033
	}
1034
1035
	/**
1036
	 * Get working period index for the user and for the period
1037
	 * @return array
1038
	 */
1039 39
	private function getWorkingPeriodIndex()
1040
	{
1041 39
	    if (!isset($this->workingPeriodIndex)) {
1042
1043
	        include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
1044
1045
	        list($index_working,, $is_free) = absences_getHalfDaysIndex(
1046
	            $this->id_user,
1047
	            BAB_DateTime::fromIsoDateTime($this->date_begin),
1048
	            BAB_DateTime::fromIsoDateTime($this->date_end),
1049
	            true
1050
	        );
1051
1052
1053
	        foreach ($index_working as $key => $period_list) {
1054
	            if (!isset($is_free[$key])) {
1055
	                unset($index_working[$key]);
1056
	            }
1057
	        }
1058
1059
	        $this->workingPeriodIndex = $index_working;
1060
	    }
1061
1062 39
	    return $this->workingPeriodIndex;
1063
	}
1064
1065
1066
1067
	/**
1068
	 * Number of free days and hours between two dates
1069
	 *
1070
	 */
1071 39
	private function loadDurations() {
1072
1073 39
		$this->duration_days 	= 0.0;
1074 39
		$this->duration_hours 	= 0.0;
1075 39
		$this->working_periods = array();
1076
1077
1078
1079 39
		$index_working = $this->getWorkingPeriodIndex();
1080
1081
1082 39
		foreach ($index_working as $period_list) {
1083
1084 39
			$this->duration_days += 0.5;
1085
1086 39
			foreach($period_list as $p)
1087
			{
1088
				/*@var $p bab_CalendarPeriod */
1089
1090 39
				if ($p->getCollection() instanceof bab_WorkingPeriodCollection)
1091 39
				{
1092 39
				    $this->working_periods[] = $p;
1093 39
					$this->duration_hours 	+= ($p->getDuration() / 3600);
1094 39
				}
1095 39
			}
1096 39
		}
1097 39
	}
1098
1099
1100
	/**
1101
	 * Get list of working periods of the entry
1102
	 * @return bab_CalendarPeriod[]
1103
	 */
1104 39
	protected function getWorkingPeriods()
1105
	{
1106 39
	    if (!isset($this->working_periods)) {
1107 39
	        $this->loadDurations();
1108 39
	    }
1109
	    
1110 39
	    return $this->working_periods;
1111
	}
1112
1113
1114
	/**
1115
	 * List of working periods for one day
1116
	 * @param string $date 10 chars
1117
	 *
1118
	 * @return bab_CalendarPeriod[]
1119
	 */
1120
	public function getDayWorkingPeriods($date)
1121
	{
1122
	    $return = array();
1123
	    foreach ($this->getWorkingPeriods() as $period) {
0 ignored issues
show
Bug introduced by
The expression $this->getWorkingPeriods() of type null|array<integer,object<bab_CalendarPeriod>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1124
	        if ($date === date('Y-m-d', $period->ts_begin)) {
1125
	            $return[] = $period;
1126
	        }
1127
	    }
1128
1129
	    return $return;
1130
	}
1131
1132
1133
	/**
1134
	 * List of working periods for one day
1135
	 * @param string $date 10 chars
1136
	 *
1137
	 * @return absences_EntryPeriod[]
1138
	 */
1139
	public function getDayPlannedPeriods($date)
1140
	{
1141
	    $return = array();
1142
	    foreach ($this->getPlannedPeriodsIterator() as $period) {
1143
	        if ($date === substr($period->date_begin, 0, 10)) {
1144
	            $return[] = $period;
1145
	        }
1146
	    }
1147
1148
	    return $return;
1149
	}
1150
1151
1152
1153
1154
	/**
1155
	 * Get period duration in days
1156
	 * @return float
1157
	 */
1158
	public function getDurationDays()
1159
	{
1160
		if (!isset($this->duration_days))
1161
		{
1162
			$this->loadDurations();
1163
		}
1164
1165
		return $this->duration_days;
1166
	}
1167
1168
	/**
1169
	 * Get period duration in hours
1170
	 * @return float
1171
	 */
1172
	public function getDurationHours()
1173
	{
1174
		if (!isset($this->duration_hours))
1175
		{
1176
			$this->loadDurations();
1177
		}
1178
1179
		return $this->duration_hours;
1180
	}
1181
1182
1183
	/**
1184
	 * Convert a number of days to hours
1185
	 * @param float $days
1186
	 * @return float
1187
	 */
1188 24 View Code Duplication
	private function daysToHours($days)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1189
	{
1190 24
	    $plannedDays = $this->getPlannedDurationDays();
1191
	    
1192 24
	    if (!$plannedDays) {
1193
	        return 0;
1194
	    }
1195
	    
1196 24
		$ratio = $this->getPlannedDurationHours() / $plannedDays;
1197 24
		return round(($ratio * $days), 2);
1198
	}
1199
1200
	/**
1201
	 * Convert a number of hours to days
1202
	 * @param float $hours
1203
	 * @return float
1204
	 */
1205 1 View Code Duplication
	private function hoursToDays($hours)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1206
	{
1207 1
	    $plannedHours = $this->getPlannedDurationHours();
1208
	    
1209 1
	    if (!$plannedHours) {
1210
	        return 0;
1211
	    }
1212
	    
1213 1
		$ratio = $this->getPlannedDurationDays() / $plannedHours;
1214 1
		return round(($ratio * $hours), 2);
1215
	}
1216
1217
1218
1219
	/**
1220
	 * Compute totals for loaded elements
1221
	 */
1222 24
	private function loadedElementsTotal()
1223
	{
1224 24
		if (empty($this->elements))
1225 24
		{
1226
			$this->loadElements();
1227
		}
1228
1229 24
		$this->total_days = 0.0;
1230 24
		$this->total_hours = 0.0;
1231 24
		$this->total_type_days = array();
1232 24
		$this->total_type_hours = array();
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type double of property $total_type_hours.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1233
1234 24
		foreach($this->elements as $elem)
1235
		{
1236
			/*@var $elem absences_EntryElem */
1237 24
			$right = $elem->getRight();
1238
1239 24
			$quantity = (float) $elem->quantity;
1240
1241 24
			switch($right->quantity_unit)
1242
			{
1243 24
				case 'D':
1244 24
					$hours = $this->daysToHours($quantity);
1245 24
					$this->addQUantityInCache($right, $quantity, $hours);
1246 24
					break;
1247 1
				case 'H':
1248 1
				    $days = $this->hoursToDays($quantity);
1249 1
				    $this->addQUantityInCache($right, $days, $quantity);
1250 1
					break;
1251 24
			}
1252 24
		}
1253 24
	}
1254
1255
1256
	/**
1257
	 * Populate the cache variables for one element
1258
	 *
1259
	 * @param absences_Right $right
1260
	 * @param float $days
1261
	 * @param float $hours
1262
	 */
1263 24
	private function addQUantityInCache(absences_Right $right, $days, $hours)
1264
	{
1265 24
	    if (!isset($this->total_type_days[$right->id_type])) {
1266 24
	        $this->total_type_days[$right->id_type] = 0.0;
1267 24
	    }
1268
1269 24
	    if (!isset($this->total_type_hours[$right->id_type])) {
1270 24
	        $this->total_type_hours[$right->id_type] = 0.0;
1271 24
	    }
1272
1273 24
	    $this->total_days += $days;
1274 24
	    $this->total_hours += $hours;
1275 24
	    $this->total_type_days[$right->id_type] += $days;
1276 24
	    $this->total_type_hours[$right->id_type] += $hours;
1277 24
	}
1278
1279
1280
1281
1282
1283
	/**
1284
	 * Get sum of elements quantity, converted in days
1285
	 *
1286
	 * @param int $id_type Optional filter by type
1287
	 *
1288
	 * @return float
1289
	 */
1290 24 View Code Duplication
	public function getTotalDays($id_type = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1291
	{
1292 24
		if (!isset($this->total_days)) {
1293
			$this->loadedElementsTotal();
1294
		}
1295
1296 24
		if (isset($id_type)) {
1297
1298
		    if (!isset($this->total_type_days[$id_type])) {
1299
		        return 0.0;
1300
		    }
1301
1302
		    return $this->total_type_days[$id_type];
1303
		}
1304
1305 24
		return $this->total_days;
1306
	}
1307
1308
	/**
1309
	 * Get sum of elements quantity, converted in hours
1310
	 *
1311
	 * @param int $id_type Optional filter by type
1312
	 *
1313
	 * @return float
1314
	 */
1315 24 View Code Duplication
	public function getTotalHours($id_type = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1316
	{
1317 24
		if (!isset($this->total_hours)) {
1318 24
			$this->loadedElementsTotal();
1319 24
		}
1320
1321 24
		if (isset($id_type)) {
1322
1323
		    if (!isset($this->total_type_hours[$id_type])) {
1324
		        return 0.0;
1325
		    }
1326
1327
		    return $this->total_type_hours[$id_type];
1328
		}
1329
1330 24
		return $this->total_hours;
1331
	}
1332
1333
1334
1335
1336
	/**
1337
	 * Number of days on entry between two dates
1338
	 * si les dates de l'element sont a cheval sur la periode demandee
1339
	 * on utlise les heures travailles qui etait en vigeur au moment de la creation
1340
	 * de la demande
1341
	 *
1342
	 * @param string   $begin	Datetime
1343
	 * @param string   $end		Datetime
1344
	 * @param int      $id_type Optional filter by type
1345
	 *
1346
	 * @return float (days)
1347
	 */
1348 1 View Code Duplication
	public function getPlannedDaysBetween($begin, $end, $id_type = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1349
	{
1350 1
	    if (empty($this->elements))
1351 1
	    {
1352
	        $this->loadElements();
1353
	    }
1354
1355 1
	    $total = 0.0;
1356 1
	    foreach($this->elements as $elem)
1357
	    {
1358
	        /*@var $elem absences_EntryElem */
1359 1
	        $right = $elem->getRight();
1360
1361 1
	        if (isset($id_type) && (int) $id_type !== (int) $right->id_type) {
1362
	            continue;
1363
	        }
1364
1365 1
	        $quantity = $elem->getPlannedQuantityBetween($begin, $end);
1366
1367 1
	        if ('H' === $right->quantity_unit) {
1368
	            $days = $this->hoursToDays($quantity);
1369
	        } else {
1370 1
	            $days = $quantity;
1371
	        }
1372
1373 1
	        $total += $days;
1374 1
	    }
1375
1376 1
	    return $total;
1377
	}
1378
1379
1380
1381
1382
	/**
1383
	 * Number of hours on entry between two dates
1384
	 * si les dates de l'element sont a cheval sur la periode demandee
1385
	 * on utlise les heures travailles qui etait en vigeur au moment de la creation
1386
	 * de la demande
1387
	 *
1388
	 * @param string   $begin	Datetime
1389
	 * @param string   $end		Datetime
1390
	 * @param int      $id_type Optional filter by type
1391
	 *
1392
	 * @return float (hours)
1393
	 */
1394 View Code Duplication
	public function getPlannedHoursBetween($begin, $end, $id_type = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1395
	{
1396
	    if (empty($this->elements))
1397
	    {
1398
	        $this->loadElements();
1399
	    }
1400
1401
	    $total = 0.0;
1402
	    foreach($this->elements as $elem)
1403
	    {
1404
	        /*@var $elem absences_EntryElem */
1405
	        $right = $elem->getRight();
1406
1407
	        if (isset($id_type) && (int) $id_type !== (int) $right->id_type) {
1408
	            continue;
1409
	        }
1410
1411
	        $quantity = $elem->getPlannedQuantityBetween($begin, $end);
1412
1413
	        if ('D' === $right->quantity_unit) {
1414
	            $hours = $this->daysToHours($quantity);
1415
	        } else {
1416
	            $hours = $quantity;
1417
	        }
1418
1419
	        $total += $hours;
1420
	    }
1421
1422
	    return $total;
1423
	}
1424
1425
1426
1427
1428
1429
1430
1431
1432
	/**
1433
	 * Test if the loaded elements in entry contains rights in hours
1434
	 * @return bool
1435
	 */
1436
	public function containsHours()
1437
	{
1438
		foreach($this->elements as $elem)
1439
		{
1440
			if ('H' === $elem->getRight()->quantity_unit)
1441
			{
1442
				return true;
1443
			}
1444
		}
1445
1446
		return false;
1447
	}
1448
1449
1450
	/**
1451
	 * Test if loaded elements in entry require approval
1452
	 *
1453
	 * @return bool
1454
	 */
1455
	public function requireApproval()
1456
	{
1457
		if (empty($this->elements))
1458
		{
1459
			$this->loadElements();
1460
		}
1461
1462
		foreach($this->elements as $elem)
1463
		{
1464
			if (1 === (int) $elem->getRight()->require_approval)
1465
			{
1466
				return true;
1467
			}
1468
		}
1469
1470
		return false;
1471
	}
1472
	
1473
	
1474
	public function checkAvailable()
1475
	{
1476
	    $res = $this->getElementsIterator();
1477
	    foreach ($res as $element) {
1478
	        /*@var $element absences_EntryElem */
1479
	        if (!$element->isQuantityAvailable()) {
1480
	            throw new Exception(sprintf(absences_translate('Failed to submit this request, the quantity for right %s s not available'), $element->getRight()->description));
1481
	        }
1482
	    }
1483
	}
1484
1485
	
1486
	/**
1487
	 * Check elements validity
1488
	 * @throws UnexpectedValueException
1489
	 * 
1490
	 * @return int number of elements with quantity > 0
1491
	 */
1492
	protected function checkElementsValidity()
1493
	{
1494
	    $count = 0;
1495
	    foreach($this->elements as $elem)
1496
	    {
1497
	        $quantity = (int) round(100 * $elem->quantity);
1498
	        if ($quantity > 0)
1499
	        {
1500
	            $count++;
1501
	        }
1502
	    
1503
	        $elem->checkValidity();
1504
	    }
1505
	    
1506
	    return $count;
1507
	}
1508
	
1509
	
1510
	/**
1511
	 * throw an exception if there is another entry in the same period
1512
	 * @throws absences_EntryException
1513
	 */
1514
	protected function checkOtherEntriesValidity()
1515
	{
1516
	    $otherEntries = new absences_EntryIterator();
1517
	    $otherEntries->from = $this->date_begin;
1518
	    $otherEntries->to = $this->date_end;
1519
	    $otherEntries->users = array($this->id_user);
1520
	    
1521
	    
1522
	    foreach ($otherEntries as $otherEntry) {
1523
	    
1524
	        /* @var $otherEntry absences_Entry */
1525
	    
1526 View Code Duplication
	        if ($this->id != $otherEntry->id) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1527
	            $e = new absences_EntryException(absences_translate('There is allready an absence request in the period'));
1528
	            $e->entry = $this;
1529
	            $e->blocking = true;
1530
	            throw $e;
1531
	        }
1532
	    }
1533
	    
1534
	}
1535
1536
1537
	/**
1538
	 * Check request validity
1539
	 *
1540
	 * @throws absences_EntryException
1541
	 * @throws UnexpectedValueException
1542
	 * 
1543
	 * @param bool $checkDuration Checking duration require a saved entry
1544
	 *                            Use false if entry is checked before save
1545
	 * 
1546
	 * @return bool
1547
	 */
1548
	public function checkValidity($checkDuration = true)
1549
	{
1550
		// verify mandatory data
1551
1552
		if (!isset($this->id_user) || $this->id_user <= 0)
1553
		{
1554
			throw new UnexpectedValueException('Unexpected id_user');
1555
		}
1556
1557
1558
		$count = $this->checkElementsValidity();
1559
1560
1561 View Code Duplication
		if (0 === $count)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1562
		{
1563
			$e = new absences_EntryException(absences_translate('At least one vacation right must be used'));
1564
			$e->entry = $this;
1565
			$e->blocking = true;
1566
			throw $e;
1567
		}
1568
1569
1570
1571
		// test user validity
1572
1573
		require_once $GLOBALS['babInstallPath'].'utilit/userinfosincl.php';
1574
1575
		$creationdate = bab_userInfos::getCreationDate($this->id_user);
1576
1577
		if ($creationdate >= $this->date_end) {
1578
			$e = new absences_EntryException(sprintf(absences_translate('The vacation request end before user creation date : %s'), bab_shortDate(bab_mktime($creationdate))));
1579
			$e->entry = $this;
1580
			$e->blocking = true;
1581
			throw $e;
1582
		}
1583
1584
		// T8359 : si l'utilisateur est reactive la demande ne sera pas presente, il faudra reenregistrer le droit
1585
		// avant la correction de ce ticket, on creeais les demandes pour eviter ce probleme
1586
1587
		if (!bab_userInfos::isValid($this->id_user)) {
1588
1589
		    /*
1590
		    $e = new absences_EntryException(absences_translate('This user account is disabled'));
1591
		    $e->entry = $this;
1592
		    $e->blocking = true;
1593
		    throw $e;
1594
		    */
1595
1596
		    return false;
1597
		}
1598
1599
1600
		// test quantity / period duration in days
1601
		
1602
		if ($checkDuration) {
1603
1604
    		$total = (int) round(100 * $this->getTotalDays());
1605
    		$duration = (int) round(100 * $this->getDurationDays());
1606
    
1607
    
1608 View Code Duplication
    		if (0 === $duration)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1609
    		{
1610
    			$e = new absences_EntryException(absences_translate('The selected period is not available'));
1611
    			$e->entry = $this;
1612
    			$e->blocking = true;
1613
    			throw $e;
1614
    		}
1615
    
1616
    
1617
    
1618
    		if ($total !== $duration)
1619
    		{
1620
    			$e = new absences_EntryException(sprintf(absences_translate('The total quantity (%s) does not match the period duration (%s)'), absences_quantity($this->getTotalDays(), 'D'), absences_quantity($this->getDurationDays(), 'D')));
1621
    			$e->entry = $this;
1622
    			$e->blocking = !((bool) absences_getVacationOption('allow_mismatch'));
1623
    			throw $e;
1624
    		}
1625
		}
1626
1627
		// test quantity / period duration in hours only if there is one right in hours
1628
		/*
1629
		 *
1630
		 * Pas de verification du nombre d'heure pour le moment, la precision de la verification du nombre de jours devrais suffire a verifier aussi les heures
1631
		 * c'est ce qui a ete fait sur la partie en HTML lorsque les les tests etait fait uniquement cote client (vacuser.html template newvacation)
1632
		 *
1633
		if ($this->containsHours())
1634
		{
1635
			$total = (int) round(100 * $this->getTotalHours());
1636
			$duration = (int) round(100 * $this->getDurationHours());
1637
1638
			if ($total !== $duration)
1639
			{
1640
				$e = new absences_EntryException(absences_translate('The total quantity does not match the period duration in hours'));
1641
				$e->blocking = !((bool) absences_getVacationOption('allow_mismatch'));
1642
				throw $e;
1643
			}
1644
		}
1645
1646
		*/
1647
1648
1649
1650
		// verifier si la periode demandee n'est pas deja occuppe par une autre demande
1651
        $this->checkOtherEntriesValidity();
1652
		
1653
1654
		return true;
1655
	}
1656
1657
1658
	/**
1659
	 * (non-PHPdoc)
1660
	 * @see absences_Request::modifiedOn()
1661
	 *
1662
	 * @return string
1663
	 */
1664 1
	public function modifiedOn()
1665
	{
1666 1
		return $this->date;
1667
	}
1668
1669
	/**
1670
	 * Test if the entry is a fixed vacation
1671
	 * @return bool
1672
	 */
1673
	public function isFixed()
1674
	{
1675
		return (1 === (int) $this->creation_type);
1676
1677
	}
1678
1679
1680
1681
1682
1683
	/**
1684
	 * Test if entry is previsonal
1685
	 * @return bool
1686
	 */
1687
	public function isPrevisonal()
1688
	{
1689
		return ($this->status === 'P');
1690
	}
1691
1692
	/**
1693
	 * Test if at least one entry in folder is previsonal
1694
	 * @return boolean
1695
	 */
1696
	public function isFolderPrevisonal()
1697
	{
1698
		if (!$this->folder)
1699
		{
1700
			return false;
1701
		}
1702
1703
		$I = new absences_EntryIterator;
1704
		$I->folder = $this->folder;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->folder can also be of type integer. However, the property $folder is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1705
		$I->status = 'P';
1706
1707
		return ($I->count() > 0);
1708
	}
1709
1710
	/**
1711
	 * Get the first entry in folder
1712
	 * @return absences_Entry
1713
	 */
1714 View Code Duplication
	public function getFolderFirst()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1715
	{
1716
		if (!$this->folder)
1717
		{
1718
			return null;
1719
		}
1720
1721
		$I = new absences_EntryIterator;
1722
		$I->folder = $this->folder;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->folder can also be of type integer. However, the property $folder is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1723
1724
		foreach($I as $first)
1725
		{
1726
			return $first;
1727
		}
1728
1729
		return null;
1730
	}
1731
1732
1733
	/**
1734
	 * Process specific code when the request is rejected
1735
	 *
1736
	 */
1737
	protected function onReject()
1738
	{
1739
		$this->updateCalendar();
1740
	}
1741
1742
1743
	/**
1744
	 * Process specific code when the request is confirmed
1745
	 *
1746
	 */
1747
	public function onConfirm()
1748
	{
1749
		$this->updateCalendar();
1750
	}
1751
1752
1753
1754
	/**
1755
	 * Call the user calendar backend to update the event
1756
	 * call the event to notify about calendar event modification
1757
	 */
1758
	public function updateCalendar()
1759
	{
1760
		require_once $GLOBALS['babInstallPath'].'utilit/dateTime.php';
1761
1762
		// try to update event copy in other backend (caldav)
1763
1764
		$begin = BAB_DateTime::fromIsoDateTime($this->date_begin);
1765
		$end = BAB_DateTime::fromIsoDateTime($this->date_end);
1766
		$period = absences_getPeriod($this->id, $this->id_user,  $begin, $end);
1767
		if ($period) {
1768
1769
			// probably set a new description if the event has been approved or rejected
1770
			absences_setPeriodProperties($period, $this);
1771
1772
			// save copy of event to calendar backend (if caldav)
1773
			$period->save();
1774
		}
1775
1776
1777
		// Update calendar data overlapped with event
1778
1779
		$date_begin = bab_mktime($this->date_begin);
1780
		$date_end	= bab_mktime($this->date_end);
1781
1782
		include_once $GLOBALS['babInstallPath']."utilit/eventperiod.php";
1783
		$event = new bab_eventPeriodModified($date_begin, $date_end, $this->id_user);
1784
		$event->types = BAB_PERIOD_VACATION;
1785
		bab_fireEvent($event);
1786
	}
1787
1788
1789
	public function getTitle()
1790
	{
1791
	    if (isset($this->todelete) && $this->todelete) {
1792
	        return absences_translate('vacation request to delete');
1793
	    }
1794
	    
1795
		return absences_translate('vacation request');
1796
	}
1797
1798
1799 View Code Duplication
	public function getNotifyFields()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1800
	{
1801
		return array(
1802
			absences_translate('From') 		=> bab_shortDate(bab_mktime($this->date_begin)),
1803
			absences_translate('Until')		=> bab_shortDate(bab_mktime($this->date_end)),
1804
			absences_translate('Quantity') 	=> absences_quantity($this->getTotalDays(), 'D')
1805
		);
1806
	}
1807
1808
1809 View Code Duplication
	public function getYear()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1810
	{
1811
		$year = (int) substr($this->date_begin, 0, 4);
1812
1813
		if (0 === $year)
1814
		{
1815
			return null;
1816
		}
1817
1818
		return $year;
1819
	}
1820
1821
1822 View Code Duplication
	public function getArchiveYear()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1823
	{
1824
		require_once $GLOBALS['babInstallPath'].'utilit/dateTime.php';
1825
		$year = (int) substr($this->date_begin, 0, 4);
1826
1827
		if (0 === $year)
1828
		{
1829
			return null;
1830
		}
1831
1832
		$day = absences_getVacationOption('archivage_day');
1833
		$month = absences_getVacationOption('archivage_month');
1834
1835
		$currentYear = new BAB_DateTime($year, $month, $day);
1836
		if($this->date_begin < $currentYear->getIsoDate()){
1837
			$year = $year-1;
1838
		}
1839
1840
		return $year;
1841
	}
1842
1843 View Code Duplication
	public function archive()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1844
	{
1845
		global $babDB;
1846
1847
		$babDB->db_query("
1848
			UPDATE absences_entries
1849
			SET archived=".$babDB->quote(1)."
1850
			WHERE
1851
				id=".$babDB->quote($this->id)."
1852
		");
1853
	}
1854
1855
1856 View Code Duplication
	public function setNotified()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1857
	{
1858
		global $babDB;
1859
1860
		if (!$this->id)
1861
		{
1862
			throw new Exception('Missing ID');
1863
		}
1864
1865
		$babDB->db_query("
1866
			UPDATE absences_entries
1867
				SET appr_notified=".$babDB->quote(1)."
1868
			WHERE
1869
				id=".$babDB->quote($this->id)."
1870
		");
1871
	}
1872
1873
1874
	public function getManagerEditUrl()
1875
	{
1876
		$addon = bab_getAddonInfosInstance('absences');
1877
		$ts = bab_mktime($this->date_begin);
1878
		return $addon->getUrl().'vacuser&idx=period&id='.$this->id.'&year='.date('Y', $ts).'&month='.date('n', $ts).'&rfrom=1';
1879
	}
1880
1881
1882
	public function getManagerDeleteUrl()
1883
	{
1884
		$addon = bab_getAddonInfosInstance('absences');
1885
		$url = new bab_url($addon->getUrl().'vacadmb');
1886
		$url->idx = 'delete';
1887
		$url->id_entry = $this->id;
1888
		$url->from = $_SERVER['REQUEST_URI'];
1889
		return $url->toString();
1890
	}
1891
1892
1893
	public function getManagerFrame()
1894
	{
1895
		$W = bab_Widgets();
1896
		$vbox = $W->VBoxLayout();
1897
1898
		if ($this->isFixed())
1899
		{
1900
			$vbox->addItem($W->Icon(absences_translate('Fixed vacation period'), Func_Icons::ACTIONS_VIEW_CALENDAR_DAY));
1901
		} else {
1902
			$vbox->addItem($W->Link($W->Icon(absences_translate('Vacation request'), Func_Icons::ACTIONS_VIEW_LIST_DETAILS), absences_addon()->getUrl()."vacadmb&idx=morvw&id=".$this->id));
1903
		}
1904
		$vbox->addItem($W->Label(absences_DateTimePeriod($this->date_begin, $this->date_end)));
1905
1906
		return $vbox;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $vbox; (Widget_VBoxLayout) is incompatible with the return type declared by the abstract method absences_Request::getManagerFrame of type Widget_Frame.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1907
	}
1908
1909
1910
	/**
1911
	 *
1912
	 * @param bool 			$display_username	Affiche le nom du demandeur dans la card frame ou non, utile pour les listes contenant plusieurs demandeurs possibles
1913
	 * @param int			$rfrom				si les demandes de la liste sont modifiee par un gestionnaire ou gestionnaire delegue
1914
 	 * @param int			$ide				id entite de l'organigramme >0 si gestionnaire delegue seulement
1915
	 */
1916
	public function getCardFrame($display_username, $rfrom, $ide)
1917
	{
1918
		bab_functionality::includeOriginal('Icons');
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class bab_functionality as the method includeOriginal() does only exist in the following sub-classes of bab_functionality: Func_Archive, Func_Archive_Zip, Func_Archive_Zip_ZipArchive, Func_Archive_Zip_Zlib, Func_CalendarBackend, Func_CalendarBackend_Ovi, Func_ContextActions, Func_ContextActions_Article, Func_ContextActions_ArticleTopic, Func_Home, Func_Home_Ovidentia, Func_Icons, Func_Icons_Default, Func_Ovml, Func_Ovml_Container, Func_Ovml_Container_Addon, Func_Ovml_Container_Article, Func_Ovml_Container_ArticleCategories, Func_Ovml_Container_ArticleCategory, Func_Ovml_Container_ArticleCategoryNext, Func_Ovml_Container_ArticleCategoryPrevious, Func_Ovml_Container_ArticleFiles, Func_Ovml_Container_ArticleNext, Func_Ovml_Container_ArticlePrevious, Func_Ovml_Container_ArticleTopic, Func_Ovml_Container_ArticleTopicNext, Func_Ovml_Container_ArticleTopicPrevious, Func_Ovml_Container_ArticleTopics, Func_Ovml_Container_Articles, Func_Ovml_Container_ArticlesHomePages, Func_Ovml_Container_CalendarCategories, Func_Ovml_Container_CalendarEventDomains, Func_Ovml_Container_CalendarEvents, Func_Ovml_Container_CalendarGroupEvents, Func_Ovml_Container_CalendarResourceEvents, Func_Ovml_Container_CalendarUserEvents, Func_Ovml_Container_Calendars, Func_Ovml_Container_DbDirectories, Func_Ovml_Container_DbDirectory, Func_Ovml_Container_DbDirectoryAcl, Func_Ovml_Container_DbDirectoryEntry, Func_Ovml_Container_DbDirectoryEntryFields, Func_Ovml_Container_DbDirectoryFields, Func_Ovml_Container_DbDirectoryMemberFields, Func_Ovml_Container_DbDirectoryMembers, Func_Ovml_Container_Delegation, Func_Ovml_Container_DelegationAdministrators, Func_Ovml_Container_DelegationItems, Func_Ovml_Container_DelegationManaged, Func_Ovml_Container_Delegations, Func_Ovml_Container_DelegationsCategories, Func_Ovml_Container_DelegationsCategory, Func_Ovml_Container_DelegationsManaged, Func_Ovml_Container_Faq, Func_Ovml_Container_FaqNext, Func_Ovml_Container_FaqPrevious, Func_Ovml_Container_FaqQuestion, Func_Ovml_Container_FaqQuestionNext, Func_Ovml_Container_FaqQuestionPrevious, Func_Ovml_Container_FaqQuestions, Func_Ovml_Container_FaqSubCategories, Func_Ovml_Container_FaqSubCategory, Func_Ovml_Container_Faqs, Func_Ovml_Container_File, Func_Ovml_Container_FileFields, Func_Ovml_Container_FileNext, Func_Ovml_Container_FilePrevious, Func_Ovml_Container_Files, Func_Ovml_Container_Folder, Func_Ovml_Container_FolderNext, Func_Ovml_Container_FolderPrevious, Func_Ovml_Container_Folders, Func_Ovml_Container_Forum, Func_Ovml_Container_ForumNext, Func_Ovml_Container_ForumPrevious, Func_Ovml_Container_Forums, Func_Ovml_Container_IfEqual, Func_Ovml_Container_IfGreaterThan, Func_Ovml_Container_IfGreaterThanOrEqual, Func_Ovml_Container_IfIsSet, Func_Ovml_Container_IfLessThan, Func_Ovml_Container_IfLessThanOrEqual, Func_Ovml_Container_IfNotEqual, Func_Ovml_Container_IfNotIsSet, Func_Ovml_Container_IfUserMemberOfGroups, Func_Ovml_Container_Multipages, Func_Ovml_Container_ObjectsInfo, Func_Ovml_Container_OrgPathToEntity, Func_Ovml_Container_OrgUserEntities, Func_Ovml_Container_OvmlArray, Func_Ovml_Container_OvmlArrayFields, Func_Ovml_Container_OvmlSoap, Func_Ovml_Container_ParentsArticleCategory, Func_Ovml_Container_Post, Func_Ovml_Container_PostFiles, Func_Ovml_Container_RecentArticles, Func_Ovml_Container_RecentComments, Func_Ovml_Container_RecentFaqQuestions, Func_Ovml_Container_RecentFiles, Func_Ovml_Container_RecentPosts, Func_Ovml_Container_RecentThreads, Func_Ovml_Container_SitemapCustomNode, Func_Ovml_Container_SitemapEntries, Func_Ovml_Container_SitemapEntry, Func_Ovml_Container_SitemapPath, Func_Ovml_Container_Soap, Func_Ovml_Container_SubFolders, Func_Ovml_Container_Tags, Func_Ovml_Container_Thread, Func_Ovml_Container_TmProjects, Func_Ovml_Container_TmSpaces, Func_Ovml_Container_TmTaskFields, Func_Ovml_Container_TmTasks, Func_Ovml_Container_WaitingArticles, Func_Ovml_Container_WaitingComments, Func_Ovml_Container_WaitingFiles, Func_Ovml_Container_WaitingPosts, Func_Ovml_Function, Func_Ovml_Function_AOAddition, Func_Ovml_Function_AODivision, Func_Ovml_Function_AOModulus, Func_Ovml_Function_AOMultiplication, Func_Ovml_Function_AOSubtraction, Func_Ovml_Function_AddStyleSheet, Func_Ovml_Function_Addon, Func_Ovml_Function_Ajax, Func_Ovml_Function_ArticleTree, Func_Ovml_Function_CurrentNode, Func_Ovml_Function_FileTree, Func_Ovml_Function_Get, Func_Ovml_Function_GetCookie, Func_Ovml_Function_GetCsrfProtectToken, Func_Ovml_Function_GetCurrentAdmGroup, Func_Ovml_Function_GetLanguage, Func_Ovml_Function_GetPageTitle, Func_Ovml_Function_GetPath, Func_Ovml_Function_GetSelectedSkinPath, Func_Ovml_Function_GetSessionVar, Func_Ovml_Function_GetVar, Func_Ovml_Function_Header, Func_Ovml_Function_IfNotIsSet, Func_Ovml_Function_Include, Func_Ovml_Function_NextArticle, Func_Ovml_Function_Post, Func_Ovml_Function_PreviousArticle, Func_Ovml_Function_PreviousOrNextArticle, Func_Ovml_Function_PutArray, Func_Ovml_Function_PutSoapArray, Func_Ovml_Function_PutVar, Func_Ovml_Function_Recurse, Func_Ovml_Function_Request, Func_Ovml_Function_SetCookie, Func_Ovml_Function_SetSessionVar, Func_Ovml_Function_SitemapCustomNodeId, Func_Ovml_Function_SitemapMenu, Func_Ovml_Function_SitemapPosition, Func_Ovml_Function_SitemapUrl, Func_Ovml_Function_Translate, Func_Ovml_Function_UrlContent, Func_Ovml_Function_WebStat, Func_PortalAuthentication, Func_PortalAuthentication_AuthOvidentia, Func_PwdComplexity, Func_PwdComplexity_DefaultPortal, Func_SearchUi, Func_SitemapDynamicNode, Func_SitemapDynamicNode_Topic, Func_UserEditor, Func_WorkingHours, Func_WorkingHours_Ovidentia, Ovml_Container_Sitemap, bab_ArithmeticOperator, bab_Ovml_Container_Operator, bab_rgp. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1919
1920
		$W = bab_Widgets();
1921
		$layout = $W->HBoxLayout()->setHorizontalSpacing(2,'em')->addClass('widget-full-width');
1922
		$frame = $W->Frame(null, $layout);
1923
1924
		$frame->addClass(Func_Icons::ICON_LEFT_16);
1925
		$frame->addClass('absences-entry-cardframe');
1926
		$layout->addItem($col1 = $W->VBoxLayout()->setVerticalSpacing(.5,'em'));
1927
		$layout->addItem($col2 = $W->VBoxLayout()->setVerticalSpacing(.3,'em'));
1928
1929
		if ($this->isPrevisonal())
1930
		{
1931
			$btn = $W->Link($W->Button()->addItem($W->Label(absences_translate('Submit for approval'))), absences_addon()->getUrl()."vacuser&idx=subprev&id_entry=".$this->id);
1932
			$btn->setSizePolicy('display-opened');
1933
			$layout->addItem($btn);
1934
		}
1935
1936
		if ($this->isFolderPrevisonal())
1937
		{
1938
			$first = $this->getFolderFirst();
1939
			if ($first && $first->id === $this->id)
1940
			{
1941
				$btn = $W->Link($W->Button()->addItem($W->Label(absences_translate('Submit requests for approval'))), absences_addon()->getUrl()."vacuser&idx=subprev&id_entry=".$this->id);
1942
				$btn->setSizePolicy('display-closed');
1943
				$layout->addItem($btn);
1944
			}
1945
		}
1946
1947
1948
		$layout->addItem($col3 = $W->VBoxLayout()->setVerticalSpacing(.5,'em'));
1949
1950
		if ($this->isFixed())
1951
		{
1952
			$col1->addItem($W->Icon(absences_translate('Fixed vacation period'), Func_Icons::ACTIONS_VIEW_CALENDAR_DAY));
1953
			$col1->setCanvasOptions($col1->Options()->width(25,'em'));
0 ignored issues
show
Bug introduced by
It seems like $col1->Options()->width(25, 'em') targeting Widget_CanvasOptions::width() can also be of type double; however, Widget_Item::setCanvasOptions() does only seem to accept object<Widget_CanvasOptions>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1954
		} else {
1955
			$col1->addItem($W->Link($W->Icon(absences_translate('Vacation request'), Func_Icons::ACTIONS_VIEW_LIST_DETAILS), absences_addon()->getUrl()."vacuser&idx=morve&id=".$this->id));
1956
			$col1->setCanvasOptions($col1->Options()->width(25,'em'));
0 ignored issues
show
Bug introduced by
It seems like $col1->Options()->width(25, 'em') targeting Widget_CanvasOptions::width() can also be of type double; however, Widget_Item::setCanvasOptions() does only seem to accept object<Widget_CanvasOptions>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1957
1958
			$col1->addItem($this->getStatusIcon());
1959
		}
1960
1961
		$col2->setSizePolicy(Widget_SizePolicy::MAXIMUM);
1962
1963
		$col2->addItem($W->Title(absences_DateTimePeriod($this->date_begin, $this->date_end), 5));
1964
1965
		if ($display_username)
1966
		{
1967
			$col2->addItem($this->labelledValue(absences_translate('Owner'), $this->getUserName()));
1968
		}
1969
1970
		$col2->addItem($this->labelledValue(absences_translate('Quantity'), absences_vacEntryQuantity($this->id)));
1971
1972
1973
1974
1975 View Code Duplication
		if ($this->canModify())
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1976
		{
1977
			$col3->addItem($W->Link($W->Icon(absences_translate('Modify'), Func_Icons::ACTIONS_DOCUMENT_EDIT), $this->getEditUrl($rfrom, $ide)));
1978
		}
1979
1980
		if ($this->canDelete())
1981
		{
1982
			$urldelete = absences_addon()->getUrl()."vacuser&idx=delete&id_entry=".$this->id;
1983
			$col3->addItem($W->Link($W->Icon(absences_translate('Delete'), Func_Icons::ACTIONS_EDIT_DELETE), $urldelete));
1984
		}
1985
1986
		$frame->setTitle(sprintf(absences_translate('Created the %s'), bab_shortDate(bab_mktime($this->createdOn()))));
1987
1988
		return $frame;
1989
	}
1990
1991
1992
1993
1994
1995
	/**
1996
	 * @return string
1997
	 */
1998
	public function getEditUrl($rfrom, $ide = null)
1999
	{
2000
		$begin_ts = bab_mktime($this->date_begin);
2001
		$url = absences_addon()->getUrl()."vacuser&idx=period&id=".$this->id."&year=".date('Y',$begin_ts)."&month=".date('n',$begin_ts);
2002
2003
		if (isset($rfrom))
2004
		{
2005
			$url .= '&rfrom='.$rfrom;
2006
		}
2007
2008
		if (isset($ide))
2009
		{
2010
			$url .= '&ide='.$ide;
2011
		}
2012
2013
		return $url;
2014
	}
2015
2016
2017
2018
	/**
2019
	 * Delete the vacation request
2020
	 * @return bool
2021
	 */
2022
	public function delete()
2023
	{
2024
2025
2026
2027
		global $babDB;
2028
2029
		if (!$this->getRow())
2030
		{
2031
			return false;
2032
		}
2033
2034
		parent::delete();
2035
		
2036
		if ('0000-00-00 00:00:00' !== $this->date_begin && '0000-00-00 00:00:00' !== $this->date_end) {
2037
    		include_once $GLOBALS['babInstallPath']."utilit/dateTime.php";
2038
    		$date_begin = BAB_DateTime::fromIsoDateTime($this->date_begin);
2039
    		$date_end	= BAB_DateTime::fromIsoDateTime($this->date_end);
2040
    
2041
    		$period = absences_getPeriod($this->id, $this->id_user, $date_begin, $date_end);
2042
    		if (null !== $period)
2043
    		{
2044
    			$period->delete();
2045
    		}
2046
		}
2047
		$babDB->db_query("DELETE FROM absences_dynamic_rights WHERE id_entry=".$babDB->quote($this->id));
2048
		$babDB->db_query("DELETE FROM ".ABSENCES_ENTRIES_ELEM_TBL." WHERE id_entry=".$babDB->quote($this->id)."");
2049
		$babDB->db_query("DELETE FROM ".ABSENCES_ENTRIES_TBL." WHERE id=".$babDB->quote($this->id));
2050
2051
		$this->applyDynamicRight();
2052
		
2053
		if (isset($date_begin) && isset($date_end)) {
2054
    		$cal_begin = clone $date_begin;
2055
    		$cal_end = clone $date_end;
2056
    
2057
    		$cal_end->add(1, BAB_DATETIME_MONTH);
2058
    
2059
    		while ($cal_begin->getTimeStamp() <= $cal_end->getTimeStamp()) {
2060
    			$month	= $cal_begin->getMonth();
2061
    			$year	= $cal_begin->getYear();
2062
    			absences_updateCalendar($this->id_user, $year, $month);
2063
    			$cal_begin->add(1, BAB_DATETIME_MONTH);
2064
    		}
2065
    		
2066
    		$this->addMovement(
2067
    		        sprintf(
2068
    		                absences_translate('The vacation entry from %s to %s has been deleted'),
2069
    		                $date_begin->shortFormat(true),
2070
    		                $date_end->shortFormat(true)
2071
    		        )
2072
    		);
2073
		}
2074
2075
		
2076
2077
		return true;
2078
	}
2079
2080
2081
2082
2083
2084
2085
2086
	/**
2087
	 * (non-PHPdoc)
2088
	 * @see absences_Request::notifyOwner()
2089
	 */
2090
	public function notifyOwner()
2091
	{
2092
		parent::notifyOwner();
2093
2094
		if ('Y' != $this->status)
2095
		{
2096
			return;
2097
		}
2098
2099
		$agent = $this->getAgent();
2100
		$emails = $agent->getEmails();
2101
2102
		if (empty($emails))
2103
		{
2104
			return;
2105
		}
2106
2107
		require_once dirname(__FILE__).'/request.notify.php';
2108
		absences_notifyEntryOwnerEmails(array($this), $emails);
2109
	}
2110
2111
}
2112
2113
2114
2115
2116
class absences_EntryException extends Exception
2117
{
2118
	/**
2119
	 * Optional entry associated to error (unsaved entry object)
2120
	 * @var absences_Entry
2121
	 */
2122
	public $entry;
2123
2124
2125
	/**
2126
	 * Define if the exception if blocking a save
2127
	 * @var bool
2128
	 */
2129
	public $blocking = true;
2130
}
2131
2132
2133
2134
/**
2135
 * Vacation requests
2136
 * Sorted by creation date
2137
 */
2138
class absences_EntryIterator extends absences_Iterator
2139
{
2140
2141
	/**
2142
	 *
2143
	 * @var string
2144
	 */
2145
	public $orderby = 'e.createdOn ASC';
2146
2147
2148
	/**
2149
	 *
2150
	 * @var string
2151
	 */
2152
	public $folder;
2153
2154
	/**
2155
	 *
2156
	 * @var array
2157
	 */
2158
	public $users;
2159
2160
2161
	/**
2162
	 *
2163
	 * @var mixed (array|string)
2164
	 */
2165
	public $status;
2166
2167
2168
	/**
2169
	 * Entry to ignore in the folder
2170
	 * @var int
2171
	 */
2172
	public $id_entry_folder;
2173
2174
2175
	/**
2176
	 * Filtrer les demandes necessitant ou pas un email aux approbateurs
2177
	 * @var int
2178
	 */
2179
	public $appr_notified;
2180
2181
	/**
2182
	 * Filtrer les demandes avec unes instance d'approbation
2183
	 * @var bool
2184
	 */
2185
	public $idfai_set;
2186
2187
2188
	/**
2189
	 * Filtrer les demandes avec une liste d'instances d'approbation
2190
	 * @var array
2191
	 */
2192
	public $appr_idfai;
2193
2194
2195
	/**
2196
	 * Search all request created before this date time
2197
	 * @var string
2198
	 */
2199
	public $createdOn;
2200
2201
	/**
2202
	 * Search all request modified before this date
2203
	 * @var string
2204
	 */
2205
	public $modifiedOn;
2206
2207
2208
	/**
2209
	 * Datetime
2210
	 * @var string
2211
	 */
2212
	public $from;
2213
2214
2215
	/**
2216
	 * Datetime
2217
	 * @var string
2218
	 */
2219
	public $to;
2220
2221
2222
	/**
2223
	 * Filtrer les demandes par annee
2224
	 * @var int
2225
	 */
2226
	public $year;
2227
2228
2229
	/**
2230
	 * Filtrer les demandes par organisation
2231
	 * @var int
2232
	 */
2233
	public $organization;
2234
2235
2236
	/**
2237
	 * Filtrer les demandes par statut d'archivage
2238
	 * @var int
2239
	 */
2240
	public $archived = 0;
2241
	
2242
	/**
2243
	 * with at least one element with this right
2244
	 */
2245
	public $id_right;
2246
2247
2248
2249 6
	public function getObject($data)
2250
	{
2251
2252 6
		$entry = new absences_Entry;
2253 6
		$entry->setRow($data);
2254 6
		return $entry;
2255
2256
	}
2257
	
2258
	/**
2259
	 * @return string
2260
	 */
2261 21
	protected function getBaseQuery()
2262
	{
2263
	    return 'SELECT e.* 
2264
				FROM
2265
					absences_entries e 
2266 21
				';
2267
	
2268
	}
2269
	
2270
	
2271
	/**
2272
	 * @return array
2273
	 */
2274 21
	protected function getWhere()
2275
	{
2276 21
	    global $babDB;
2277
	    
2278 21
	    $where = array();
2279 21
	    if (isset($this->folder))
2280 21
	    {
2281
	        $where[] = 'e.folder='.$babDB->quote($this->folder);
2282
	    
2283
	        if (isset($this->id_entry_folder))
2284
	        {
2285
	            $where[] = 'e.id<>'.$babDB->quote($this->id_entry_folder);
2286
	        }
2287
	    }
2288
	    
2289 21
	    if (!empty($this->users))
2290 21
	    {
2291 21
	        $where[] = 'e.id_user IN('.$babDB->quote($this->users).')';
2292 21
	    }
2293
	    
2294 21
	    if (isset($this->status))
2295 21
	    {
2296 7
	        $where[] = 'e.status IN('.$babDB->quote($this->status).')';
2297 7
	    }
2298
	    
2299 21
	    if (isset($this->createdOn))
2300 21
	    {
2301 15
	        $where[] = 'e.createdOn<='.$babDB->quote($this->createdOn);
2302 15
	    }
2303
	    
2304 21
	    if (isset($this->modifiedOn))
2305 21
	    {
2306
	        $where[] = 'e.`date`<='.$babDB->quote($this->modifiedOn);
2307
	    }
2308
	    
2309 21
	    if (isset($this->appr_notified))
2310 21
	    {
2311
	        $where[] = 'e.appr_notified='.$babDB->quote($this->appr_notified);
2312
	    }
2313
	    
2314 21
	    if (isset($this->idfai_set) && $this->idfai_set)
2315 21
	    {
2316
	        $where[] = 'e.idfai>'.$babDB->quote(0);
2317
	    }
2318
	    
2319 21
	    if (isset($this->appr_idfai))
2320 21
	    {
2321
	        $where[] = 'e.idfai IN('.$babDB->quote($this->appr_idfai).')';
2322
	    }
2323
	    
2324 21
	    if (isset($this->from))
2325 21
	    {
2326
	        $where[] = 'e.date_end >'.$babDB->quote($this->from);
2327
	    }
2328
	    
2329 21
	    if (isset($this->to))
2330 21
	    {
2331
	        $where[] = 'e.date_begin <'.$babDB->quote($this->to);
2332
	    }
2333
	    
2334 21
	    if (isset($this->startTo))
2335 21
	    {
2336
	        $where[] = 'e.date_begin <='.$babDB->quote($this->startTo);
0 ignored issues
show
Bug introduced by
The property startTo does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
2337
	    }
2338
	    
2339 21
	    if (isset($this->startFrom))
2340 21
	    {
2341
	        $where[] = 'e.date_begin >='.$babDB->quote($this->startFrom);
0 ignored issues
show
Bug introduced by
The property startFrom does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
2342
	    }
2343
	    
2344 21
	    if (isset($this->year))
2345 21
	    {
2346
	        $where[] = 'YEAR(e.date_begin)='.$babDB->quote($this->year);
2347
	    }
2348
	    
2349 21
	    if (isset($this->organization) && $this->organization)
2350 21
	    {
2351
	        $where[] = 'e.id_user=p.id_user';
2352
	        $where[] = 'p.id_organization='.$babDB->quote($this->organization);
2353
	    }
2354
	    
2355 21
	    if (isset($this->archived))
2356 21
	    {
2357 21
	        $where[] = 'e.archived='.$babDB->quote($this->archived);
2358 21
	    }
2359
	    	
2360 21
	    if (isset($this->id_right))
2361 21
	    {
2362 21
	        $where[] = 'e.id IN(SELECT id_entry FROM absences_entries_elem WHERE id_right='.$babDB->quote($this->id_right).')';
2363 21
	    }
2364
	    
2365 21
	    return $where;
2366
	}
2367
2368
2369
2370 21
	public function executeQuery()
2371
	{
2372 21
		if(is_null($this->_oResult))
2373 21
		{
2374 21
			$req = $this->getBaseQuery();
2375
2376 21
			$where = $this->getWhere();
2377
			
2378 21
			if (isset($this->organization) && $this->organization)
2379 21
			{
2380
			    $req .= ', absences_personnel p';
2381
			}
2382
2383
			if ($where)
0 ignored issues
show
Bug Best Practice introduced by
The expression $where of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2384 21
			{
2385 21
				$req .= ' WHERE '.implode(' AND ', $where);
2386 21
			}
2387
2388 21
			$req .= ' ORDER BY '.$this->orderby;
2389
			
2390 21
			$this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2391 21
		}
2392 21
	}
2393
}
2394
2395
2396
/**
2397
 * Entry elements iterator joined on entries
2398
 */
2399
class absences_ElementEntryIterator extends absences_EntryIterator
2400
{
2401
    
2402
    public function getObject($data)
2403
    {
2404
        require_once dirname(__FILE__).'/type.class.php';
2405
        require_once dirname(__FILE__).'/right.class.php';
2406
        require_once dirname(__FILE__).'/entry_elem.class.php';
2407
        
2408
        $entry_row = $this->getRowByPrefix($data, 'entry');
2409
        $entry = new absences_Entry($entry_row['id']);
0 ignored issues
show
Unused Code introduced by
The call to absences_Entry::__construct() has too many arguments starting with $entry_row['id'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2410
        $entry->setRow($entry_row);
2411
    
2412
        $type_row = $this->getRowByPrefix($data, 'type');
2413
        $type = new absences_Type($type_row['id']);
2414
        $type->setRow($type_row);
2415
    
2416
        $right_row = $this->getRowByPrefix($data, 'right');
2417
        $right = new absences_Right($right_row['id']);
2418
        $right->setRow($right_row);
2419
        $right->setType($type);
2420
    
2421
        $elem_row = $this->getRowByPrefix($data, 'elem');
2422
        $elem = new absences_EntryElem();
2423
        $elem->setRow($elem_row);
2424
        $elem->setRight($right);
2425
        $elem->setEntry($entry);
2426
    
2427
        return $elem;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $elem; (absences_EntryElem) is incompatible with the return type of the parent method absences_EntryIterator::getObject of type absences_Entry.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2428
    }
2429
    
2430
    
2431
    /**
2432
     * @return string
2433
     */
2434
    protected function getBaseQuery()
2435
    {
2436
        return 'SELECT 
2437
                
2438
                    e.id                    entry__id,
2439
                    e.id_user               entry__id_user,
2440
                    e.createdby             entry__createdby,
2441
                    e.date_begin            entry__date_begin,
2442
                    e.date_end              entry__date_end,
2443
                    e.idfai                 entry__idfai,
2444
                    e.comment               entry__comment,
2445
                    e.createdOn             entry__createdOn,
2446
                    e.date                  entry__date,
2447
                    e.status                entry__status,
2448
                    e.comment2              entry__comment2,
2449
                    e.id_approver           entry__id_approver,
2450
                    e.folder                entry__folder,
2451
                    e.creation_type         entry__creation_type,
2452
                    e.appr_notified         entry__appr_notified,
2453
                    e.archived              entry__archived,
2454
                    e.todelete              entry__todelete,
2455
                    e.firstconfirm          entry__firstconfirm,
2456
                
2457
                    ee.id 					elem__id,
2458
    				ee.id_entry 			elem__id_entry,
2459
    				ee.id_right				elem__id_right,
2460
    				ee.quantity				elem__quantity,
2461
    			    ee.date_begin           elem__date_begin,
2462
    			    ee.date_end             elem__date_end,
2463
    
2464
    				r.id					right__id,
2465
    				r.id_creditor			right__id_creditor,
2466
    				r.kind					right__kind,
2467
    			    r.createdOn			    right__createdOn,
2468
    				r.date_entry			right__date_entry,
2469
    				r.date_begin			right__date_begin,
2470
    				r.date_end				right__date_end,
2471
    				r.quantity				right__quantity,
2472
    				r.quantity_unit			right__quantity_unit,
2473
    				r.quantity_inc_month 	right__quantity_inc_month,
2474
    				r.quantity_inc_max 		right__quantity_inc_max,
2475
    				r.quantity_inc_last		right__quantity_inc_last,
2476
    				r.id_type				right__id_type,
2477
    				r.description			right__description,
2478
    				r.active				right__active,
2479
    				r.cbalance	 			right__cbalance,
2480
    				r.date_begin_valid		right__date_begin_valid,
2481
    				r.date_end_valid		right__date_end_valid,
2482
    				r.date_end_fixed		right__date_end_fixed,
2483
    				r.date_begin_fixed		right__date_begin_fixed,
2484
                    r.hide_empty 		    right__hide_empty,
2485
    				r.no_distribution 		right__no_distribution,
2486
    				r.use_in_cet			right__use_in_cet,
2487
    				r.id_rgroup				right__id_rgroup,
2488
    				r.earlier				right__earlier,
2489
    				r.earlier_begin_valid 	right__earlier_begin_valid,
2490
    				r.earlier_end_valid		right__earlier_end_valid,
2491
    				r.later					right__later,
2492
    				r.later_begin_valid		right__later_begin_valid,
2493
    				r.later_end_valid		right__later_end_valid,
2494
    				r.delay_before			right__delay_before,
2495
    				r.id_report_type		right__id_report_type,
2496
    				r.date_end_report		right__date_end_report,
2497
    				r.description_report	right__description_report,
2498
    				r.id_reported_from		right__id_reported_from,
2499
    				r.quantity_alert_days	right__quantity_alert_days,
2500
    				r.quantity_alert_types	right__quantity_alert_types,
2501
    				r.quantity_alert_begin	right__quantity_alert_begin,
2502
    				r.quantity_alert_end	right__quantity_alert_end,
2503
    				r.dynconf_types			right__dynconf_types,
2504
    				r.dynconf_begin			right__dynconf_begin,
2505
    				r.dynconf_end			right__dynconf_end,
2506
    				r.sync_status			right__sync_status,
2507
    				r.sync_update			right__sync_update,
2508
    				r.uuid					right__uuid,
2509
    				r.archived				right__archived,
2510
    				r.sortkey				right__sortkey,
2511
    				r.require_approval		right__require_approval,
2512
    
2513
    
2514
    				t.id					type__id,
2515
    				t.name					type__name,
2516
    				t.description			type__description,
2517
    				t.color					type__color
2518
				FROM
2519
					absences_entries e,
2520
                    absences_entries_elem ee,
2521
                    absences_rights r,
2522
                    absences_types t 
2523
				';
2524
    
2525
    }
2526
    
2527
    /**
2528
     * @return array
2529
     */
2530
    protected function getWhere()
2531
    {
2532
        $where = parent::getWhere();
2533
        
2534
        $where[] = 'ee.id_entry=e.id';
2535
        $where[] = 'ee.id_right=r.id';
2536
        $where[] = 'r.id_type=t.id';
2537
        
2538
        return $where;
2539
    }
2540
}
2541
2542
2543
2544
/**
2545
 * Entry elements iterator
2546
 */
2547
class absences_EntryElemIterator extends absences_Iterator
2548
{
2549
	/**
2550
	 *
2551
	 * @var absences_Entry
2552
	 */
2553
	public $entry;
2554
2555
	/**
2556
	 * Filter by a list of id vaction types
2557
	 * @var int[]
2558
	 */
2559
	public $types;
2560
2561
2562
	public function getObject($data)
2563
	{
2564
		require_once dirname(__FILE__).'/type.class.php';
2565
		require_once dirname(__FILE__).'/right.class.php';
2566
		require_once dirname(__FILE__).'/entry_elem.class.php';
2567
2568
		$type_row = $this->getRowByPrefix($data, 'type');
2569
		$type = new absences_Type($type_row['id']);
2570
		$type->setRow($type_row);
2571
2572
		$right_row = $this->getRowByPrefix($data, 'right');
2573
		$right = new absences_Right($right_row['id']);
2574
		$right->setRow($right_row);
2575
		$right->setType($type);
2576
2577
		$elem_row = $this->getRowByPrefix($data, 'elem');
2578
		$elem = new absences_EntryElem();
2579
		$elem->setRow($elem_row);
2580
		$elem->setRight($right);
2581
		$elem->setEntry($this->entry);
2582
2583
		return $elem;
2584
	}
2585
2586
2587
2588
	public function executeQuery()
2589
	{
2590
		if(is_null($this->_oResult))
2591
		{
2592
			global $babDB;
2593
			$req = 'SELECT
2594
2595
				ee.id 					elem__id,
2596
				ee.id_entry 			elem__id_entry,
2597
				ee.id_right				elem__id_right,
2598
				ee.quantity				elem__quantity,
2599
			    ee.date_begin           elem__date_begin,
2600
			    ee.date_end             elem__date_end,
2601
2602
				r.id					right__id,
2603
				r.id_creditor			right__id_creditor,
2604
				r.kind					right__kind,
2605
			    r.createdOn			    right__createdOn,
2606
				r.date_entry			right__date_entry,
2607
				r.date_begin			right__date_begin,
2608
				r.date_end				right__date_end,
2609
				r.quantity				right__quantity,
2610
				r.quantity_unit			right__quantity_unit,
2611
				r.quantity_inc_month 	right__quantity_inc_month,
2612
				r.quantity_inc_max 		right__quantity_inc_max,
2613
				r.quantity_inc_last		right__quantity_inc_last,
2614
				r.id_type				right__id_type,
2615
				r.description			right__description,
2616
				r.active				right__active,
2617
				r.cbalance	 			right__cbalance,
2618
				r.date_begin_valid		right__date_begin_valid,
2619
				r.date_end_valid		right__date_end_valid,
2620
				r.date_end_fixed		right__date_end_fixed,
2621
				r.date_begin_fixed		right__date_begin_fixed,
2622
			    r.hide_empty 		    right__hide_empty,
2623
				r.no_distribution 		right__no_distribution,
2624
				r.use_in_cet			right__use_in_cet,
2625
				r.id_rgroup				right__id_rgroup,
2626
				r.earlier				right__earlier,
2627
				r.earlier_begin_valid 	right__earlier_begin_valid,
2628
				r.earlier_end_valid		right__earlier_end_valid,
2629
				r.later					right__later,
2630
				r.later_begin_valid		right__later_begin_valid,
2631
				r.later_end_valid		right__later_end_valid,
2632
				r.delay_before			right__delay_before,
2633
				r.id_report_type		right__id_report_type,
2634
				r.date_end_report		right__date_end_report,
2635
				r.description_report	right__description_report,
2636
				r.id_reported_from		right__id_reported_from,
2637
				r.quantity_alert_days	right__quantity_alert_days,
2638
				r.quantity_alert_types	right__quantity_alert_types,
2639
				r.quantity_alert_begin	right__quantity_alert_begin,
2640
				r.quantity_alert_end	right__quantity_alert_end,
2641
				r.dynconf_types			right__dynconf_types,
2642
				r.dynconf_begin			right__dynconf_begin,
2643
				r.dynconf_end			right__dynconf_end,
2644
				r.sync_status			right__sync_status,
2645
				r.sync_update			right__sync_update,
2646
				r.uuid					right__uuid,
2647
				r.archived				right__archived,
2648
				r.sortkey				right__sortkey,
2649
				r.require_approval		right__require_approval,
2650
2651
2652
				t.id					type__id,
2653
				t.name					type__name,
2654
				t.description			type__description,
2655
				t.color					type__color
2656
			FROM
2657
				absences_entries_elem ee,
2658
				absences_rights r,
2659
				absences_types t
2660
2661
			WHERE
2662
				ee.id_entry='.$babDB->quote($this->entry->id).'
2663
				AND ee.id_right = r.id
2664
				AND r.id_type = t.id';
2665
2666
			if (isset($this->types)) {
2667
			    $req .= ' AND t.id IN('.$babDB->quote($this->types).')';
2668
			}
2669
2670
		    $req .= ' ORDER BY ee.date_begin';
2671
2672
			$this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2673
		}
2674
	}
2675
2676
2677
}
2678
2679
2680
2681
/**
2682
 * Entry periods iterator
2683
 */
2684
class absences_EntryPeriodIterator extends absences_Iterator
2685
{
2686
    /**
2687
     *
2688
     * @var absences_Entry
2689
     */
2690
    public $entry;
2691
2692
2693
2694
    public function getObject($data)
2695
    {
2696
        require_once dirname(__FILE__).'/entry_period.class.php';
2697
2698
2699
2700
        $elem_row = $this->getRowByPrefix($data, 'period');
2701
        $elem = new absences_EntryPeriod();
2702
        $elem->setRow($elem_row);
2703
        $elem->setEntry($this->entry);
2704
2705
        return $elem;
2706
    }
2707
2708 28
2709
2710 28
    public function executeQuery()
2711 28
    {
2712 28
        if(is_null($this->_oResult))
2713
        {
2714
            global $babDB;
2715
            $req = 'SELECT
2716
2717
				ep.id 					period__id,
2718
				ep.id_entry 			period__id_entry,
2719
			    ep.date_begin           period__date_begin,
2720
			    ep.date_end             period__date_end
2721
2722
			FROM
2723 28
				absences_entries_periods ep
2724
			WHERE
2725
				ep.id_entry='.$babDB->quote($this->entry->id).'';
2726 28
2727
2728 28
            $req .= ' ORDER BY ep.date_begin';
2729 28
2730 28
            $this->setMySqlResult($this->getDataBaseAdapter()->db_query($req));
2731
        }
2732
    }
2733
2734
2735
}
2736