Overview

Packages

  • Horde
    • Block
  • Klutz
  • None

Classes

  • Klutz
  • Klutz_Comic
  • Klutz_Comic_Bysize
  • Klutz_Comic_Direct
  • Klutz_Comic_Search
  • Klutz_Driver
  • Klutz_Driver_File
  • Klutz_Driver_Sql
  • Klutz_Image
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Klutz Driver implementation for comics as files.
  4:  *
  5:  * Required parameters:<pre>
  6:  *   'directory'  The main directory the comics are stored in
  7:  *   'sumsfile'   The filename to hold md5sums for images</pre>
  8:  *
  9:  * @author  Marcus I. Ryan <marcus@riboflavin.net>
 10:  * @since   Klutz 0.1
 11:  * @package Klutz
 12:  */
 13: class Klutz_Driver_File extends Klutz_Driver
 14: {
 15:     /**
 16:      * The base directory we store comics in.
 17:      *
 18:      * @var string
 19:      */
 20:     var $basedir = null;
 21: 
 22:     /**
 23:      * The format for the various subdirectories.
 24:      * WARNING: DO NOT CHANGE THIS!
 25:      *
 26:      * @var string
 27:      */
 28:     var $subdir = 'Ymd';
 29: 
 30:     /**
 31:      * The file we store unique image identifiers in.
 32:      *
 33:      * @var string
 34:      */
 35:     var $sumsfile = 'sums';
 36: 
 37:     /**
 38:      * The actual array of unique image identifiers (md5 sums right now).
 39:      *
 40:      * Key is the full path of the comic, value is md5
 41:      *
 42:      * @var array
 43:      */
 44:     var $diffs = array();
 45: 
 46:     /**
 47:      * Constructs a new file storage object.
 48:      *
 49:      * @param array $params  A hash containing connection parameters.
 50:      */
 51:     function Klutz_Driver_file($params = array())
 52:     {
 53:         if (empty($params['basedir'])) {
 54:             return null;
 55:         }
 56: 
 57:         $this->basedir = $params['basedir'];
 58:         if (substr($this->basedir,-1,1) != "/") {
 59:             $this->basedir .= "/";
 60:         }
 61: 
 62:         if (!empty($params['sumsfile'])) {
 63:             $this->sumsfile = $params['sumsfile'];
 64:         }
 65:         if (substr($this->sumsfile, 0, 1) != "/") {
 66:             $this->sumsfile = $this->basedir . $this->sumsfile;
 67:         }
 68: 
 69:         $this->loadSums();
 70:     }
 71: 
 72:     /**
 73:      * Load a list of unique identifiers for comics from the sumsfile.
 74:      *
 75:      * @return void
 76:      */
 77:     function loadSums()
 78:     {
 79:         if (file_exists($this->sumsfile)) {
 80:             ob_start();
 81:             readfile($this->sumsfile);
 82:             foreach (explode("\n", ob_get_contents()) as $entry) {
 83:                 if (empty($entry)) {
 84:                     continue;
 85:                 }
 86:                 list($file, $sum) = explode(') = ', $entry);
 87:                 $this->diffs[substr($file, 5)] = $sum;
 88:             }
 89:             ob_end_clean();
 90:         }
 91:     }
 92: 
 93:     /**
 94:      * Save the list of unique identifiers for comics to the sumsfile.
 95:      *
 96:      * @return void
 97:      */
 98:     function saveSums()
 99:     {
100:         $fp = fopen($this->sumsfile, "wb+");
101:         if ($fp === false) {
102:             Horde::logMessage('Unable to create/write to ' . $this->sumsfile,
103:                               __FILE__, __LINE__, PEAR_LOG_ERR);
104:             return false;
105:         }
106:         foreach ($this->diffs as $file => $sum) {
107:             if (empty($file)) { continue; }
108:             fwrite($fp, "MD5 ($file) = $sum\n");
109:         }
110:         fclose($fp);
111:     }
112: 
113:     /**
114:      * Rebuild the table of unique identifiers.
115:      *
116:      * @return void
117:      */
118:     function rebuildSums()
119:     {
120:         $this->diffs = array();
121:         $d = dir($this->basedir);
122:         while (false !== ($entry = $d->read())) {
123:             if (is_dir($d->path . $entry) && strlen($entry) == 8
124:                 && is_numeric($entry)) {
125: 
126:                 // we're reasonably sure this is a valid dir
127:                 $sd = dir($this->basedir . $entry);
128:                 while (false !== ($file = $sd->read())) {
129:                     $file = $sd->path . '/' . $file;
130:                     if (is_file($file)) {
131:                         ob_start();
132:                         readfile($file);
133:                         $this->diffs[$file] = md5(ob_get_contents());
134:                         ob_end_clean();
135:                     }
136:                 }
137:             }
138:         }
139:         $d->close();
140:         $this->saveSums();
141:     }
142: 
143:     /**
144:      * Add a unique identifier for a given image.
145:      *
146:      * @param string $index             The index for the comic
147:      * @param timestamp $date           The date of the comic
148:      * @param string $data              The md5 of the raw (binary) image data
149:      *
150:      * @return void
151:      */
152:     function addSum($index, $date, $data)
153:     {
154:         $key = $this->basedir . date($this->subdir, $date) . '/' . $index;
155:         $this->diffs[$key] = $data;
156:     }
157: 
158:     /**
159:      * Remove the unique identifier for the given comic and/or
160:      * date. If both are passed, removes the uid for that comic and
161:      * date. If only a comic is passed, removes all uids for that
162:      * comic. If only a date is passed removes uids for all comics on
163:      * that date. If neither is passed, all uids are wiped out.
164:      *
165:      * @param string $index    Index for the comic to delete.  If left out all
166:      *                         comics will be assumed.
167:      * @param timestamp $date  Date to remove. If left out, assumes all dates.
168:      *
169:      * @return void
170:      */
171:     function removeSum($index = null, $date = null)
172:     {
173:         if (is_null($date)) {
174:             $date = mktime(0, 0, 0);
175:         }
176: 
177:         if (is_null($index)) {
178:             $cmp = $this->basedir . date($this->subdir, $date);
179:             foreach (array_keys($this->diffs) as $key) {
180:                 if (strncmp($key, $cmp, strlen($cmp)) == 0) {
181:                     unset($this->diffs[$key]);
182:                 }
183:             }
184:         } else {
185:             $key = $this->basedir . date($this->subdir, $date) . '/' . $index;
186:             unset($this->diffs[$key]);
187:         }
188:     }
189: 
190:     /**
191:      * Determine if the image passed is a unique image (one we don't already
192:      * have).
193:      *
194:      * This allows for $days = random, etc., but keeps us from getting the same
195:      * comic day after day.
196:      *
197:      * @param object Klutz_Image $image  Raw (binary) image data
198:      *
199:      * @return boolean  True if unique, false otherwise.
200:      */
201:     function isUnique($image)
202:     {
203:         if (!is_a($image, 'Klutz_Image')) {
204:             return null;
205:         }
206: 
207:         return !in_array(md5($image->data), $this->diffs);
208:     }
209: 
210:     /**
211:      * Get a list of the dates for which we have comics between
212:      * $oldest and $newest. Only returns dates we have at least one
213:      * comic for.
214:      *
215:      * @param timestamp $date    The reference date (default today)
216:      * @param timestamp $oldest  The earliest possible date to return (default
217:      *                           first of the month)
218:      * @param timestamp $newest  The latest possible date to return (default
219:      *                           last date of the month)
220:      *
221:      * @return array timestamps  Any dates between $oldest and $newest that we
222:      *                           have comics for.
223:      */
224:     function listDates($date = null, $oldest = null, $newest = null)
225:     {
226:         if (is_null($date)) {
227:             $date = mktime(0, 0, 0);
228:         }
229: 
230:         // Using Date as a reference, return all dates for the same
231:         // time of day.
232:         $d = getdate($date);
233: 
234:         if (is_null($oldest)) {
235:             $oldest = mktime(0, 0, 0, $d['mon'], 1, $d['year']);
236:         }
237: 
238:         if (is_null($newest)) {
239:             $newest = mktime(0, 0, 0, $d['mon'] + 1, 0, $d['year']);
240:         }
241: 
242:         $d = @dir($this->basedir);
243:         if (!$d) {
244:             return array();
245:         }
246: 
247:         $return = array();
248:         while (false !== ($entry = $d->read())) {
249:             if (is_dir($d->path . $entry) && strlen($entry) == 8
250:                 && is_numeric($entry)) {
251: 
252:                 // we're reasonably sure this is a valid dir
253:                 $time = mktime(0, 0, 0, substr($entry, 4, 2),
254:                                substr($entry, -2), substr($entry, 0, 4));
255:                 if ($time >= $oldest && $time <= $newest) {
256:                     $return[] = $time;
257:                 }
258:             }
259:         }
260:         $d->close();
261:         sort($return, SORT_NUMERIC);
262:         return $return;
263:     }
264: 
265:     /**
266:      * Get the image dimensions for the requested image.
267:      *
268:      * @param string $index    The index of the comic to check
269:      * @param timestamp $date  The date of the comic to check (default today)
270:      *
271:      * @return string  Attributes for an <img> tag giving height and width
272:      */
273:     function imageSize($index, $date = null)
274:     {
275:         $image = $this->retrieveImage($index, $date);
276:         if (is_a($image, 'Klutz_Image') && !empty($image->size)) {
277:             return $image->size;
278:         } else {
279:             return '';
280:         }
281:     }
282: 
283:     /**
284:      * Find out if we already have a local copy of this image.
285:      *
286:      * @param string $index    The index of the comic to check
287:      * @param timestamp $date  The date of the comic to check (default today)
288:      *
289:      * @return boolean  False in this driver
290:      */
291:     function imageExists($index, $date = null)
292:     {
293:         if (is_null($date)) {
294:             $date = mktime(0, 0, 0);
295:         }
296: 
297:         $dir = $this->basedir . date($this->subdir, $date);
298:         return (file_exists($dir . '/' . $index) && is_file($dir . '/' . $index));
299:     }
300: 
301:     /**
302:      * Retrieve an image from storage. Make sure the image exists
303:      * first with imageExists().
304:      *
305:      * @param string $index    The index of the comic to retrieve
306:      * @param timestamp $date  The date for which we want $comic
307:      *
308:      * @return mixed  If the image exists locally, return a Klutz_Image object.
309:      *                If it doesn't, return a string with the URL pointing to
310:      *                the comic.
311:      */
312:     function retrieveImage($index, $date = null)
313:     {
314:         if (is_null($date)) {
315:             $date = mktime(0, 0, 0);
316:         }
317: 
318:         $dir = $this->basedir . date($this->subdir, $date);
319:         return new Klutz_Image($dir . '/' . $index);
320:     }
321: 
322:     /**
323:      * Store an image for later retrieval.
324:      *
325:      * @param string $index    The index of the comic to retrieve
326:      * @param string $image    Raw (binary) image data to store
327:      * @param timestamp $data  Date to store it under (default today)
328:      *
329:      * @return boolean  True on success, false otherwise
330:      */
331:     function storeImage($index, $image, $date = null)
332:     {
333:         if (!is_a($image, 'Klutz_Image')) {
334:             return false;
335:         }
336: 
337:         if (is_null($date)) {
338:             $date = mktime(0, 0, 0);
339:         }
340: 
341:         // Make sure $this->basedir exists and is writeable.
342:         if (!file_exists($this->basedir)) {
343:             if (!file_exists(dirname($this->basedir))) {
344:                 return false;
345:             }
346:             if (!mkdir($this->basedir)) {
347:                 return false;
348:             }
349:         }
350:         if (!is_writable($this->basedir)) {
351:             return false;
352:         }
353: 
354:         $dir = $this->basedir . date($this->subdir, $date);
355: 
356:         if (!file_exists($dir)) {
357:             mkdir($dir);
358:         } elseif (!is_writable($dir)) {
359:             return false;
360:         }
361: 
362:         $fp = fopen($dir . '/' . $index, 'w+');
363:         fwrite($fp, $image->data);
364:         fclose($fp);
365:         $this->addSum($index, $date, md5($image->data));
366:         return true;
367:     }
368: 
369:     /**
370:      * Remove an image from the storage system (including its unique
371:      * ID).
372:      *
373:      * @param string $index    The index of the comic to remove
374:      * @param timestamp $date  The date of the comic to remove (default today)
375:      *
376:      * @return boolean  True on success, else false
377:      */
378:     function removeImage($index, $date = null)
379:     {
380:         if (is_null($date)) {
381:             $date = mktime(0, 0, 0);
382:         }
383: 
384:         if ($this->imageExists($index, $date)) {
385:             $file = $this->basedir . date($this->subdir, $date) . '/' . $index;
386:             if (unlink($file)) {
387:                 $this->removeSum($index, $date);
388:                 return true;
389:             }
390:         }
391:         return false;
392:     }
393: 
394:     /**
395:      * Remove all images from the storage system (including unique
396:      * IDs) for a given date.
397:      *
398:      * @param timestamp $date  The date to remove comics for (default today)
399:      *
400:      * @return boolean  True on success, else false
401:      */
402:     function removeDate($date = null)
403:     {
404:         if (is_null($date)) {
405:             $date = mktime(0, 0, 0);
406:         }
407: 
408:         $dir = $this->basedir . date($this->subdir, $date);
409:         if (file_exists($dir) && is_dir($dir)) {
410:             $d = dir($dir);
411:             if (!$d) {
412:                 return false;
413:             }
414: 
415:             while (false !== ($file = $d->read())) {
416:                 $file = $d->path . '/' . $file;
417:                 if (is_file($file)) {
418:                     unlink($file);
419:                 }
420:             }
421:             $d->close();
422:             if (@rmdir($dir)) {
423:                 $this->removeSum(null, $date);
424:                 return true;
425:             }
426:         }
427: 
428:         return false;
429:     }
430: 
431: }
432: 
API documentation generated by ApiGen