1<?php
2// +----------------------------------------------------------------------+
3// | PEAR :: Cache                                                        |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2003 The PHP Group                                |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 2.0 of the PHP license,       |
8// | that is bundled with this package in the file LICENSE, and is        |
9// | available at through the world-wide-web at                           |
10// | http://www.php.net/license/2_02.txt.                                 |
11// | If you did not receive a copy of the PHP license and are unable to   |
12// | obtain it through the world-wide-web, please send a note to          |
13// | license@php.net so we can mail you a copy immediately.               |
14// +----------------------------------------------------------------------+
15// | Authors: Ulf Wendel <ulf.wendel@phpdoc.de>                           |
16// +----------------------------------------------------------------------+
17//
18// $Id: Graphics.php 178289 2005-01-26 09:47:28Z dufuz $
19
20require_once 'Cache.php';
21
22/**
23* Graphics disk cache.
24*
25* The usual way to create images is to pass some arguments that describe the image
26* to a script that dynamically creates an image. For every image of a page
27* a new PHP interpreter gets started. This is a good way to kill your webserver.
28*
29* When dealing with dynamically generated images you should not call another script
30* to generate the images but generate the images by the script that produces the page
31* that contains the images. This is a major improvement but it's only half the way.
32*
33* There's no need to rerender an image on every request. A simple disk cache can reduce
34* the computation time dramatically. This is what the class graphics_cache is for.
35*
36* Usage:
37*
38* // create an instance of the graphics cache
39* $cache = new graphics_cache;
40*
41* $img = ImageCreate(...);
42*
43* // compute an ID for your image based on typical parameters
44* $id = m5d( $size, $colors, $label);
45*
46* // check if it's cached
47* if (!($link = $cache->getImageLink($id, 'gif'))) {
48*
49*   // hmmm, it's not cached, create it
50*   ...
51*   // cacheImageLink() and cacheImage() make the ImageGIF() call!
52*   // cacheImage() returns the value of ImageGIF() [etc.], cacheImageLink() returns a URL
53*   $link = $cache->cacheImageLink($id, $img, 'gif');
54*
55* }
56*
57* // Ok, let's build the ImageLink
58* $size = getImageSize($link[0]);
59* printf('<img src="%s" %s>', $link[1], $size[3]);
60*
61* // for cacheImage():
62* // header('Content-type: image/gif'); print $cache->cacheImage($id, $img, 'gif');
63*
64*
65* The class requires PHP 4.0.2+ [ImageType()]. Note that cacheImage() works with
66* the output buffer. Modify it if required!
67*
68* @author   Ulf Wendel <ulf.wendel@phpdoc.de>
69* @version  $Id: Graphics.php 178289 2005-01-26 09:47:28Z dufuz $
70* @package  Cache
71*/
72class Cache_Graphics extends Cache
73{
74
75
76    /**
77    * Cache URL prefix.
78    *
79    * Make sure that the cache URL prefix points to the $cache_dir, otherwise
80    * your links will be broken. Use setCacheURL to specify the cache_url and
81    * setCacheDir() for the cache_dir.
82    *
83    * @var  string
84    * @see  setCacheURL(), setCacheDir()
85    */
86    var $cache_url = '';
87
88    /**
89    * Directory where cached files get stored.
90    * s
91    * Make sure that the cache_dir is writable and offers enough space. Check
92    * also if your cache_url points to the directory. Use setCacheDir() to set
93    * the variable.
94    *
95    * @var  string
96    * @see  setCacheDir(), setCacheURL()
97    */
98    var $cache_dir = '';
99
100    /**
101    * Nameprefix of cached files.
102    *
103    * Per default the prefix "graphics_" gets used. You might use this
104    * for versioning or to ease (manual) clean ups.
105    *
106    * @var      string
107    */
108    var $cache_file_prefix = 'graphics_';
109
110
111    /**
112    * Cache container group.
113    *
114    * @var      string
115    */
116    var $cache_group = 'graphics';
117
118
119    /**
120    * Mapping from supported image type to a ImageType() constant.
121    *
122    * Referr to the PHP manual for more informations on ImageType()
123    *
124    * @var  array
125    * @link http://www.php.net/ImageType
126    */
127    var $imagetypes = array(
128                                'gif'   => IMG_GIF,
129                                'jpg'   => IMG_JPG,
130                                'png'   => IMG_PNG,
131                                'wbmp'  => IMG_WBMP
132                            );
133
134
135    /**
136    * Instantiates a cache file container.
137    *
138    */
139    function Cache_Graphics()
140    {
141        $this->Cache('file', array('cache_dir' => $this->cache_dir, 'filename_prefix' => $this->cache_file_prefix));
142
143    } // end constructor
144
145
146    /**
147    * Returns the content of a cached image file.
148    *
149    * This function can be used to send the image directly to the browser.
150    * Make sure that you send a correspondending header before sending the image itself.
151    *
152    * Always try to get the image from the cache before you compute it. See
153    * the class docs for an example.
154    *
155    * @param    string  Image-ID
156    * @param    string  Image type: gif, jpg, png, wbmp
157    * @return   string  Image file contents if a cached file exists otherwise an empty string
158    * @see      cacheImage()
159    */
160    function getImage($id, $format = 'png')
161    {
162        $id = $this->generateID($id, $format);
163        return $this->get($id, $this->cache_group);
164    } // end func getImage
165
166
167    /**
168    * Returns an array with a link to the cached image and the image file path.
169    *
170    * Always try to get the image from the cache before you compute it. See
171    * the class docs for an example.
172    *
173    * @param    string  Image-ID
174    * @param    string  Image type: gif, jpg, png, wbmp
175    * @return   array   [ full path to the image file, image url ]
176    * @throw    Cache_Error
177    * @see      cacheImageLink()
178    */
179    function getImageLink($id, $format = 'png')
180    {
181        $id = $this->generateID($id, $format);
182        if (!$this->container->idExists($id, $this->cache_group)) {
183            return array();
184        }
185        $file = $this->cache_url . $this->cache_file_prefix . $id;
186
187        return array($this->container->getFilename($id, $this->cache_group), $file);
188    } // end func getImageLink
189
190
191    /**
192    * Create an image from the given image handler, cache it and return the file content.
193    *
194    * Always try to retrive the image from the cache before you compute it.
195    *
196    * Warning: this function uses the output buffer. If you expect collisions
197    * modify the code.
198    *
199    * @param    string  Image-ID. Used as a part of the cache filename.
200    *                   Use md5() to generate a "unique" ID for your image
201    *                   based on characteristic values such as the color, size etc.
202    * @param    string  Image handler to create the image from.
203    * @param    string  Image type: gif, jpg, png, wbmp. Also used as filename suffix.
204    *                   If an unsupported type is requested the functions tries to
205    *                   fallback to a supported type before throwing an exeption.
206    * @return   string  Image content returned by ImageGIF/...
207    * @throws   Cache_Error
208    * @access   public
209    * @see      getImage()
210    */
211    function cacheImage($id, $img, $format = 'png')
212    {
213        if (!$id) {
214            return new Cache_Error('You must provide an ID for and image to be cached!', __FILE__, __LINE__);
215        }
216        $id = $this->generateID($id, $format);
217        $types = ImageTypes();
218
219        // Check if the requested image type is supported by the GD lib.
220        // If not, try a callback to the first available image type.
221        if (!isset($this->imagetypes[$format]) || !($types & $this->imagetypes[$format])) {
222            foreach ($this->imagetypes as $supported => $bitmask) {
223                if ($types & $bitmask) {
224                    new Cache_Error("The build in GD lib does not support the image type $format. Fallback to $supported.", __FILE__, __LINE__);
225                } else {
226                    return new Cache_Error("Hmm, is your PHP build with GD support? Can't find any supported types.", __FILE__, __LINE__);
227                }
228            }
229        }
230
231        if ($image = $this->get($id, $this->cache_group)) {
232            return $image;
233        }
234        // save the image to the output buffer, write it to disk and
235        // return the image.
236        ob_end_clean();
237        ob_start();
238
239        if (strtoupper($format) == 'JPG') {
240            $genFormat = 'JPEG';
241        } else {
242            $genFormat = strtoupper($format);
243        }
244
245        // generate the image
246        $func = 'Image' . $genFormat;
247        $func($img);
248        ImageDestroy($img);
249
250        ob_end();
251        $image = ob_get_contents();
252        ob_end_clean();
253
254        // save the generated image to disk
255        $this->save($id, $image, 0, $this->cache_group);
256
257        return $image;
258    } // end func cacheImage
259
260
261    /**
262    * Create an image from the given image handler, cache it and return a url and the file path of the image.
263    *
264    * Always try to retrive the image from the cache before you compute it.
265    *
266    * @param    string  Image-ID. Used as a part of the cache filename.
267    *                   Use md5() to generate a "unique" ID for your image
268    *                   based on characteristic values such as the color, size etc.
269    * @param    string  Image handler to create the image from.
270    * @param    string  Image type: gif, jpg, png, wbmp. Also used as filename suffix.
271    *                   If an unsupported type is requested the functions tries to
272    *                   fallback to a supported type before throwing an exeption.
273    * @return   array  [ full path to the image file, image url ]
274    * @throws   Cache_Error
275    * @access   public
276    */
277    function cacheImageLink($id, &$img, $format = 'png')
278    {
279        if (!$id) {
280            return new Cache_Error ('You must provide an ID for and image to be cached!', __FILE__, __LINE__);
281         }
282        $id = $this->generateID($id, $format);
283        $types = ImageTypes();
284
285        // Check if the requested image type is supported by the GD lib.
286        // If not, try a callback to the first available image type.
287        if (!isset($this->imagetypes[$format]) || !($types & $this->imagetypes[$format])) {
288            foreach ($this->imagetypes as $supported => $bitmask)
289                if ($types & $bitmask) {
290                    new Cache_Error("The build in GD lib does not support the image type $format. Fallback to $supported.", __FILE__, __LINE__);
291                } else {
292                    return new Cache_Error("Hmm, is your PHP build with GD support? Can't find any supported types.", __FILE__, __LINE__);
293                }
294        }
295
296        $url = $this->cache_url . $this->cache_file_prefix . $id;
297        $ffile = $this->container->getFilename($id, $this->cache_group);
298
299        if ($this->isCached($id, $this->cache_group) && !isExpired($id, $this->cache_group)) {
300            return array($ffile, $url);
301        }
302
303        if (strtoupper($format) == 'JPG') {
304            $genFormat = 'JPEG';
305        } else {
306            $genFormat = strtoupper($format);
307        }
308
309        $func = 'Image' . $genFormat;
310        $func($img, $ffile);
311
312        ImageDestroy($img);
313
314        return array($ffile, $url);
315    } // end func cacheImageLink
316
317
318    /**
319    * Sets the URL prefix used when rendering HTML Tags.
320    *
321    * Make sure that the URL matches the cache directory,
322    * otherwise you'll get broken links.
323    *
324    * @param    string
325    * @access   public
326    * @see      setCacheDir()
327    */
328    function setCacheURL($cache_url)
329    {
330        if ($cache_url && '/' != substr($cache_url, 1)) {
331            $cache_url .= '/';
332        }
333        $this->cache_url = $cache_url;
334
335    } // end func setCacheURL
336
337
338    /**
339    * Sets the directory where to cache generated Images
340    *
341    * @param    string
342    * @access   public
343    * @see      setCacheURL()
344    */
345    function setCacheDir($cache_dir)
346    {
347        if ($cache_dir && '/' != substr($cache_dir, 1)) {
348            $cache_dir .= '/';
349        }
350        $this->cache_dir = $cache_dir;
351        $this->container->cache_dir = $cache_dir;
352    } // end func setCacheDir
353
354
355    function generateID($variable, $format = 'png')
356    {
357      return md5(serialize($variable)) . '.' . $format;
358    } // end func generateID
359
360
361} // end class Cache_Graphics
362?>
363