/**
  * @param $images
  * @param array $cachedImagesKeys
  */
 public static function setImagesCache($images, $cachedImagesKeys = [])
 {
     !static::$mc && (static::$mc = new mc());
     $fileNameAsKey = [];
     foreach ($images as $url => &$image) {
         $fileName = basename($url);
         if (!in_array($fileName, $cachedImagesKeys)) {
             $fileNameAsKey[$fileName] = $image;
         }
     }
     $fileNameAsKey && static::$mc->batchSet($fileNameAsKey);
     $cachedImagesKeys && static::$mc->touchKeys($cachedImagesKeys);
 }
 public static function handle($url, $packImages = false)
 {
     $postParam = static::parseUrlParam($url);
     $quickInfo = null;
     # try to process it
     try {
         # a valid tumblr url given
         if ($postParam) {
             $cachedInfo = Input::fetchQuickInfoCache($postParam);
             # quick response info found
             if ($cachedInfo) {
                 # make just header response
                 if ($_SERVER['REQUEST_METHOD'] === 'HEAD') {
                     syslog(LOG_INFO, "HEAD Response.");
                     foreach ($cachedInfo['headers'] as $header) {
                         header($header);
                     }
                 } else {
                     syslog(LOG_INFO, "Content Response.");
                     $content = $cachedInfo['content'];
                     switch ($cachedInfo['type']) {
                         case 'html':
                             Output::echoHtmlFile($content);
                             break;
                         case 'video':
                         case 'singlePhoto':
                             Output::redirect($content);
                             break;
                         case 'htmlZip':
                             Output::echoZipFile($content);
                             break;
                         case 'error':
                             Output::echoTxtFile($content);
                             break;
                     }
                 }
             } else {
                 $postJSON = Input::fetchPostInfoCache($postParam) ?: Input::queryTumblrApi($postParam);
                 # post json gotten
                 if ($postJSON) {
                     # save post info to memcached
                     Output::setPostInfoCache($postParam, $postJSON);
                     $postInfo = $postJSON['posts'][0];
                     $postType = Content::parsePostType($postInfo);
                     $parserName = 'parse' . ucfirst($postType);
                     switch ($postType) {
                         case 'audio':
                         case 'answer':
                         case 'conversation':
                         case 'link':
                         case 'regular':
                         case 'quote':
                             $output = Content::$parserName($postInfo);
                             $zipStr = Content::getHtmlZipPack($output);
                             Output::echoZipFile($zipStr);
                             $quickInfo = ['type' => 'htmlZip', 'content' => $zipStr];
                             break;
                         case 'video':
                             $output = Content::$parserName($postInfo);
                             # video source parsed
                             if ($output) {
                                 Output::redirect($output);
                                 $quickInfo = ['type' => 'video', 'content' => $output];
                             } else {
                                 $errMsg = "Can't not parse video post, maybe it's too complicated to get the video source out.";
                                 throw new Exception($errMsg);
                             }
                             break;
                         case 'unknow':
                         case 'photo':
                         default:
                             $photoUrls = Content::$parserName($postInfo);
                             $photoCount = count($photoUrls);
                             # photo found
                             if ($photoCount > 0) {
                                 # one photo
                                 if ($photoCount === 1) {
                                     Output::redirect($photoUrls[0]);
                                     $quickInfo = ['type' => 'singlePhoto', 'content' => $photoUrls[0]];
                                 } else {
                                     # to make a images pack
                                     if ($packImages) {
                                         $imagesCache = Input::fetchImagesCache($photoUrls);
                                         $total = count($photoUrls);
                                         $cached = count($imagesCache);
                                         $fetched = 0;
                                         $startTime = microtime(true);
                                         # get images
                                         $imagesCont = array_fill_keys($photoUrls, null);
                                         $randomOrder = array_values($photoUrls);
                                         shuffle($randomOrder);
                                         foreach ($randomOrder as $imgUrl) {
                                             $fileName = basename($imgUrl);
                                             # image in cache found
                                             if (isset($imagesCache[$fileName])) {
                                                 $imagesCont[$imgUrl] =& $imagesCache[$fileName];
                                             } else {
                                                 $imagesCont[$imgUrl] = Input::fetchImage($imgUrl);
                                                 # fetch from network
                                                 $imagesCont[$imgUrl] && static::$mc->singleSet($fileName, $imagesCont[$imgUrl]);
                                                 # write to cache
                                                 $fetched++;
                                             }
                                         }
                                         # output
                                         $zipPack = Content::getImagesZipPack($imagesCont);
                                         Output::echoZipFile($zipPack);
                                         # statement record
                                         $timeUsed = number_format(microtime(true) - $startTime, 3, '.', '');
                                         syslog(LOG_INFO, "Total: {$total}, From cache: {$cached}, From network: {$fetched}, Time used: {$timeUsed}s");
                                         # refresh cache
                                         static::$mc->touchKeys(array_keys($imagesCache));
                                         # Output::writeImagesToCache($images, array_keys($imagesCache));
                                         $quickInfo = ['HEADOnly' => true, 'headers' => []];
                                     } else {
                                         $page = Content::getImagesDownPage($photoUrls);
                                         $readme = "Sever overloading all the time so no more images packing, open the htm file with google chrome and DIY thank you.\r\n服务器扛不住,取消图片打包,请使用谷歌浏览器打开htm文件自行下载,靴靴。";
                                         $zipStr = Content::getHtmlZipPack($page, null, $readme);
                                         Output::echoZipFile($zipStr);
                                         $quickInfo = ['type' => 'htmlZip', 'content' => $zipStr];
                                     }
                                 }
                             } else {
                                 $errMsg = "No images found in the tumblr post.";
                                 throw new Exception($errMsg);
                             }
                             break;
                     }
                 } else {
                     $postParam = false;
                     # don't write quick response
                     $errMsg = 'No post info back from Tumblr.';
                     throw new Exception($errMsg);
                 }
             }
         } else {
             $errMsg = "Not a valid tumblr URL.";
             throw new Exception($errMsg);
         }
     } catch (Exception $e) {
         $errText = Content::getErrorText($e->getMessage());
         $quickInfo = ['type' => 'error', 'content' => $errText];
         Output::echoTxtFile($errText);
     } finally {
         if ($postParam && $quickInfo) {
             $quickInfo['headers'] = headers_list();
             Output::setQuickInfoCache($postParam, $quickInfo);
         }
     }
     return true;
 }