add docblocks to all files, classes, methods and variables
[bdrem.git] / src / bdrem / Source / Sql.php
1 <?php
2 /**
3  * Part of bdrem
4  *
5  * PHP version 5
6  *
7  * @category  Tools
8  * @package   Bdrem
9  * @author    Christian Weiske <cweiske@cweiske.de>
10  * @copyright 2014 Christian Weiske
11  * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
12  * @link      http://cweiske.de/bdrem.htm
13  */
14 namespace bdrem;
15
16 /**
17  * Fetch data from an SQL database
18  *
19  * @category  Tools
20  * @package   Bdrem
21  * @author    Christian Weiske <cweiske@cweiske.de>
22  * @copyright 2014 Christian Weiske
23  * @license   http://www.gnu.org/licenses/agpl.html GNU AGPL v3
24  * @link      http://cweiske.de/bdrem.htm
25  */
26 class Source_Sql
27 {
28     /**
29      * PDO data source description
30      * @var string
31      */
32     protected $dsn;
33
34     /**
35      * Database user name
36      * @var string
37      */
38     protected $user;
39
40     /**
41      * Database password
42      * @var string
43      */
44     protected $password;
45
46     /**
47      * Database table with event data
48      * @var string
49      */
50     protected $table;
51
52     /**
53      * Field configuration
54      * Keys:
55      * - date - array of columns with dates and their event title,
56      *          e.g. 'c_birthday' => 'Birthday'
57      * - name - array of name columns
58      * - nameFormat - sprintf-compatible name formatting instruction,
59      *                uses name columns
60      *
61      * @var array
62      */
63     protected $fields;
64
65     /**
66      * Set SQL server configuration
67      *
68      * @param array $config SQL server configuration with keys:
69      *                      dsn, user, password, table and fields
70      */
71     public function __construct($config)
72     {
73         $this->dsn      = $config['dsn'];
74         $this->user     = $config['user'];
75         $this->password = $config['password'];
76         $this->table    = $config['table'];
77         $this->fields   = $config['fields'];
78     }
79
80     /**
81      * Return all events for the given date range
82      *
83      * @param string  $strDate       Date the events shall be found for,
84      *                               YYYY-MM-DD
85      * @param integer $nDaysPrevious Include number of days before $strDate
86      * @param integer $nDaysNext     Include number of days after $strDate
87      *
88      * @return Event[] Array of matching event objects
89      */
90     public function getEvents($strDate, $nDaysPrevious, $nDaysNext)
91     {
92         $dbh = new \PDO(
93             $this->dsn, $this->user, $this->password,
94             array(\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION)
95         );
96         $arDays = $this->getDates($strDate, $nDaysPrevious, $nDaysNext);
97         $arEvents = array();
98
99         foreach ($this->fields['date'] as $field => $typeName) {
100             if (substr($this->dsn, 0, 5) == 'dblib') {
101                 //MS SQL Server does of course cook its own sht
102                 $sqlMonth = 'DATEPART(month, ' . $field . ')';
103                 $sqlDay = 'DATEPART(day, ' . $field . ')';
104             } else {
105                 $sqlMonth = 'EXTRACT(MONTH FROM ' . $field . ')';
106                 $sqlDay = 'EXTRACT(DAY FROM ' . $field . ')';
107             }
108
109             $parts = array();
110             foreach ($arDays as $month => $days) {
111                 $parts[] = '('
112                     . $sqlMonth . ' = ' . $dbh->quote($month, \PDO::PARAM_INT)
113                     . ' AND ' . $sqlDay . ' >= '
114                     . $dbh->quote(min($days), \PDO::PARAM_INT)
115                     . ' AND ' . $sqlDay . ' <= '
116                     . $dbh->quote(max($days), \PDO::PARAM_INT)
117                     . ')';
118             }
119             $sql = 'SELECT ' . $field . ' AS e_date'
120                 . ', ' . implode(', ', (array) $this->fields['name'])
121                 . ' FROM ' . $this->table
122                 . ' WHERE '
123                 . implode(' OR ', $parts);
124
125             $res = $dbh->query($sql);
126             while ($row = $res->fetchObject()) {
127                 $arNameData = array();
128                 foreach ((array) $this->fields['name'] as $fieldName) {
129                     $arNameData[] = $row->$fieldName;
130                 }
131                 $name = call_user_func_array(
132                     'sprintf',
133                     array_merge(
134                         array($this->fields['nameFormat']),
135                         $arNameData
136                     )
137                 );
138                 $event = new Event(
139                     $name, $typeName,
140                     str_replace('0000', '????', $row->e_date)
141                 );
142                 if ($event->isWithin($strDate, $nDaysPrevious, $nDaysNext)) {
143                     $arEvents[] = $event;
144                 }
145             }
146         }
147         return $arEvents;
148     }
149
150     /**
151      * Create an array of dates that are included in the given range.
152      *
153      * @param string  $strDate       Date the events shall be found for,
154      *                               YYYY-MM-DD
155      * @param integer $nDaysPrevious Include number of days before $strDate
156      * @param integer $nDaysNext     Include number of days after $strDate
157      *
158      * @return array Key is the month, value an array of days
159      */
160     protected function getDates($strDate, $nDaysPrevious, $nDaysNext)
161     {
162         $ts = strtotime($strDate) - 86400 * $nDaysPrevious;
163         $numDays = $nDaysPrevious + $nDaysNext;
164
165         $arDays = array();
166         do {
167             $arDays[(int) date('n', $ts)][] = (int) date('j', $ts);
168             $ts += 86400;
169         } while (--$numDays >= 0);
170         return $arDays;
171     }
172 }
173 ?>