1<?php 2 3// Note that REAL cache should check for Last-Modified HTTP header at least; 4// As I don't like idea of implementing the full-scaled HTTP protocol library 5// and curl extension is very rare, this implementation of cache is very simple. 6// Cache is cleared after the script finishes it work! 7 8// Also, it can have problems with simultaneous access to the images. 9 10// The class responsible for downloading and caching images 11// as PHP does not support the static variables, we'll use a global variable 12// containing all cached objects; note that cache consumes memory! 13// 14$GLOBALS['g_image_cache'] = array(); 15 16class Image { 17 var $_handle; 18 var $_filename; 19 var $_type; 20 21 function Image($handle, $filename, $type) { 22 $this->_handle = $handle; 23 $this->_filename = $filename; 24 $this->_type = $type; 25 } 26 27 function get_filename() { 28 return $this->_filename; 29 } 30 31 function get_handle() { 32 return $this->_handle; 33 } 34 35 function get_type() { 36 return $this->_type; 37 } 38 39 function sx() { 40 if (!$this->_handle) { 41 return 0; 42 }; 43 44 return imagesx($this->_handle); 45 } 46 47 function sy() { 48 if (!$this->_handle) { 49 return 0; 50 }; 51 52 return imagesy($this->_handle); 53 } 54} 55 56class ImageFactory { 57 // Static funcion; checks if given URL is already cached and either returns 58 // cached object or downloads the requested image 59 // 60 function get($url, &$pipeline) { 61 global $g_config; 62 if (!$g_config['renderimages']) { return null; }; 63 64 global $g_image_cache; 65 66 // Check if this URL have been cached 67 // 68 if (isset($g_image_cache[$url])) { 69 // return do_image_open($g_image_cache[$url]); 70 return $g_image_cache[$url]; 71 }; 72 73 // Download image; we should do it before we call do_image_open, 74 // as it tries to open image file twice: first to determine image type 75 // and second to actually create the image - PHP url wrappers do no caching 76 // at all 77 // 78 $filename = ImageFactory::make_cache_filename($url); 79 80 // REQUIRES: PHP 4.3.0+ 81 // we suppress warning messages, as missing image files will cause 'copy' to print 82 // several warnings 83 // 84 // @TODO: change to fetcher class call 85 // 86 87 $data = $pipeline->fetch($url); 88 89 if (is_null($data)) { 90 error_log("Cannot fetch image: ".$url); 91 return null; 92 }; 93 94 $file = fopen($filename, 'wb'); 95 fwrite($file, $data->content); 96 fclose($file); 97 $pipeline->pop_base_url(); 98 99 // register it in the cached objects array 100 // 101 $handle = do_image_open($filename, $type); 102 if ($handle) { 103 $g_image_cache[$url] =& new Image($handle, 104 $filename, 105 $type); 106 } else { 107 $g_image_cache[$url] = null; 108 }; 109 // return image 110 // 111 // return do_image_open($filename); 112 return $g_image_cache[$url]; 113 } 114 115 // Makes the filename to contain the cached version of URL 116 // 117 function make_cache_filename($url) { 118 // We cannot use the $url as an cache image name as it could be longer than 119 // allowed file name length (especially after escaping specialy symbols) 120 // thus, we generate long almost random 32-character name using the md5 hash function 121 // 122 return CACHE_DIR.md5(time() + $url + rand()); 123 } 124 125 // Checks if cache directory is available 126 // 127 function check_cache_dir() { 128 // TODO: some cool easily understandable error message for the case 129 // image cache directory cannot be created or accessed 130 131 // Check if CACHE_DIR exists 132 // 133 if (!is_dir(CACHE_DIR)) { 134 // Cache directory does not exists; try to create it (with read/write rightss for the owner only) 135 // 136 if (!mkdir(CACHE_DIR, 0700)) { die("Cache directory cannot be created"); } 137 }; 138 139 // Check if we can read and write to the CACHE_DIR 140 // 141 // Note that directory should have 'rwx' (7) permission, so the script will 142 // be able to list directory contents; under Windows is_executable always returns false 143 // for directories, so we need to drop this check in this case. 144 // 145 // A user's note for 'is_executable' function on PHP5: 146 // "The change doesn't appear to be documented, so I thought I would mention it. 147 // In php5, as opposed to php4, you can no longer rely on is_executable to check the executable bit 148 // on a directory in 'nix. You can still use the first note's method to check if a directory is traversable: 149 // @file_exists("adirectory/.");" 150 // 151 if (!is_readable(CACHE_DIR) || 152 !is_writeable(CACHE_DIR) || 153 (!@file_exists(CACHE_DIR.'.'))) { 154 // omg. Cache directory exists, but useless 155 // 156 die("Check cache directory permissions; cannot either read or write to directory cache"); 157 }; 158 159 return; 160 } 161 162 // Clears the image cache (as we're neither implemented checking of Last-Modified HTTP header nor 163 // provided the means of limiting the cache size 164 // 165 // TODO: Will cause problems with simultaneous access to the same images 166 // 167 function clear_cache() { 168 foreach ($GLOBALS['g_image_cache'] as $key => $value) { 169 if (!is_null($value)) { 170 unlink($value->get_filename()); 171 }; 172 }; 173 $g_image_cache = array(); 174 } 175} 176?>