Plop
A simple logging library for PHP
TimedRotatingFile.php
1 <?php
2 /*
3  This file is part of Plop, a simple logging library for PHP.
4 
5  Copyright © 2010-2014 François Poirotte
6 
7  Plop is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  Plop is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with Plop. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 namespace Plop\Handler;
22 
30 {
32  protected $when;
33 
35  protected $backupCount;
36 
38  protected $utc;
39 
41  protected $interval;
42 
44  protected $suffix;
45 
47  protected $extMatch;
48 
50  protected $dayOfWeek;
51 
53  protected $rolloverAt;
54 
56  protected static $dayNames = array(
57  'Monday',
58  'Tuesday',
59  'Wednesday',
60  'Thursday',
61  'Friday',
62  'Saturday',
63  'Sunday'
64  );
65 
118  public function __construct(
119  $filename,
120  $when = 'h',
121  $interval = 1,
122  $backupCount = 0,
123  $delay = false,
124  $utc = false
125  ) {
126  parent::__construct($filename, 'a', $delay);
127  $this->when = strtoupper($when);
128  $this->backupCount = $backupCount;
129  $this->utc = $utc;
130  $this->rolloverAt = null;
131  $this->dayOfWeek = null;
132 
133  if ($this->when == 'S') {
134  $this->interval = 1;
135  $this->suffix = '%Y-%m-%d_%H-%M-%S';
136  $this->extMatch = '^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}$';
137  } elseif ($this->when == 'M') {
138  $this->interval = 60;
139  $this->suffix = '%Y-%m-%d_%H-%M';
140  $this->extMatch = '^\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}$';
141  } elseif ($this->when == 'H') {
142  $this->interval = 60 * 60;
143  $this->suffix = '%Y-%m-%d_%H';
144  $this->extMatch = '^\\d{4}-\\d{2}-\\d{2}_\\d{2}$';
145  } elseif ($this->when == 'D' || $this->when == 'MIDNIGHT') {
146  $this->interval = 60 * 60 * 24;
147  $this->suffix = '%Y-%m-%d';
148  $this->extMatch = '^\\d{4}-\\d{2}-\\d{2}$';
149  } elseif (substr($this->when, 0, 1) == 'W') {
150  $this->interval = 60 * 60 * 24 * 7;
151  if (strlen($this->when) != 2) {
152  throw new \Plop\Exception(
153  sprintf(
154  'You must specify a day for weekly rollover '.
155  'from 0 to 6 (0 is Monday), not %s',
156  $this->when
157  )
158  );
159  }
160 
161  $day = ord($this->when[1]) - ord('0');
162  if ($day < 0 || $day > 6) {
163  throw new \Plop\Exception(
164  sprintf(
165  'Invalid day specified for weekly rollover: %s',
166  $this->when[1]
167  )
168  );
169  }
170 
171  $this->dayOfWeek = $day;
172  $this->suffix = '%Y-%m-%d';
173  $this->extMatch = '^\\d{4}-\\d{2}-\\d{2}$';
174  } else {
175  throw new \Plop\Exception(
176  sprintf(
177  'Invalid rollover interval specified: %s',
178  $this->when
179  )
180  );
181  }
182 
183  if (!is_int($interval) || $interval < 1) {
184  throw new \Plop\Exception(
185  'The interval should be an integer ' .
186  'greater than or equal to 1'
187  );
188  }
189 
190  $this->interval = $this->interval * $interval;
191  $this->rolloverAt = $this->computeRollover($this->getTime());
192  }
193 
204  protected function computeRollover($currentTime)
205  {
206  if ($this->when == 'MIDNIGHT') {
207  return strtotime("midnight + 1 day", $currentTime);
208  }
209 
210  if (substr($this->when, 0, 1) == 'W') {
211  return strtotime(
212  "next " . self::$dayNames[$this->dayOfWeek],
213  $currentTime
214  );
215  }
216 
217  return $currentTime + $this->interval;
218  }
219 
228  protected function getTime()
229  {
230  return time();
231  }
232 
234  protected function shouldRollover(\Plop\RecordInterface $record)
235  {
236  return ($this->getTime() >= $this->rolloverAt);
237  }
238 
245  protected function getFilesToDelete()
246  {
247  $dirName = dirname($this->baseFilename);
248  $baseName = basename($this->baseFilename);
249  $fileNames = scandir($dirName);
250  $result = array();
251  $prefix = $baseName . '.';
252  $plen = strlen($prefix);
253 
254  foreach ($fileNames as $fileName) {
255  if ($fileName == '.' || $fileName == '..') {
256  continue;
257  }
258 
259  if (!strncmp($fileName, $prefix, $plen)) {
260  $suffix = substr($fileName, $plen);
261  if (preg_match($this->extMatch, $suffix)) {
262  $result[] = $dirName . DIRECTORY_SEPARATOR . $fileName;
263  }
264  }
265  }
266 
267  sort($result);
268  $rlen = count($result);
269  if ($rlen < $this->backupCount) {
270  $result = array();
271  } else {
272  $result = array_slice($result, 0, $rlen - $this->backupCount);
273  }
274  return $result;
275  }
276 
278  protected function doRollover()
279  {
280  if (is_resource($this->stream)) {
281  fclose($this->stream);
282  }
283 
284  $t = $this->rolloverAt - $this->interval;
285 
286  if ($this->utc) {
287  $formatFunc = 'gmstrftime';
288  } else {
289  $formatFunc = 'strftime';
290  }
291 
292  $dfn = $this->baseFilename . '.' . $formatFunc($this->suffix, $t);
293  if (file_exists($dfn)) {
294  @unlink($dfn);
295  }
296 
297  rename($this->baseFilename, $dfn);
298  if ($this->backupCount > 0) {
299  foreach ($this->getFilesToDelete() as $s) {
300  @unlink($s);
301  }
302  }
303 
304  $this->mode = 'w';
305  $this->stream = $this->open();
306  $currentTime = time();
307  $newRolloverAt = $this->computeRollover($currentTime);
308  while ($newRolloverAt <= $currentTime) {
309  $newRolloverAt += $this->interval;
310  }
311  $this->rolloverAt = $newRolloverAt;
312  }
313 }
$dayOfWeek
Day of week (0=Monday...6=Sunday) when the log rotation happens.
shouldRollover(\Plop\RecordInterface $record)
Interface for a log record.
__construct($filename, $when= 'h', $interval=1, $backupCount=0, $delay=false, $utc=false)
$utc
Whether the files are named based on UTC time or local time.
static $dayNames
The names of days in English, starting with Monday.
$extMatch
A PCRE pattern that matches rotated files.
$when
Log rotation specification (eg. &#39;W&#39; or &#39;MIDNIGHT&#39;).
$backupCount
Number of backup log files to keep.
$suffix
The date format that will be used as a suffix for the log files.
An abstract class for handlers that must deal with file rotations.
An handler that writes logs to a file but also handles that file&#39;s rotation, based on time...
$interval
Interval between file rotations.
$rolloverAt
UNIX timestamp of the next log rotation.