用過timthumb這個類的都應該很熟悉,此類能夠用來生成圖片的縮略圖並加以處理,若是在linux環境下安裝了optipng或pngcrush工具,也能夠進行網站的截圖操做。功能很是的強大,因此趁着假期花了兩天時間把源碼從頭至尾很詳細的註釋了一遍。簡單說一下此類的使用方法:javascript
訪問地址爲:http://localhost/timthumb.php?src=http://localhost/200.jpg&w=200&h=300&q=100&f=3,9|4,2&s=1&ct=1php
參數都是get提交的,可選參數和說明以下:html
src : 須要進行圖片縮放的源圖片地址,或者是須要進行截圖操做的網頁地址java
webshot : 若是此值爲真則進行截圖操做node
w : 生成圖片的寬度,若是寬度或高度只設置了一個值,則根據其中一個值進行等比縮放linux
h : 生成圖片的高度,若是高度和寬度都沒有指定,則默認爲100*100web
zc : 生成圖片的縮放模式,可選值0, 1, 2, 3, 默認爲1,每一個值的不一樣之處可看下面文件的第100行註釋算法
q : 生成圖片的質量,默認90shell
a : 超出部分的裁剪位置,和縮放模式有關,可選值t, b, l, r, 默認爲從頂部裁剪canvas
f : 須要對生成後的圖片使用一些過濾器的話,則在這裏傳不一樣過濾器的代碼和值,具體操做方法可見下面文件的第821行註解
s : 是否對生產的圖片進行銳化處理
cc : 生成圖片的背景畫布顏色
ct : 生成png圖片時背景是否透明
關於截圖操做就很少說了,文件裏作了很詳細的註釋,下面就是註釋完的文件
文件下載:http://vdisk.weibo.com/s/eZvcI
<?php //定義版本信息 define ('VERSION', '2.8.10'); //若是有配置文件,則加載timthumb-config.php,沒有的話使用下面的值 if( file_exists(dirname(__FILE__) . '/timthumb-config.php')){ require_once('timthumb-config.php'); } //調試日誌記錄到web服務器日誌中 if(! defined('DEBUG_ON') ){ define ('DEBUG_ON', false); } //調試級別,高於這個值的level都不會記錄,1最低,3最高 if(! defined('DEBUG_LEVEL') ){ define ('DEBUG_LEVEL', 1); } //最大佔用內存限制30M if(! defined('MEMORY_LIMIT') ){ define ('MEMORY_LIMIT', '30M'); } //關閉仿盜鏈 if(! defined('BLOCK_EXTERNAL_LEECHERS') ){ define ('BLOCK_EXTERNAL_LEECHERS', false); } // 容許從外部獲取圖片 if(! defined('ALLOW_EXTERNAL') ){ define ('ALLOW_EXTERNAL', TRUE); } //容許獲取全部外部站點url if(! defined('ALLOW_ALL_EXTERNAL_SITES') ){ define ('ALLOW_ALL_EXTERNAL_SITES', false); } //啓用文件緩存 if(! defined('FILE_CACHE_ENABLED') ){ define ('FILE_CACHE_ENABLED', TRUE); } //文件緩存更新時間,s if(! defined('FILE_CACHE_TIME_BETWEEN_CLEANS')){ define ('FILE_CACHE_TIME_BETWEEN_CLEANS', 86400); } //文件緩存生存時間,s,過了這個時間的緩存文件就會被刪除 if(! defined('FILE_CACHE_MAX_FILE_AGE') ){ define ('FILE_CACHE_MAX_FILE_AGE', 86400); } //緩存文件後綴 if(! defined('FILE_CACHE_SUFFIX') ){ define ('FILE_CACHE_SUFFIX', '.timthumb.txt'); } //緩存文件前綴 if(! defined('FILE_CACHE_PREFIX') ){ define ('FILE_CACHE_PREFIX', 'timthumb'); } //緩存文件目錄,留空則使用系統臨時目錄(推薦) if(! defined('FILE_CACHE_DIRECTORY') ){ define ('FILE_CACHE_DIRECTORY', './cache'); } //圖片最大尺寸,此腳本最大能處理10485760字節的圖片,也就是10M if(! defined('MAX_FILE_SIZE') ){ define ('MAX_FILE_SIZE', 10485760); } //CURL的超時時間 if(! defined('CURL_TIMEOUT') ){ define ('CURL_TIMEOUT', 20); } //清理錯誤緩存的時間 if(! defined('WAIT_BETWEEN_FETCH_ERRORS') ){ define ('WAIT_BETWEEN_FETCH_ERRORS', 3600); } //瀏覽器緩存時間 if(! defined('BROWSER_CACHE_MAX_AGE') ){ define ('BROWSER_CACHE_MAX_AGE', 864000); } //關閉全部瀏覽器緩存 if(! defined('BROWSER_CACHE_DISABLE') ){ define ('BROWSER_CACHE_DISABLE', false); } //最大圖像寬度 if(! defined('MAX_WIDTH') ){ define ('MAX_WIDTH', 1500); } //最大圖像高度 if(! defined('MAX_HEIGHT') ){ define ('MAX_HEIGHT', 1500); } //404錯誤時顯示的提示圖片地址,設置測值則不會顯示具體的錯誤信息 if(! defined('NOT_FOUND_IMAGE') ){ define ('NOT_FOUND_IMAGE', ''); } //其餘錯誤時顯示的提示圖片地址,設置測值則不會顯示具體的錯誤信息 if(! defined('ERROR_IMAGE') ){ define ('ERROR_IMAGE', ''); } //PNG圖片背景顏色,使用false表明透明 if(! defined('PNG_IS_TRANSPARENT') ){ define ('PNG_IS_TRANSPARENT', FALSE); } //默認圖片質量 if(! defined('DEFAULT_Q') ){ define ('DEFAULT_Q', 90); } //默認 縮放/裁剪 模式,0:根據傳入的值進行縮放(不裁剪), 1:以最合適的比例裁剪和調整大小(裁剪), 2:按比例調整大小,並添加邊框(裁剪),2:按比例調整大小,不添加邊框(裁剪) if(! defined('DEFAULT_ZC') ){ define ('DEFAULT_ZC', 1); } //默認須要對圖片進行的處理操做,可選值和參數格式請參看processImageAndWriteToCache函數中的$filters和$imageFilters的註釋 if(! defined('DEFAULT_F') ){ define ('DEFAULT_F', ''); } //是否對圖片進行銳化 if(! defined('DEFAULT_S') ){ define ('DEFAULT_S', 0); } //默認畫布顏色 if(! defined('DEFAULT_CC') ){ define ('DEFAULT_CC', 'ffffff'); } //如下是圖片壓縮設置,前提是你的主機中有optipng或者pngcrush這兩個工具,不然的話不會啓用此功能 //此功能只對png圖片有效 if(! defined('OPTIPNG_ENABLED') ){ define ('OPTIPNG_ENABLED', false); } if(! defined('OPTIPNG_PATH') ){ define ('OPTIPNG_PATH', '/usr/bin/optipng'); } //優先使用optipng,由於有更好的壓縮比 if(! defined('PNGCRUSH_ENABLED') ){ define ('PNGCRUSH_ENABLED', false); } if(! defined('PNGCRUSH_PATH') ){ define ('PNGCRUSH_PATH', '/usr/bin/pngcrush'); } //optipng不存在的話,使用pngcrush //圖片壓縮設置結束 /* * * 如下是網站截圖配置 * 首先,網站截圖須要root權限 * Ubuntu 上使用網站截圖的步驟: * 1.用這個命令安裝Xvfb sudo apt-get install subversion libqt4-webkit libqt4-dev g++ xvfb * 2.新建一個文件夾,並下載下面的源碼 * 3.用這個命令下載最新的CutyCapt svn co https://cutycapt.svn.sourceforge.net/svnroot/cutycapt * 4.進入CutyCapt文件夾 * 5.編譯並安裝CutyCapt * 6.嘗試運行如下命令: xvfb-run --server-args="-screen 0, 1024x768x24" CutyCapt --url="http://markmaunder.com/" --out=test.png * 7.若是生成了一個 test.php的圖片,證實一切正常,如今經過瀏覽器試試,訪問下面的地址:http://yoursite.com/path/to/timthumb.php?src=http://markmaunder.com/&webshot=1 * * 須要注意的地方: * 1.第一次webshot加載時,須要數秒鐘,以後加載就很快了 * * 高級用戶: * 1.若是想提速大約25%,而且你瞭解linux,能夠運行如下命令: * nohup Xvfb :100 -ac -nolisten tcp -screen 0, 1024x768x24 > /dev/null 2>&1 & * 並設置 WEBSHOT_XVFB_RUNNING 的值爲true * * */ //測試的功能,若是設置此值爲true, 並在查詢字符串中加上webshot=1,會讓腳本返回瀏覽器的截圖,而不是獲取圖像 if(! defined('WEBSHOT_ENABLED') ){ define ('WEBSHOT_ENABLED', false); } //定義CutyCapt地址 if(! defined('WEBSHOT_CUTYCAPT') ){ define ('WEBSHOT_CUTYCAPT', '/usr/local/bin/CutyCapt'); } //Xvfb地址 if(! defined('WEBSHOT_XVFB') ){ define ('WEBSHOT_XVFB', '/usr/bin/xvfb-run'); } //截圖屏幕寬度1024 if(! defined('WEBSHOT_SCREEN_X') ){ define ('WEBSHOT_SCREEN_X', '1024'); } //截圖屏幕高度768 if(! defined('WEBSHOT_SCREEN_Y') ){ define ('WEBSHOT_SCREEN_Y', '768'); } //色深,做者說他只測試過24 if(! defined('WEBSHOT_COLOR_DEPTH') ){ define ('WEBSHOT_COLOR_DEPTH', '24'); } //截圖格式 if(! defined('WEBSHOT_IMAGE_FORMAT') ){ define ('WEBSHOT_IMAGE_FORMAT', 'png'); } //截圖超時時間,單位s if(! defined('WEBSHOT_TIMEOUT') ){ define ('WEBSHOT_TIMEOUT', '20'); } //user_agent頭 if(! defined('WEBSHOT_USER_AGENT') ){ define ('WEBSHOT_USER_AGENT', "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18"); } //是否啓用JS if(! defined('WEBSHOT_JAVASCRIPT_ON') ){ define ('WEBSHOT_JAVASCRIPT_ON', true); } //是否啓用java if(! defined('WEBSHOT_JAVA_ON') ){ define ('WEBSHOT_JAVA_ON', false); } //開啓flash和其餘插件 if(! defined('WEBSHOT_PLUGINS_ON') ){ define ('WEBSHOT_PLUGINS_ON', true); } //代理服務器 if(! defined('WEBSHOT_PROXY') ){ define ('WEBSHOT_PROXY', ''); } //若是運行了XVFB,此項設爲true if(! defined('WEBSHOT_XVFB_RUNNING') ){ define ('WEBSHOT_XVFB_RUNNING', false); } // 若是 ALLOW_EXTERNAL 的值爲真 而且 ALLOW_ALL_EXTERNAL_SITES 的值爲假,那麼截圖的圖片只能從下面這些數組中的域和子域進行 if(! isset($ALLOWED_SITES)){ $ALLOWED_SITES = array ( 'flickr.com', 'staticflickr.com', 'picasa.com', 'img.youtube.com', 'upload.wikimedia.org', 'photobucket.com', 'imgur.com', 'imageshack.us', 'tinypic.com', ); } /*截圖配置結束*/ // ------------------------------------------------------------- // -------------------------- 配置結束 ------------------------ // ------------------------------------------------------------- timthumb::start(); class timthumb { protected $src = ""; //須要獲取的圖片url protected $is404 = false; //404錯誤碼 protected $docRoot = ""; //服務器文檔根目錄 protected $lastURLError = false; //上一次請求外部url的錯誤信息 protected $localImage = ""; //若是請求的url是本地圖片,則爲本地圖片的地址 protected $localImageMTime = 0; //本地圖片的修改時間 protected $url = false; //用parse_url解析src後的數組 protected $myHost = ""; //本機域名 protected $isURL = false; //是否爲外部圖片地址 protected $cachefile = ''; //緩存文件地址 protected $errors = array(); //錯誤信息列表 protected $toDeletes = array(); //析構函數中須要刪除的資源列表 protected $cacheDirectory = ''; //緩存地址 protected $startTime = 0; //開始時間 protected $lastBenchTime = 0; //上一次debug完成的時間 protected $cropTop = false; //是否啓用裁剪 protected $salt = ""; //文件修改時間和inode鏈接的字符串的鹽值 protected $fileCacheVersion = 1; //文件緩存版本,當這個類升級或者被更改時,這個值應該改變,從而從新生成緩存 protected $filePrependSecurityBlock = "<?php die('Execution denied!'); //"; //緩存文件安全頭,防止直接訪問 protected static $curlDataWritten = 0; //將curl獲取到的數據寫入緩存文件的長度 protected static $curlFH = false; //curl請求成功後要將獲取到的數據寫到此文件內 /*外部調用接口*/ public static function start(){ //實例化模型 $tim = new timthumb(); //檢查實例化模型時是否有錯誤 $tim->handleErrors(); //此函數爲空,用作自定義的數據驗證 $tim->securityChecks(); //嘗試讀取瀏覽器緩存 if($tim->tryBrowserCache()){ //成功的話就輸出緩存 exit(0); } //檢測錯誤 $tim->handleErrors(); //若是啓用了文件緩存,而且讀取服務端緩存 if(FILE_CACHE_ENABLED && $tim->tryServerCache()){ //成功的話輸出緩存 exit(0); } //檢測讀取服務端緩存時的錯誤 $tim->handleErrors(); //生成和處理圖片主函數 $tim->run(); //檢測圖片生成和處理時的錯誤 $tim->handleErrors(); //程序執行完畢運行析構方法並退出 exit(0); } /*構造方法,用來獲取和設置一些基本屬性*/ public function __construct(){ //將容許的域設爲全局變量 global $ALLOWED_SITES; //記錄開始時間 $this->startTime = microtime(true); //設置時區 date_default_timezone_set('UTC'); //寫日誌,記錄請求IP和請求URL $this->debug(1, "Starting new request from " . $this->getIP() . " to " . $_SERVER['REQUEST_URI']); //獲取服務器文檔根目錄 $this->calcDocRoot(); //獲取文件的修改時間和inode,inode只在linux系統下有效 $this->salt = @filemtime(__FILE__) . '-' . @fileinode(__FILE__); //記錄salt信息,級別3 $this->debug(3, "Salt is: " . $this->salt); //若是定義了緩存文件地址 if(FILE_CACHE_DIRECTORY){ //若是這個地址不存在,或爲非目錄 if(! is_dir(FILE_CACHE_DIRECTORY)){ //創建這個目錄 @mkdir(FILE_CACHE_DIRECTORY); //若是創建失敗 if(! is_dir(FILE_CACHE_DIRECTORY)){ //記錄錯誤信息,中止執行 $this->error("Could not create the file cache directory."); return false; } } //將緩存地址寫入成員屬性 $this->cacheDirectory = FILE_CACHE_DIRECTORY; //在緩存目錄下建立一個index.html文件,防止列目錄 if (!touch($this->cacheDirectory . '/index.html')) { //若是出錯,記錄錯誤信息 $this->error("Could not create the index.html file - to fix this create an empty file named index.html file in the cache directory."); } //若是沒定義緩存文件地址,則用系統的臨時文件目錄作爲緩存文件目錄 } else { $this->cacheDirectory = sys_get_temp_dir(); } //進行緩存檢查,清除過時緩存 $this->cleanCache(); //記錄本機域名 $this->myHost = preg_replace('/^www\./i', '', $_SERVER['HTTP_HOST']); //獲取圖片地址,此地址應該由$_GET中的src參數傳遞 $this->src = $this->param('src'); //此數組是解析src後的結果,包括scheme,host,port,user,pass,path,query,fragment其中一個或多個值 $this->url = parse_url($this->src); //若是圖片地址是本機的,則刪除圖片url中本機的域名部分 $this->src = preg_replace('/https?:\/\/(?:www\.)?' . $this->myHost . '/i', '', $this->src); //若是圖片地址的長度小於3,則是無效地址 if(strlen($this->src) <= 3){ //添加錯誤信息 $this->error("No image specified"); return false; } //若是開啓了防盜鏈,而且存在來源地址,也就是HTTP_REFERER頭,而且來源地址不是本機,則進行防盜鏈處理 if(BLOCK_EXTERNAL_LEECHERS && array_key_exists('HTTP_REFERER', $_SERVER) && (! preg_match('/^https?:\/\/(?:www\.)?' . $this->myHost . '(?:$|\/)/i', $_SERVER['HTTP_REFERER']))){ //此base64編碼的內容是一張顯示 No Hotlinking的圖片 $imgData = base64_decode("R0lGODlhUAAMAIAAAP8AAP///yH5BAAHAP8ALAAAAABQAAwAAAJpjI+py+0Po5y0OgAMjjv01YUZ\nOGplhWXfNa6JCLnWkXplrcBmW+spbwvaVr/cDyg7IoFC2KbYVC2NQ5MQ4ZNao9Ynzjl9ScNYpneb\nDULB3RP6JuPuaGfuuV4fumf8PuvqFyhYtjdoeFgAADs="); //顯示內容爲gif圖片 header('Content-Type: image/gif'); //內容長度 header('Content-Length: ' . sizeof($imgData)); //無網頁緩存 header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); //兼容http1.0和https header("Pragma: no-cache"); //內容當即過時 header('Expires: ' . gmdate ('D, d M Y H:i:s', time())); //輸出圖片 echo $imgData; return false; //退出腳本 exit(0); } //若是此時的src屬性包含http等字符,說明是外部連接 if(preg_match('/^https?:\/\/[^\/]+/i', $this->src)){ //寫日誌,說明這個連接是外部的,級別2 $this->debug(2, "Is a request for an external URL: " . $this->src); //將isURL設爲true,說明是外部url $this->isURL = true; //若是不包含的話,說明是內部連接 } else { $this->debug(2, "Is a request for an internal file: " . $this->src); } //若是圖片的src是外部網站,而且配置文件不容許從外部獲取圖片,則退出 if($this->isURL && (! ALLOW_EXTERNAL)){ $this->error("You are not allowed to fetch images from an external website."); return false; } //若是容許從外部網站獲取圖片 if($this->isURL){ //而且配置文件容許從全部的外部網站獲取圖片 if(ALLOW_ALL_EXTERNAL_SITES){ //寫日誌,容許從外部網站取回圖片,級別2 $this->debug(2, "Fetching from all external sites is enabled."); //若是配置文件不容許從全部的外部網站獲取圖片 } else { //寫日誌,只能從指定的外部網站獲取圖片,級別2 $this->debug(2, "Fetching only from selected external sites is enabled."); //此爲驗證位,默認爲false $allowed = false; //遍歷配置文件中容許站點的列表 foreach($ALLOWED_SITES as $site){ //這裏對圖片的url跟容許訪問站點的列表進行驗證,前面的條件對應的是有主機名的,後面的內容對應的是沒有主機名的,寫的很精巧 if ((strtolower(substr($this->url['host'],-strlen($site)-1)) === strtolower(".$site")) || (strtolower($this->url['host'])===strtolower($site))) { //經過驗證則寫一條日誌,驗證成功,級別3 $this->debug(3, "URL hostname {$this->url['host']} matches $site so allowing."); //驗證位爲true $allowed = true; } } //若是沒經過驗證, 寫錯誤信息並退出 if(! $allowed){ return $this->error("You may not fetch images from that site. To enable this site in timthumb, you can either add it to \$ALLOWED_SITES and set ALLOW_EXTERNAL=true. Or you can set ALLOW_ALL_EXTERNAL_SITES=true, depending on your security needs."); } } } //緩存文件的前綴,若是是內部圖片,用_int_,外部圖片用_ext_ $cachePrefix = ($this->isURL ? '_ext_' : '_int_'); //若是是外部圖片地址 if($this->isURL){ //獲得GET字符串的數組 $arr = explode('&', $_SERVER ['QUERY_STRING']); //按字母順序對數組元素排序 asort($arr); //生成緩存文件地址 緩存目錄 + / + 緩存前綴 + $cachePrefix + 惟一散列值 + 緩存後綴 $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . implode('', $arr) . $this->fileCacheVersion) . FILE_CACHE_SUFFIX; //若是是本地圖片 } else { //獲取本地圖片地址 $this->localImage = $this->getLocalImagePath($this->src); //若是獲取不到地址 if(! $this->localImage){ //寫日誌,沒有找到此文件,級別1 $this->debug(1, "Could not find the local image: {$this->localImage}"); //記錄錯誤信息 $this->error("Could not find the internal image you specified."); //設置404錯誤信息 $this->set404(); //終止執行程序 return false; } //寫日誌,記錄本地圖片信息,級別1 $this->debug(1, "Local image path is {$this->localImage}"); //獲取文件修改時間 $this->localImageMTime = @filemtime($this->localImage); //生成緩存文件地址, 緩存目錄 + / + 緩存前綴 + $cachePrefix + 惟一散列值 + 緩存後綴 $this->cachefile = $this->cacheDirectory . '/' . FILE_CACHE_PREFIX . $cachePrefix . md5($this->salt . $this->localImageMTime . $_SERVER ['QUERY_STRING'] . $this->fileCacheVersion) . FILE_CACHE_SUFFIX; } //記錄緩存文件地址 $this->debug(2, "Cache file is: " . $this->cachefile); //構造函數完成 return true; } /*析構方法,刪除toDeletes數組中的每個文件*/ public function __destruct(){ foreach($this->toDeletes as $del){ $this->debug(2, "Deleting temp file $del"); @unlink($del); } } /*主函數,經過不一樣參數調用不一樣的圖片處理函數*/ public function run(){ //若是是外部的圖片連接 if($this->isURL){ //可是配置文件不容許從外部獲取連接 if(! ALLOW_EXTERNAL){ //寫日誌,說明配置文件禁止訪問外部圖片,級別1 $this->debug(1, "Got a request for an external image but ALLOW_EXTERNAL is disabled so returning error msg."); //寫錯誤記錄 $this->error("You are not allowed to fetch images from an external website."); //退出執行 return false; } //配置文件容許從外部獲取連接,則寫日誌,接着運行,級別3 $this->debug(3, "Got request for external image. Starting serveExternalImage."); //若是get了webshot參數而且爲真,則進行截圖操做 if($this->param('webshot')){ //若是配置文件容許截圖 if(WEBSHOT_ENABLED){ //寫日誌,說明要進行截圖操做,級別3 $this->debug(3, "webshot param is set, so we're going to take a webshot."); //截圖操做 $this->serveWebshot(); //若是配置文件不容許截圖 } else { //記錄錯誤信息並退出 $this->error("You added the webshot parameter but webshots are disabled on this server. You need to set WEBSHOT_ENABLED == true to enable webshots."); } //若是不存在sebshot參數或爲假 } else { //寫日誌,記錄不進行截圖操做,級別3 $this->debug(3, "webshot is NOT set so we're going to try to fetch a regular image."); //從外部URL得到圖片並處理 $this->serveExternalImage(); } //不然的話就是內部圖片 } else { //寫日誌,記錄是內部圖片,級別3 $this->debug(3, "Got request for internal image. Starting serveInternalImage()"); //得到內部圖片並處理 $this->serveInternalImage(); } //程序執行完畢 return true; } /*此函數用來處理錯誤*/ protected function handleErrors(){ //若是錯誤列表中有內容 if($this->haveErrors()){ //首先檢測404錯誤,若是設置了404圖片地址而且的確有404錯誤 if(NOT_FOUND_IMAGE && $this->is404()){ //那麼輸出錯誤圖片,並退出腳本 if($this->serveImg(NOT_FOUND_IMAGE)){ exit(0); //輸出失敗的話記錄錯誤信息 } else { $this->error("Additionally, the 404 image that is configured could not be found or there was an error serving it."); } } //若是沒有404錯誤,而且定義了錯誤圖片,那麼輸出此圖片 if(ERROR_IMAGE){ //輸出其餘錯誤提示圖片,並退出腳本 if($this->serveImg(ERROR_IMAGE)){ exit(0); //輸出失敗的話記錄錯誤信息 } else { $this->error("Additionally, the error image that is configured could not be found or there was an error serving it."); } } //若是上面兩個常量都沒定義,則根據模板輸出詳細錯誤信息 $this->serveErrors(); exit(0); } //沒有錯誤的話返回假 return false; } /*此函數用來讀取瀏覽器緩存文件,前提是瀏覽器緩存的文件有效,具體的實現請看函數內部*/ protected function tryBrowserCache(){ //若是配置文件關閉了全部瀏覽器緩存,則寫日誌,並返回假 if(BROWSER_CACHE_DISABLE){ $this->debug(3, "Browser caching is disabled"); return false; } //若是瀏覽器記錄了頁面上次修改的時間 if(!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ){ //寫日誌,記錄,級別3 $this->debug(3, "Got a conditional get"); //緩存文件最後修改時間 $mtime = false; //若是緩存地址無效 if(! is_file($this->cachefile)){ //說明沒有緩存,返回假 return false; } //若是存在本地圖片修改時間,也就是說所請求的圖片是本機的 if($this->localImageMTime){ //緩存文件修改時間設置爲本地圖片修改時間 $mtime = $this->localImageMTime; //寫日誌,記錄實際文件修改時間,級別3 $this->debug(3, "Local real file's modification time is $mtime"); //若是請求的圖片不是本地的 } else if(is_file($this->cachefile)){ //獲取緩存文件修改時間 $mtime = @filemtime($this->cachefile); //寫日誌,記錄緩存文件修改時間,級別3 $this->debug(3, "Cached file's modification time is $mtime"); } //若是沒有獲取到緩存文件最後修改時間,說明沒有緩存,退出 if(! $mtime){ return false; } //將瀏覽器中存儲的上次修改時間轉爲時間戳 $iftime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); //寫日誌,記錄UNIX時間戳,級別3 $this->debug(3, "The conditional get's if-modified-since unixtime is $iftime"); //若是這個時間小於1秒,說明值無效,退出 if($iftime < 1){ //寫日誌,記錄此值無效 $this->debug(3, "Got an invalid conditional get modified since time. Returning false."); return false; } //若是瀏覽器存儲的時間小於實際緩存文件的最後修改時間,也就是說距上次訪問後,文件被更改了,要從新請求頁面 if($iftime < $mtime){ //寫日誌,記錄文件已被更改,級別3 $this->debug(3, "File has been modified since last fetch."); return false; //不然就不用從新請求頁面,直接讀取緩存 } else { //寫日誌,記錄緩存有效,直接讀取緩存,級別3 $this->debug(3, "File has not been modified since last get, so serving a 304."); //設置HTTP頭響應碼爲304 header ($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified'); //記錄這次操做,級別1 $this->debug(1, "Returning 304 not modified"); //讀取成功返回真 return true; } } //沒有讀取到緩存,返回假 return false; } /*此函數用來運行緩存文件的GC和讀取服務器上的緩存文件*/ protected function tryServerCache(){ //寫日誌,記錄將讀取服務端緩存,級別3 $this->debug(3, "Trying server cache"); //若是緩存文件存在 if(file_exists($this->cachefile)){ //寫日誌,記錄緩存文件存在 $this->debug(3, "Cachefile {$this->cachefile} exists"); //若是請求的是外部圖片地址 if($this->isURL){ //寫日誌,記錄這是一次外部請求,級別3 $this->debug(3, "This is an external request, so checking if the cachefile is empty which means the request failed previously."); //若是緩存文件的大小小於1,也就是說是一個無效的緩存文件 if(filesize($this->cachefile) < 1){ //寫日誌,記錄這是一個空的緩存文件,級別3 $this->debug(3, "Found an empty cachefile indicating a failed earlier request. Checking how old it is."); //若是已到了配置文件中清理無效緩存的時間 if(time() - @filemtime($this->cachefile) > WAIT_BETWEEN_FETCH_ERRORS){ //寫日誌,記錄此次刪除操做,級別3 $this->debug(3, "File is older than " . WAIT_BETWEEN_FETCH_ERRORS . " seconds. Deleting and returning false so app can try and load file."); //刪除此緩存文件 @unlink($this->cachefile); //返回假,說明沒有讀取到服務端緩存 return false; //不然,空的緩存文件說明上次請求失敗,因此要寫錯誤記錄 } else { //寫日誌,記錄空的緩存文件依然有效,級別3 $this->debug(3, "Empty cachefile is still fresh so returning message saying we had an error fetching this image from remote host."); //設置404錯誤 $this->set404(); //設置錯誤信息 $this->error("An error occured fetching image."); //返回假表明沒有獲得緩存 return false; } } //不然就是正確的緩存文件 } else { //寫日誌,記錄將要直接讀取緩存文件,級別3 $this->debug(3, "Trying to serve cachefile {$this->cachefile}"); } //若是輸出圖像緩存成功 if($this->serveCacheFile()){ //寫日誌,記錄緩存文件信息,級別3 $this->debug(3, "Succesfully served cachefile {$this->cachefile}"); return true; //若是不成功 } else { //寫日誌,記錄錯誤信息,級別3 $this->debug(3, "Failed to serve cachefile {$this->cachefile} - Deleting it from cache."); //刪除此無效緩存,以便下次請求能從新建立 @unlink($this->cachefile); //一樣返回真,由於在serverCacheFile已經記錄了錯誤信息 return true; } } } /*此函數用來記錄錯誤信息*/ protected function error($err){ //寫記錄,記錄錯誤信息,級別3 $this->debug(3, "Adding error message: $err"); //記錄到錯誤信息數組 $this->errors[] = $err; return false; } /*測函數用來檢測存儲錯誤信息的數組中是否有內容,也就是說在上一個操做中,是否有錯誤*/ protected function haveErrors(){ if(sizeof($this->errors) > 0){ return true; } return false; } /*此函數輸出已存儲的錯誤信息*/ protected function serveErrors(){ //設置http頭 header ($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); //循環輸出錯誤列表信息 $html = '<ul>'; foreach($this->errors as $err){ $html .= '<li>' . htmlentities($err) . '</li>'; } $html .= '</ul>'; //輸出其餘錯誤信息 echo '<h1>A TimThumb error has occured</h1>The following error(s) occured:<br />' . $html . '<br />'; echo '<br />Query String : ' . htmlentities ($_SERVER['QUERY_STRING']); echo '<br />TimThumb version : ' . VERSION . '</pre>'; } /*此函數用來讀取本地圖片*/ protected function serveInternalImage(){ //寫日誌,記錄本地圖片地址 $this->debug(3, "Local image path is $this->localImage"); //若是地址無效 if(! $this->localImage){ //記錄此錯誤,並退出執行 $this->sanityFail("localImage not set after verifying it earlier in the code."); return false; } //獲取本地圖片大小 $fileSize = filesize($this->localImage); //若是本地圖片的尺寸超過配置文件的相關設置 if($fileSize > MAX_FILE_SIZE){ //記錄錯誤緣由,並退出 $this->error("The file you specified is greater than the maximum allowed file size."); return false; } //若是獲取到的圖片尺寸無效 if($fileSize <= 0){ //記錄錯誤並退出 $this->error("The file you specified is <= 0 bytes."); return false; } //若是經過了以上驗證,則寫日誌,記錄將用processImageAndWriteToCache函數處理本地圖片 $this->debug(3, "Calling processImageAndWriteToCache() for local image."); //處理成功則從緩存返回圖片 if($this->processImageAndWriteToCache($this->localImage)){ $this->serveCacheFile(); return true; //失敗則返回假 } else { return false; } } /*此函數用來清理緩存*/ protected function cleanCache(){ //若是定義的緩存時間小於0,則退出 if (FILE_CACHE_TIME_BETWEEN_CLEANS < 0) { return; } //寫日誌,記錄清除緩存操做,級別3 $this->debug(3, "cleanCache() called"); //此文件爲記錄上次進行清除緩存操做的時間戳文件 $lastCleanFile = $this->cacheDirectory . '/timthumb_cacheLastCleanTime.touch'; //若是上面定義的文件不存在,說明這是第一次進行清除緩存操做,建立此文件並返回空便可 if(! is_file($lastCleanFile)){ //寫日誌,記錄建立文件,級別1 $this->debug(1, "File tracking last clean doesn't exist. Creating $lastCleanFile"); //建立此文件 if (!touch($lastCleanFile)) { //失敗的話報錯並退出 $this->error("Could not create cache clean timestamp file."); } return; } //若是已超過緩存時間 if(@filemtime($lastCleanFile) < (time() - FILE_CACHE_TIME_BETWEEN_CLEANS) ){ //寫日誌,記錄下面進行的清除緩存操做 $this->debug(1, "Cache was last cleaned more than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago. Cleaning now."); //建立新的清除緩存時間戳文件 if (!touch($lastCleanFile)) { //失敗的話記錄錯誤信息 $this->error("Could not create cache clean timestamp file."); } //此數組存的是全部緩存文件,根據前面定義的緩存文件目錄和緩存文件後綴獲得的 $files = glob($this->cacheDirectory . '/*' . FILE_CACHE_SUFFIX); //若是有緩存文件 if ($files) { //計算當前時間和緩存最大生存時間的差值,用於下面判斷緩存文件是否刪除 $timeAgo = time() - FILE_CACHE_MAX_FILE_AGE; //遍歷緩存文件數組 foreach($files as $file){ //若是文件建立時間小於上面計算的值,也就是說此緩存文件的死期到了,就刪除它 if(@filemtime($file) < $timeAgo){ //記錄刪除緩存文件,級別3 $this->debug(3, "Deleting cache file $file older than max age: " . FILE_CACHE_MAX_FILE_AGE . " seconds"); @unlink($file); } } } return true; //若是沒超過緩存時間,則不用清除 } else { //寫日誌,記錄不用清除緩存 $this->debug(3, "Cache was cleaned less than " . FILE_CACHE_TIME_BETWEEN_CLEANS . " seconds ago so no cleaning needed."); } return false; } /*核心函數,處理圖片並寫入緩存*/ protected function processImageAndWriteToCache($localImage){ //獲取圖片信息 $sData = getimagesize($localImage); //圖像類型標記 $origType = $sData[2]; //mime類型 $mimeType = $sData['mime']; //寫日誌,記錄傳入圖像的mime類型 $this->debug(3, "Mime type of image is $mimeType"); //進行圖像mime類型驗證,只容許gif , jpg 和 png if(! preg_match('/^image\/(?:gif|jpg|jpeg|png)$/i', $mimeType)){ //若是不是這四種類型,記錄錯誤信息,並退出腳本 return $this->error("The image being resized is not a valid gif, jpg or png."); } //圖片處理須要GD庫支持,這裏檢測是否安裝了GD庫 if (!function_exists ('imagecreatetruecolor')) { //沒有安裝的話推出腳本 return $this->error('GD Library Error: imagecreatetruecolor does not exist - please contact your webhost and ask them to install the GD library'); } //若是安裝了GD庫,而且支持圖像過濾器函數imagefilter,且支持IMG_FILTER_NEGATE常量 if (function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) { //定義一個過濾器效果數組,後面的數字表明須要額外傳入的參數 $imageFilters = array ( //負片 1 => array (IMG_FILTER_NEGATE, 0), //黑白的 2 => array (IMG_FILTER_GRAYSCALE, 0), //亮度級別 3 => array (IMG_FILTER_BRIGHTNESS, 1), //對比度級別 4 => array (IMG_FILTER_CONTRAST, 1), //圖像轉換爲制定顏色 5 => array (IMG_FILTER_COLORIZE, 4), //突出邊緣 6 => array (IMG_FILTER_EDGEDETECT, 0), //浮雕 7 => array (IMG_FILTER_EMBOSS, 0), //用高斯算法模糊圖像 8 => array (IMG_FILTER_GAUSSIAN_BLUR, 0), //模糊圖像 9 => array (IMG_FILTER_SELECTIVE_BLUR, 0), //平均移除法來達到輪廓效果 10 => array (IMG_FILTER_MEAN_REMOVAL, 0), //平滑處理 11 => array (IMG_FILTER_SMOOTH, 0), ); } //生成圖片寬度,由get中w參數指定,默認爲0 $new_width = (int) abs ($this->param('w', 0)); //生成圖片高度,由get中h參數指定,默認爲0 $new_height = (int) abs ($this->param('h', 0)); //生成圖片縮放模式,由get中zc參數指定,默認爲配置文件中DEFAULT_ZC的值 $zoom_crop = (int) $this->param('zc', DEFAULT_ZC); //生成圖片的質量,由get中q參數指定,默認爲配置文件中DEFAULT_Q的值 $quality = (int) abs ($this->param('q', DEFAULT_Q)); //裁剪的位置 $align = $this->cropTop ? 't' : $this->param('a', 'c'); //須要進行的圖片處理操做,多個過濾器用"|"分割,可選參數請參看$imageFilters處的註釋,因爲不一樣的過濾器須要的參數不一樣,如一個過濾器須要多個參數,多個參數用,分隔。例:1,2|3,1,1 表明對圖像分別應用1和3過濾效果,1和3所對應的過濾效果是由$imageFilters數組肯定的,其中1號過濾器還須要一個額外的參數,這裏傳了1,3號過濾器還須要2個額外的參數,這裏傳了1和1. $filters = $this->param('f', DEFAULT_F); //是否對圖片進行銳化,由get中s參數指定,默認爲配置文件中DEFAULT_S的值 $sharpen = (bool) $this->param('s', DEFAULT_S); //生成圖片的默認背景畫布顏色,由get中cc參數指定,默認爲配置文件中DEFAULT_CC的值 $canvas_color = $this->param('cc', DEFAULT_CC); //生成png圖片的背景是否透明 $canvas_trans = (bool) $this->param('ct', '1'); // 若是高度和寬度都沒有指定,設置他們爲100*100 if ($new_width == 0 && $new_height == 0) { $new_width = 100; $new_height = 100; } // 限制最大高度和最大寬度 $new_width = min ($new_width, MAX_WIDTH); $new_height = min ($new_height, MAX_HEIGHT); // 檢測並設置php運行最大佔用內存 $this->setMemoryLimit(); // 打開圖像資源 $image = $this->openImage ($mimeType, $localImage); //若是打開失敗,記錄信息並退出腳本 if ($image === false) { return $this->error('Unable to open image.'); } // 得到原始圖片,也就是上面打開圖片的寬和高 $width = imagesx ($image); $height = imagesy ($image); $origin_x = 0; $origin_y = 0; // 若是新生成圖片的寬或高沒有指定,則用此等比算法算出高或寬的值 if ($new_width && !$new_height) { $new_height = floor ($height * ($new_width / $width)); } else if ($new_height && !$new_width) { $new_width = floor ($width * ($new_height / $height)); } // 若是縮放模式選擇的是3,也就是說get中zc=3或者配置文件中DEFAULT_ZC=3,則進行等比縮放,不裁剪 if ($zoom_crop == 3) { $final_height = $height * ($new_width / $width); //根據等比算法設置等比計算後的寬或高 if ($final_height > $new_height) { $new_width = $width * ($new_height / $height); } else { $new_height = $final_height; } } // 利用處理完畢的長和寬建立新畫布, $canvas = imagecreatetruecolor ($new_width, $new_height); //關閉混色模式,也就是把PNG的alpha值保存,從而使背景透明 imagealphablending ($canvas, false); //進行默認畫布顏色的檢測並轉換,若是給出的是3個字符長度表示的顏色值 if (strlen($canvas_color) == 3) { //if is 3-char notation, edit string into 6-char notation //轉換爲6個字符表示的顏色值 $canvas_color = str_repeat(substr($canvas_color, 0, 1), 2) . str_repeat(substr($canvas_color, 1, 1), 2) . str_repeat(substr($canvas_color, 2, 1), 2); //若是不是3個長度也不是6個長度的字符串,則爲非法字符串,設置爲默認值 } else if (strlen($canvas_color) != 6) { $canvas_color = DEFAULT_CC; } //將上面獲得的R 、G 、B 三種顏色值轉換爲10進製表示 $canvas_color_R = hexdec (substr ($canvas_color, 0, 2)); $canvas_color_G = hexdec (substr ($canvas_color, 2, 2)); $canvas_color_B = hexdec (substr ($canvas_color, 4, 2)); // 若是傳入圖片的格式是png,而且配置文件設置png背景顏色爲透明,而且在get傳入了ct的值爲真,那麼就設置背景顏色爲透明 if(preg_match('/^image\/png$/i', $mimeType) && !PNG_IS_TRANSPARENT && $canvas_trans){ $color = imagecolorallocatealpha ($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 127); //反之設置爲不透明 }else{ $color = imagecolorallocatealpha ($canvas, $canvas_color_R, $canvas_color_G, $canvas_color_B, 0); } // 使用分配的顏色填充背景 imagefill ($canvas, 0, 0, $color); // 若是縮放模式選擇的是2,那麼畫布的體積是按傳入的值建立的,並計算出邊框的寬度 if ($zoom_crop == 2) { //等比縮放的高度 $final_height = $height * ($new_width / $width); //若是計算出的等比高度,大於傳入的高度 if ($final_height > $new_height) { //origin_x等於傳入的新高度的二分之一 $origin_x = $new_width / 2; //設置新寬度爲等比計算出的值 $new_width = $width * ($new_height / $height); //計算出兩次origin_x的差值 $origin_x = round ($origin_x - ($new_width / 2)); //不然,計算出兩次origin_y的差值 } else { $origin_y = $new_height / 2; $new_height = $final_height; $origin_y = round ($origin_y - ($new_height / 2)); } } // 保存圖像時保存完整的alpha信息 imagesavealpha ($canvas, true); //若是縮放模式選擇的是1或2或3 if ($zoom_crop > 0) { $src_x = $src_y = 0; //圖片原寬度 $src_w = $width; //圖片原高度 $src_h = $height; //圖片縱橫比 $cmp_x = $width / $new_width; $cmp_y = $height / $new_height; //裁剪算法 if ($cmp_x > $cmp_y) { $src_w = round ($width / $cmp_x * $cmp_y); $src_x = round (($width - ($width / $cmp_x * $cmp_y)) / 2); } else if ($cmp_y > $cmp_x) { $src_h = round ($height / $cmp_y * $cmp_x); $src_y = round (($height - ($height / $cmp_y * $cmp_x)) / 2); } // 根據傳入參數算出裁剪的位置 if ($align) { if (strpos ($align, 't') !== false) { $src_y = 0; } if (strpos ($align, 'b') !== false) { $src_y = $height - $src_h; } if (strpos ($align, 'l') !== false) { $src_x = 0; } if (strpos ($align, 'r') !== false) { $src_x = $width - $src_w; } } //將圖像根據算法進行裁剪,並拷貝到背景圖片上 imagecopyresampled ($canvas, $image, $origin_x, $origin_y, $src_x, $src_y, $new_width, $new_height, $src_w, $src_h); } else { //裁剪模式選擇的是0,則不進行裁剪,並生成圖像 imagecopyresampled ($canvas, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height); } //若是定義了圖片處理操做,而且支持圖片處理函數 if ($filters != '' && function_exists ('imagefilter') && defined ('IMG_FILTER_NEGATE')) { // 分割每一個過濾處理 $filterList = explode ('|', $filters); foreach ($filterList as $fl) { //分割一個過濾操做中的參數 $filterSettings = explode (',', $fl); //若是所選的過濾操做存在 if (isset ($imageFilters[$filterSettings[0]])) { //將全部參數轉爲int類型 for ($i = 0; $i < 4; $i ++) { if (!isset ($filterSettings[$i])) { $filterSettings[$i] = null; } else { $filterSettings[$i] = (int) $filterSettings[$i]; } } //根據$imageFilters中定義的每一個過濾效果須要的參數的不一樣,對圖像應用過濾器效果 switch ($imageFilters[$filterSettings[0]][1]) { case 1: imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1]); break; case 2: imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2]); break; case 3: imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3]); break; case 4: imagefilter ($canvas, $imageFilters[$filterSettings[0]][0], $filterSettings[1], $filterSettings[2], $filterSettings[3], $filterSettings[4]); break; default: imagefilter ($canvas, $imageFilters[$filterSettings[0]][0]); break; } } } } // 若是設置了銳化值,而且系統支持銳化函數,則進行銳化操做 if ($sharpen && function_exists ('imageconvolution')) { $sharpenMatrix = array ( array (-1,-1,-1), array (-1,16,-1), array (-1,-1,-1), ); $divisor = 8; $offset = 0; imageconvolution ($canvas, $sharpenMatrix, $divisor, $offset); } //若是圖片是PNG或者GIF,則用imagetruecolortopalette來減少他們的體積 if ( (IMAGETYPE_PNG == $origType || IMAGETYPE_GIF == $origType) && function_exists('imageistruecolor') && !imageistruecolor( $image ) && imagecolortransparent( $image ) > 0 ){ imagetruecolortopalette( $canvas, false, imagecolorstotal( $image ) ); } //根據生成的不一樣圖片類型,生成圖片緩存,$imgType的值用於生成安全頭 $imgType = ""; $tempfile = tempnam($this->cacheDirectory, 'timthumb_tmpimg_'); if(preg_match('/^image\/(?:jpg|jpeg)$/i', $mimeType)){ $imgType = 'jpg'; imagejpeg($canvas, $tempfile, $quality); } else if(preg_match('/^image\/png$/i', $mimeType)){ $imgType = 'png'; imagepng($canvas, $tempfile, floor($quality * 0.09)); } else if(preg_match('/^image\/gif$/i', $mimeType)){ $imgType = 'gif'; imagegif($canvas, $tempfile); } else { //若是不是以上三種類型,記錄此次錯誤並退出 return $this->sanityFail("Could not match mime type after verifying it previously."); } //優先使用optipng工具進行png圖片的壓縮,前提是你裝了這個工具 if($imgType == 'png' && OPTIPNG_ENABLED && OPTIPNG_PATH && @is_file(OPTIPNG_PATH)){ //記錄optipng的地址 $exec = OPTIPNG_PATH; //寫日誌,記錄optipng將運行,級別3 $this->debug(3, "optipng'ing $tempfile"); //獲取圖片大小 $presize = filesize($tempfile); //進行圖片壓縮操做 $out = `$exec -o1 $tempfile`; //清除文件狀態緩存 clearstatcache(); //獲取壓縮後的文件大小 $aftersize = filesize($tempfile); //算出壓縮了多大 $sizeDrop = $presize - $aftersize; //根據算出的不一樣的值,寫日誌,級別1 if($sizeDrop > 0){ $this->debug(1, "optipng reduced size by $sizeDrop"); } else if($sizeDrop < 0){ $this->debug(1, "optipng increased size! Difference was: $sizeDrop"); } else { $this->debug(1, "optipng did not change image size."); } //optipng不存在,就嘗試使用pngcrush } else if($imgType == 'png' && PNGCRUSH_ENABLED && PNGCRUSH_PATH && @is_file(PNGCRUSH_PATH)){ $exec = PNGCRUSH_PATH; //和optipng不一樣的是,pngcrush會將處理完的文件新生成一個文件,因此這裏新建個文件 $tempfile2 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_'); //寫日誌,記錄文件名 $this->debug(3, "pngcrush'ing $tempfile to $tempfile2"); //運行pngcrush $out = `$exec $tempfile $tempfile2`; $todel = ""; //若是生成文件成功 if(is_file($tempfile2)){ //算出壓縮後的文件大小的差值 $sizeDrop = filesize($tempfile) - filesize($tempfile2); //若是是一次有效的壓縮,則將壓縮後的文件做爲緩存文件 if($sizeDrop > 0){ $this->debug(1, "pngcrush was succesful and gave a $sizeDrop byte size reduction"); $todel = $tempfile; $tempfile = $tempfile2; //不然的話則這個文件沒有存在的必要 } else { $this->debug(1, "pngcrush did not reduce file size. Difference was $sizeDrop bytes."); $todel = $tempfile2; } //沒有運行成功也須要刪除這個文件 } else { $this->debug(3, "pngcrush failed with output: $out"); $todel = $tempfile2; } //刪除無效文件或壓縮前比較大的文件 @unlink($todel); } //在緩存圖片上寫入安全頭 $this->debug(3, "Rewriting image with security header."); //建立一個新的緩存文件 $tempfile4 = tempnam($this->cacheDirectory, 'timthumb_tmpimg_'); // $context = stream_context_create (); //讀取生成的圖片緩存內容 $fp = fopen($tempfile,'r',0,$context); //向新緩存文件寫入安全頭,安全頭的長度應該老是$this->filePrependSecurityBlock的長度 + 6 file_put_contents($tempfile4, $this->filePrependSecurityBlock . $imgType . ' ?' . '>'); //將讀取出來的緩存圖片內容寫入新緩存文件 file_put_contents($tempfile4, $fp, FILE_APPEND); //關閉文件資源 fclose($fp); //刪除以前不安全的圖片緩存文件 @unlink($tempfile); //寫日誌,給緩存文件加鎖~~ $this->debug(3, "Locking and replacing cache file."); //建立鎖文件文件名 $lockFile = $this->cachefile . '.lock'; //建立或打開鎖文件 $fh = fopen($lockFile, 'w'); //建立失敗直接退出 if(! $fh){ return $this->error("Could not open the lockfile for writing an image."); } //若是給鎖文件加入寫入鎖成功 if(flock($fh, LOCK_EX)){ //刪除原緩存文件 @unlink($this->cachefile); //重命名覆蓋,將安全的緩存文件做爲緩存文件 rename($tempfile4, $this->cachefile); //釋放寫入鎖 flock($fh, LOCK_UN); //釋放資源 fclose($fh); //刪除鎖文件 @unlink($lockFile); //不然 } else { //關閉資源 fclose($fh); //刪除鎖文件 @unlink($lockFile); //刪除安全的緩存文件 @unlink($tempfile4); //記錄錯誤並退出 return $this->error("Could not get a lock for writing."); } //寫日誌,記錄操做完成 $this->debug(3, "Done image replace with security header. Cleaning up and running cleanCache()"); //釋放圖片資源 imagedestroy($canvas); imagedestroy($image); //生成緩存成功返回真 return true; } /*此函數用來獲取服務器文檔根目錄*/ protected function calcDocRoot(){ //直接獲取文檔根目錄 $docRoot = @$_SERVER['DOCUMENT_ROOT']; //若是定義了LOCAL_FILE_BASE_DIRECTORY,則使用此值 if (defined('LOCAL_FILE_BASE_DIRECTORY')) { $docRoot = LOCAL_FILE_BASE_DIRECTORY; } //若是沒有獲取到文檔根目錄,也就是DOCUMENT_ROOT的值 if(!isset($docRoot)){ //寫一條記錄,說明DOCUMENT_ROOT沒找到,注意level是3 $this->debug(3, "DOCUMENT_ROOT is not set. This is probably windows. Starting search 1."); //用SCRIPT_FILENAME和PHP_SELF來獲得文檔根目錄 if(isset($_SERVER['SCRIPT_FILENAME'])){ $docRoot = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0-strlen($_SERVER['PHP_SELF']))); //寫一條記錄,說明DOCUMENT_ROOT的值是經過SCRIPT_FILENAME和PHP_SELF來得的,級別3 $this->debug(3, "Generated docRoot using SCRIPT_FILENAME and PHP_SELF as: $docRoot"); } } //若是仍是沒有獲取到文檔根目錄 if(!isset($docRoot)){ //先寫一條記錄,說明仍是沒獲得DOCUMENT_ROOT,級別3 $this->debug(3, "DOCUMENT_ROOT still is not set. Starting search 2."); //經過PATH_TRANSLATED和PHP_SELF來獲得文檔根目錄,關於PATH_TRANSLATED的說明能夠看這裏:http://blogs.msdn.com/b/david.wang/archive/2005/08/04/what-is-path-translated.aspx if(isset($_SERVER['PATH_TRANSLATED'])){ $docRoot = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0-strlen($_SERVER['PHP_SELF']))); //寫記錄,說明說明DOCUMENT_ROOT的值是經過PATH_TRANSLATED和PHP_SELF來得的,級別3 $this->debug(3, "Generated docRoot using PATH_TRANSLATED and PHP_SELF as: $docRoot"); } } //若是文檔根目錄不是服務器根目錄,則去掉最後一個 '/' if($docRoot && $_SERVER['DOCUMENT_ROOT'] != '/'){ $docRoot = preg_replace('/\/$/', '', $docRoot); } //寫記錄,說明文檔根目錄的值,級別3 $this->debug(3, "Doc root is: " . $docRoot); //賦值給成員屬性 $this->docRoot = $docRoot; } /*此函數用來獲取本地圖片地址,參數src是相對與文檔根目錄的地址*/ protected function getLocalImagePath($src){ //去掉開頭的 / $src = ltrim($src, '/'); //若是前面沒有獲取到文檔根目錄,出於安全考慮,那麼這裏只能對timthumbs.php所在的目錄下的圖片進行操做 if(! $this->docRoot){ //寫日誌,級別3,說明下面進行圖片地址的檢查 $this->debug(3, "We have no document root set, so as a last resort, lets check if the image is in the current dir and serve that."); //獲取去掉全部路徑信息的文件名 $file = preg_replace('/^.*?([^\/\\\\]+)$/', '$1', $src); //若是圖片文件和timthumb.php在同一目錄下 if(is_file($file)){ //返回此圖片的路徑 return $this->realpath($file); } //若是圖片文件和timthumb.php不在同一目錄下,寫錯誤信息,出於安全考慮,不會容許一個沒有文檔根目錄而且在timthumbs.php所在的目錄之外的文件 return $this->error("Could not find your website document root and the file specified doesn't exist in timthumbs directory. We don't support serving files outside timthumb's directory without a document root for security reasons."); } //嘗試找這張圖片,若是圖片地址存在 if(file_exists ($this->docRoot . '/' . $src)) { //寫日誌,記錄文件地址,級別3 $this->debug(3, "Found file as " . $this->docRoot . '/' . $src); //返回圖片路徑 $real = $this->realpath($this->docRoot . '/' . $src); //驗證圖片路徑是否在本機 if(stripos($real, $this->docRoot) === 0){ //是的話返回圖片地址 return $real; } else { //不然寫日誌,沒找到指定文件,級別1 $this->debug(1, "Security block: The file specified occurs outside the document root."); } } //接着找。。。 $absolute = $this->realpath('/' . $src); //若是決定地址存在 if($absolute && file_exists($absolute)){ //寫日誌,記錄圖片絕對地址,級別3 $this->debug(3, "Found absolute path: $absolute"); //若是文檔根目錄沒有定義,記錄這個錯誤信息 if(! $this->docRoot){ $this->sanityFail("docRoot not set when checking absolute path."); } //驗證圖片路徑是否在本機 if(stripos($absolute, $this->docRoot) === 0){ //在的話返回圖片地址 return $absolute; } else { //不然寫日誌,沒找到指定的文件,級別1 $this->debug(1, "Security block: The file specified occurs outside the document root."); } } //若是還沒找到指定文件,則逐級向上查找 $base = $this->docRoot; // 獲取查詢子目錄列表 if (strstr($_SERVER['SCRIPT_FILENAME'],':')) { $sub_directories = explode('\\', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME'])); } else { $sub_directories = explode('/', str_replace($this->docRoot, '', $_SERVER['SCRIPT_FILENAME'])); } //遍歷子目錄數組 foreach ($sub_directories as $sub){ //從新組合請求地址 $base .= $sub . '/'; //寫日誌,記錄搜索記錄,級別3 $this->debug(3, "Trying file as: " . $base . $src); //若是找到了這個文件 if(file_exists($base . $src)){ //寫日誌,記錄文件地址,級別3 $this->debug(3, "Found file as: " . $base . $src); //獲得實際地址 $real = $this->realpath($base . $src); //若是實際地址的確在本機中,那麼返回這個地址 if(stripos($real, $this->realpath($this->docRoot)) === 0){ return $real; } else { //找不到就寫日誌,沒找到,級別1 $this->debug(1, "Security block: The file specified occurs outside the document root."); } } } //還找不到的話,就返回false; return false; } /*此函數用來得到傳入文件參數的真實路徑*/ protected function realpath($path){ //去除路徑中帶有..的相對路徑 $remove_relatives = '/\w+\/\.\.\//'; while(preg_match($remove_relatives,$path)){ $path = preg_replace($remove_relatives, '', $path); } //若是去除後路徑中仍有..的相對路徑,則用realpath函數返回路徑,不然直接返回便可 return preg_match('#^\.\./|/\.\./#', $path) ? realpath($path) : $path; } /*此函數用來記錄在析構函數中須要刪除的資源列表*/ protected function toDelete($name){ //寫日誌,記錄須要刪除的文件信息 $this->debug(3, "Scheduling file $name to delete on destruct."); //添加到待刪除數組 $this->toDeletes[] = $name; } /*此函數是截圖操做的具體實現*/ protected function serveWebshot(){ //寫日誌,記錄開始截圖操做,級別3 $this->debug(3, "Starting serveWebshot"); //一段提示文字,能夠到http://code.google.com/p/timthumb/上按照教程進行網站截圖設置 $instr = "Please follow the instructions at http://code.google.com/p/timthumb/ to set your server up for taking website screenshots."; //若是CutyCapt不存在 if(! is_file(WEBSHOT_CUTYCAPT)){ //退出執行並記錄,CutyCapt未被安裝 return $this->error("CutyCapt is not installed. $instr"); } //若是xvfb不存在 if(! is_file(WEBSHOT_XVFB)){ //退出執行並記錄,xvfb未被安裝 return $this->Error("Xvfb is not installed. $instr"); } //CUTYCAPT地址 $cuty = WEBSHOT_CUTYCAPT; //xvfb地址 $xv = WEBSHOT_XVFB; //截圖屏幕寬度 $screenX = WEBSHOT_SCREEN_X; //截圖屏幕高度 $screenY = WEBSHOT_SCREEN_Y; //截圖色深 $colDepth = WEBSHOT_COLOR_DEPTH; //截圖保存格式 $format = WEBSHOT_IMAGE_FORMAT; //截圖超時時間,單位ms $timeout = WEBSHOT_TIMEOUT * 1000; //USER_AGENT頭 $ua = WEBSHOT_USER_AGENT; //是否啓用js $jsOn = WEBSHOT_JAVASCRIPT_ON ? 'on' : 'off'; //是否啓用java $javaOn = WEBSHOT_JAVA_ON ? 'on' : 'off'; //是否啓用其餘插件 $pluginsOn = WEBSHOT_PLUGINS_ON ? 'on' : 'off'; //是否啓用代理 $proxy = WEBSHOT_PROXY ? ' --http-proxy=' . WEBSHOT_PROXY : ''; //在緩存文件目錄,創建一個具備惟一文件名的文件,文件名前綴爲timthumb_webshot,用戶存儲截圖後的圖片 $tempfile = tempnam($this->cacheDirectory, 'timthumb_webshot'); //目標網站地址 $url = $this->src; //驗證url合法性 if(! preg_match('/^https?:\/\/[a-zA-Z0-9\.\-]+/i', $url)){ //不合法退出執行 return $this->error("Invalid URL supplied."); } //過濾掉非法字符 $url = preg_replace('/[^A-Za-z0-9\-\.\_\~:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=]+/', '', $url); //優先使用CUTYCAPT if(WEBSHOT_XVFB_RUNNING){ //設置系統變量,配置圖形輸出顯示。 putenv('DISPLAY=:100.0'); //組裝shell命令 $command = "$cuty $proxy --max-wait=$timeout --user-agent=\"$ua\" --javascript=$jsOn --java=$javaOn --plugins=$pluginsOn --js-can-open-windows=off --url=\"$url\" --out-format=$format --out=$tempfile"; //不然使用XVFB } else { $command = "$xv --server-args=\"-screen 0, {$screenX}x{$screenY}x{$colDepth}\" $cuty $proxy --max-wait=$timeout --user-agent=\"$ua\" --javascript=$jsOn --java=$javaOn --plugins=$pluginsOn --js-can-open-windows=off --url=\"$url\" --out-format=$format --out=$tempfile"; } //寫日誌,記錄執行的命令,級別3 $this->debug(3, "Executing command: $command"); //執行命令並捕獲輸出 $out = `$command`; //寫日誌,記錄輸出,級別3 $this->debug(3, "Received output: $out"); //若是剛剛建立的惟一文件名的文件失敗 if(! is_file($tempfile)){ //設置404錯誤 $this->set404(); //推出腳本 return $this->error("The command to create a thumbnail failed."); } //啓用裁剪 $this->cropTop = true; //對截取到的圖片文件處理並生成緩存 if($this->processImageAndWriteToCache($tempfile)){ //成功的話寫日誌,並從緩存讀取此圖片 $this->debug(3, "Image processed succesfully. Serving from cache"); //返回從緩存中讀取的文件內容 return $this->serveCacheFile(); //沒成功就返回假咯 } else { return false; } } /*此函數用來從外部url獲取圖像*/ protected function serveExternalImage(){ //驗證url合法性 if(! preg_match('/^https?:\/\/[a-zA-Z0-9\-\.]+/i', $this->src)){ $this->error("Invalid URL supplied."); return false; } //生成臨時緩存文件 $tempfile = tempnam($this->cacheDirectory, 'timthumb'); //寫日誌,記錄讀取外部圖像到臨時文件,級別3 $this->debug(3, "Fetching external image into temporary file $tempfile"); //將臨時緩存文件加入到待刪除列表 $this->toDelete($tempfile); //請求url並將結果寫入到臨時緩存文件中,若是沒有成功 if(! $this->getURL($this->src, $tempfile)){ //刪除此緩存文件 @unlink($this->cachefile); //再建立一個新的緩存文件 touch($this->cachefile); //寫日誌,記錄錯誤信息,級別3 $this->debug(3, "Error fetching URL: " . $this->lastURLError); //寫錯誤信息,並退出 $this->error("Error reading the URL you specified from remote host." . $this->lastURLError); return false; } //獲得獲取到圖片的MIME類型 $mimeType = $this->getMimeType($tempfile); //若是不在這三種類型中 if(! preg_match("/^image\/(?:jpg|jpeg|gif|png)$/i", $mimeType)){ //寫日誌,記錄錯誤信息,級別3 $this->debug(3, "Remote file has invalid mime type: $mimeType"); //刪除現有緩存文件 @unlink($this->cachefile); //建立新緩存文件 touch($this->cachefile); //寫錯誤信息並退出 $this->error("The remote file is not a valid image."); return false; } //處理圖像並緩存 if($this->processImageAndWriteToCache($tempfile)){ $this->debug(3, "Image processed succesfully. Serving from cache"); //成功的話返回緩存信息 return $this->serveCacheFile(); } else { //失敗返回假 return false; } } /*此函數用來將curl獲取到的數據寫入對應文件中*/ public static function curlWrite($h, $d){ //將數據寫入文件 fwrite(self::$curlFH, $d); //記錄數據長度 self::$curlDataWritten += strlen($d); //若是圖片大小超過了配置文件的限制,則返回0 if(self::$curlDataWritten > MAX_FILE_SIZE){ return 0; //不然返回圖片大小 } else { return strlen($d); } } /*此函數用來讀取並輸出服務端緩存*/ protected function serveCacheFile(){ //寫日誌,記錄讀取緩存的地址 $this->debug(3, "Serving {$this->cachefile}"); //若是緩存地址無效 if(! is_file($this->cachefile)){ //添加到錯誤記錄 $this->error("serveCacheFile called in timthumb but we couldn't find the cached file."); //中止執行腳本 return false; } //緩存地址有效的話,已只讀方式打開文件 $fp = fopen($this->cachefile, 'rb'); //若是打開失敗,直接退出腳本,並記錄錯誤信息 if(! $fp){ return $this->error("Could not open cachefile."); } //設定文件指針跳過filePrependSecurityBlock值,也就是跳過安全頭後開始讀 fseek($fp, strlen($this->filePrependSecurityBlock), SEEK_SET); //讀出文件的mime類型 $imgType = fread($fp, 3); //再跳過這個mime類型的值 fseek($fp, 3, SEEK_CUR); //若是如今文件的指針不是在安全頭後6個字符的位置,說明緩存文件可能已損壞 if(ftell($fp) != strlen($this->filePrependSecurityBlock) + 6){ //刪除此緩存文件 @unlink($this->cachefile); //記錄錯誤並退出執行 return $this->error("The cached image file seems to be corrupt."); } //緩存圖片的實際大小應該是文件大小 - 安全頭大小 $imageDataSize = filesize($this->cachefile) - (strlen($this->filePrependSecurityBlock) + 6); //設置輸出必要的HTTP頭 $this->sendImageHeaders($imgType, $imageDataSize); //輸出文件指針處全部剩餘數據 $bytesSent = @fpassthru($fp); //關閉文件資源 fclose($fp); //若是此方法執行成功,則返回真 if($bytesSent > 0){ return true; } //若是fpassthru不成功,則用file_get_contents讀取並輸出 $content = file_get_contents ($this->cachefile); //若是讀取成功 if ($content != FALSE) { //截取掉安全頭 $content = substr($content, strlen($this->filePrependSecurityBlock) + 6); //輸出圖像 echo $content; //寫日誌,記錄讀取緩存的方式 $this->debug(3, "Served using file_get_contents and echo"); return true; //讀取失敗的話記錄錯誤信息並退出執行 } else { $this->error("Cache file could not be loaded."); return false; } } /*此函數設置圖片輸出必要的http頭*/ protected function sendImageHeaders($mimeType, $dataSize){ //補全圖片的mime信息 if(! preg_match('/^image\//i', $mimeType)){ $mimeType = 'image/' . $mimeType; } //將jpg的mime類型寫標準,這裏不標準的緣由是在驗證文件安全頭時追求了便利性 if(strtolower($mimeType) == 'image/jpg'){ $mimeType = 'image/jpeg'; } //瀏覽器緩存失效時間 $gmdate_expires = gmdate ('D, d M Y H:i:s', strtotime ('now +10 days')) . ' GMT'; //文檔最後被修改時間,用來讓瀏覽器判斷是否須要從新請求頁面 $gmdate_modified = gmdate ('D, d M Y H:i:s') . ' GMT'; // 設置HTTP頭 header ('Content-Type: ' . $mimeType); header ('Accept-Ranges: none'); header ('Last-Modified: ' . $gmdate_modified); header ('Content-Length: ' . $dataSize); //若是配置文件禁止瀏覽器緩存,則設置相應的HTTP頭信息 if(BROWSER_CACHE_DISABLE){ $this->debug(3, "Browser cache is disabled so setting non-caching headers."); header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); header("Pragma: no-cache"); header('Expires: ' . gmdate ('D, d M Y H:i:s', time())); //不然按配置文件設置緩存時間 } else { $this->debug(3, "Browser caching is enabled"); header('Cache-Control: max-age=' . BROWSER_CACHE_MAX_AGE . ', must-revalidate'); header('Expires: ' . $gmdate_expires); } //運行成功返回真 return true; } /*自定義的驗證函數*/ protected function securityChecks(){ } /*此函數用來獲取$_GET數組中的參數,並容許設置默認值*/ protected function param($property, $default = ''){ //若是參數存在則返回此參數 if (isset ($_GET[$property])) { return $_GET[$property]; //不存在的話返回默認值 } else { return $default; } } /*此函數根據傳入mime類型,打開圖像資源*/ protected function openImage($mimeType, $src){ switch ($mimeType) { case 'image/jpeg': $image = imagecreatefromjpeg ($src); break; case 'image/png': $image = imagecreatefrompng ($src); break; case 'image/gif': $image = imagecreatefromgif ($src); break; //不是這三種的話,腳本退出 default: $this->error("Unrecognised mimeType"); } //返回圖像資源 return $image; } /*沒啥說的,獲取客戶端IP*/ protected function getIP(){ $rem = @$_SERVER["REMOTE_ADDR"]; $ff = @$_SERVER["HTTP_X_FORWARDED_FOR"]; $ci = @$_SERVER["HTTP_CLIENT_IP"]; if(preg_match('/^(?:192\.168|172\.16|10\.|127\.)/', $rem)){ if($ff){ return $ff; } if($ci){ return $ci; } return $rem; } else { if($rem){ return $rem; } if($ff){ return $ff; } if($ci){ return $ci; } return "UNKNOWN"; } } /*debug運行日誌函數,用來向系統日誌記錄操做信息*/ protected function debug($level, $msg){ //若是開啓了debug,而且$level也就是調試級別小於等於配置文件中的值,則開始記錄 if(DEBUG_ON && $level <= DEBUG_LEVEL){ //格式化並記錄開始時間,保留小數點後6位,這個時間表明實例化類後到這個debug執行所經歷的時間 $execTime = sprintf('%.6f', microtime(true) - $this->startTime); //這個值表明從上次debug結束,到此次debug的用時 $tick = sprintf('%.6f', 0); //若是上次debug時間存在,則用當前時間減去上次debug時間,得出差值 if($this->lastBenchTime > 0){ $tick = sprintf('%.6f', microtime(true) - $this->lastBenchTime); } //將時間更新 $this->lastBenchTime = microtime(true); //將debug信息寫到系統日誌中 error_log("TimThumb Debug line " . __LINE__ . " [$execTime : $tick]: $msg"); } } /*此函數用來記錄未知BUG*/ protected function sanityFail($msg){ //記錄BUG信息 return $this->error("There is a problem in the timthumb code. Message: Please report this error at <a href='http://code.google.com/p/timthumb/issues/list'>timthumb's bug tracking page</a>: $msg"); } /*此函數用來返回圖片文件的MIME信息*/ protected function getMimeType($file){ //獲取圖片文件的信息 $info = getimagesize($file); //成功則返回MIME信息 if(is_array($info) && $info['mime']){ return $info['mime']; } //失敗返回空 return ''; } /*此函數用來檢測並設置php運行時最大佔用內存的值*/ protected function setMemoryLimit(){ //獲取php.ini中的最大內存佔用的值 $inimem = ini_get('memory_limit'); //將上面獲得的值轉換爲以字節爲單位的數值 $inibytes = timthumb::returnBytes($inimem); //算出配置文件中內存限制的值 $ourbytes = timthumb::returnBytes(MEMORY_LIMIT); //若是php配置文件中的值小於本身設定的值 if($inibytes < $ourbytes){ //則將php.ini配置中關於最大內存的值設置爲本身設定的值 ini_set ('memory_limit', MEMORY_LIMIT); //寫日誌,記錄改變內存操做,級別3 $this->debug(3, "Increased memory from $inimem to " . MEMORY_LIMIT); //若是本身設置的值小於php.ini中的值 } else { //則不進行任何操做,寫日誌記錄此條信息便可,級別3 $this->debug(3, "Not adjusting memory size because the current setting is " . $inimem . " and our size of " . MEMORY_LIMIT . " is smaller."); } } /*此函數將G, KB, MB 轉爲B(字節)*/ protected static function returnBytes($size_str){ //取最後一個單位值,進行轉換操做,並返回轉換後的值 switch (substr ($size_str, -1)) { case 'M': case 'm': return (int)$size_str * 1048576; case 'K': case 'k': return (int)$size_str * 1024; case 'G': case 'g': return (int)$size_str * 1073741824; default: return $size_str; } } /*此函數用來將url中的資源讀取到tempfile文件中*/ protected function getURL($url, $tempfile){ //重置上次url請求錯誤信息 $this->lastURLError = false; //進行url編碼 $url = preg_replace('/ /', '%20', $url); //優先使用curl擴展 if(function_exists('curl_init')){ //寫日誌,記錄將使用curl擴展訪問url,級別3 $this->debug(3, "Curl is installed so using it to fetch URL."); //打開文件 self::$curlFH = fopen($tempfile, 'w'); //若是打開失敗,記錄錯誤信息並退出 if(! self::$curlFH){ $this->error("Could not open $tempfile for writing."); return false; } //重置寫入長度 self::$curlDataWritten = 0; //寫日誌,記錄訪問的url信息,級別3 $this->debug(3, "Fetching url with curl: $url"); //初始化curl $curl = curl_init($url); //curl選項設置 curl_setopt ($curl, CURLOPT_TIMEOUT, CURL_TIMEOUT); curl_setopt ($curl, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30"); curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt ($curl, CURLOPT_HEADER, 0); curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, FALSE); //關閉會話時執行curlWrite curl_setopt ($curl, CURLOPT_WRITEFUNCTION, 'timthumb::curlWrite'); @curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true); @curl_setopt ($curl, CURLOPT_MAXREDIRS, 10); //執行本次請求,並將結果賦給$curlResult $curlResult = curl_exec($curl); //釋放文件資源 fclose(self::$curlFH); //獲取最後一個受到的HTTP碼 $httpStatus = curl_getinfo($curl, CURLINFO_HTTP_CODE); //若是是404,那麼設置404錯誤並退出 if($httpStatus == 404){ $this->set404(); } //若是請求成功 if($curlResult){ //關閉curl,並執行curlWrite將數據寫到文件中 curl_close($curl); //返回真,請求完成 return true; //若是請求不成功 } else { //記錄錯誤信息 $this->lastURLError = curl_error($curl); //關閉資源 curl_close($curl); //執行不成功 return false; } //若是不支持curl,用file_get_contents獲取數據 } else { //獲取數據 $img = @file_get_contents ($url); //若是獲取失敗 if($img === false){ //記錄返回的錯誤信息數組 $err = error_get_last(); //若是記錄到了,而且有錯誤信息 if(is_array($err) && $err['message']){ //則記錄這個錯誤信息 $this->lastURLError = $err['message']; //不然的話記錄整個錯誤信息 } else { $this->lastURLError = $err; } //若是錯誤信息中有404,則設置爲404錯誤 if(preg_match('/404/', $this->lastURLError)){ $this->set404(); } //返回假 return false; } //若是將讀取的圖片寫入文件失敗 if(! file_put_contents($tempfile, $img)){ //寫錯誤信息並退出執行 $this->error("Could not write to $tempfile."); return false; } //沒問題的話執行成功 return true; } } /*此函數輸出指定的圖片,用於輸出錯誤信息中*/ protected function serveImg($file){ //獲取圖像信息 $s = getimagesize($file); //若是獲取不到圖像信息,推出 if(! ($s && $s['mime'])){ return false; } //設置http頭,輸出圖片 header ('Content-Type: ' . $s['mime']); header ('Content-Length: ' . filesize($file) ); header ('Cache-Control: no-store, no-cache, must-revalidate, max-age=0'); header ("Pragma: no-cache"); //使用readfile輸出圖片 $bytes = @readfile($file); if($bytes > 0){ return true; } //若是失敗,使用file_get_contents和echo輸出圖片 $content = @file_get_contents ($file); if ($content != FALSE){ echo $content; return true; } //還失敗的話返回假 return false; } /*此函數設置404 錯誤碼*/ protected function set404(){ $this->is404 = true; } /*此函數返回404錯誤碼*/ protected function is404(){ return $this->is404; } }