1<?php
2
3namespace Sabre\VObject\Recur;
4
5use DateTimeInterface;
6use Iterator;
7use Sabre\VObject\DateTimeParser;
8
9/**
10 * RRuleParser.
11 *
12 * This class receives an RRULE string, and allows you to iterate to get a list
13 * of dates in that recurrence.
14 *
15 * For instance, passing: FREQ=DAILY;LIMIT=5 will cause the iterator to contain
16 * 5 items, one for each day.
17 *
18 * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
19 * @author Evert Pot (http://evertpot.com/)
20 * @license http://sabre.io/license/ Modified BSD License
21 */
22class RDateIterator implements Iterator
23{
24    /**
25     * Creates the Iterator.
26     *
27     * @param string|array      $rrule
28     * @param DateTimeInterface $start
29     */
30    public function __construct($rrule, DateTimeInterface $start)
31    {
32        $this->startDate = $start;
33        $this->parseRDate($rrule);
34        $this->currentDate = clone $this->startDate;
35    }
36
37    /* Implementation of the Iterator interface {{{ */
38
39    public function current()
40    {
41        if (!$this->valid()) {
42            return;
43        }
44
45        return clone $this->currentDate;
46    }
47
48    /**
49     * Returns the current item number.
50     *
51     * @return int
52     */
53    public function key()
54    {
55        return $this->counter;
56    }
57
58    /**
59     * Returns whether the current item is a valid item for the recurrence
60     * iterator.
61     *
62     * @return bool
63     */
64    public function valid()
65    {
66        return $this->counter <= count($this->dates);
67    }
68
69    /**
70     * Resets the iterator.
71     */
72    public function rewind()
73    {
74        $this->currentDate = clone $this->startDate;
75        $this->counter = 0;
76    }
77
78    /**
79     * Goes on to the next iteration.
80     */
81    public function next()
82    {
83        ++$this->counter;
84        if (!$this->valid()) {
85            return;
86        }
87
88        $this->currentDate =
89            DateTimeParser::parse(
90                $this->dates[$this->counter - 1],
91                $this->startDate->getTimezone()
92            );
93    }
94
95    /* End of Iterator implementation }}} */
96
97    /**
98     * Returns true if this recurring event never ends.
99     *
100     * @return bool
101     */
102    public function isInfinite()
103    {
104        return false;
105    }
106
107    /**
108     * This method allows you to quickly go to the next occurrence after the
109     * specified date.
110     *
111     * @param DateTimeInterface $dt
112     */
113    public function fastForward(DateTimeInterface $dt)
114    {
115        while ($this->valid() && $this->currentDate < $dt) {
116            $this->next();
117        }
118    }
119
120    /**
121     * The reference start date/time for the rrule.
122     *
123     * All calculations are based on this initial date.
124     *
125     * @var DateTimeInterface
126     */
127    protected $startDate;
128
129    /**
130     * The date of the current iteration. You can get this by calling
131     * ->current().
132     *
133     * @var DateTimeInterface
134     */
135    protected $currentDate;
136
137    /**
138     * The current item in the list.
139     *
140     * You can get this number with the key() method.
141     *
142     * @var int
143     */
144    protected $counter = 0;
145
146    /* }}} */
147
148    /**
149     * This method receives a string from an RRULE property, and populates this
150     * class with all the values.
151     *
152     * @param string|array $rrule
153     */
154    protected function parseRDate($rdate)
155    {
156        if (is_string($rdate)) {
157            $rdate = explode(',', $rdate);
158        }
159
160        $this->dates = $rdate;
161    }
162
163    /**
164     * Array with the RRULE dates.
165     *
166     * @var array
167     */
168    protected $dates = [];
169}
170