Explorar o código

Update to Favicon 1.2.0

https://github.com/ArthurHoaro/favicon/releases/tag/v1.2.0
Alexandre Alapetite %!s(int64=9) %!d(string=hai) anos
pai
achega
ca4138d021
Modificáronse 3 ficheiros con 173 adicións e 75 borrados
  1. 26 27
      lib/Favicon/DataAccess.php
  2. 124 48
      lib/Favicon/Favicon.php
  3. 23 0
      lib/Favicon/FaviconDLType.php

+ 26 - 27
lib/Favicon/DataAccess.php

@@ -9,33 +9,32 @@ namespace Favicon;
  **/
  **/
 class DataAccess {
 class DataAccess {
 	public function retrieveUrl($url) {
 	public function retrieveUrl($url) {
-		$this->set_context();
-		return @file_get_contents($url);
+	    $this->set_context();
+	    return @file_get_contents($url);
 	}
 	}
-
+	
 	public function retrieveHeader($url) {
 	public function retrieveHeader($url) {
-		$this->set_context();
-		$headers = @get_headers($url, 1);
-		return is_array($headers) ? array_change_key_case($headers) : array();
-	}
-
-	public function saveCache($file, $data) {
-		file_put_contents($file, $data);
-	}
-
-	public function readCache($file) {
-		return file_get_contents($file);
-	}
-
-	private function set_context() {
-		stream_context_set_default(
-			array(
-				'http' => array(
-					'method' => 'GET',
-					'timeout' => 10,
-					'header' => "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:20.0; Favicon; +https://github.com/ArthurHoaro/favicon) Gecko/20100101 Firefox/32.0\r\n",
-				)
-			)
-		);
+	    $this->set_context();
+		return @get_headers($url, TRUE);
 	}
 	}
-}
+	
+    public function saveCache($file, $data) {
+        file_put_contents($file, $data);
+    }
+    
+    public function readCache($file) {
+    	return file_get_contents($file);
+    }
+    
+    private function set_context() {
+        stream_context_set_default(
+            array(
+                'http' => array(
+                    'method' => 'GET',
+                    'timeout' => 10,
+                    'header' => "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:20.0; Favicon; +https://github.com/ArthurHoaro/favicon) Gecko/20100101 Firefox/32.0\r\n",
+                )
+            )
+        );
+    }
+}

+ 124 - 48
lib/Favicon/Favicon.php

@@ -4,6 +4,8 @@ namespace Favicon;
 
 
 class Favicon
 class Favicon
 {
 {
+    protected static $TYPE_CACHE_URL = 'url';
+    protected static $TYPE_CACHE_IMG = 'img';
     protected $url = '';
     protected $url = '';
     protected $cacheDir;
     protected $cacheDir;
     protected $cacheTimeout;
     protected $cacheTimeout;
@@ -16,18 +18,24 @@ class Favicon
         }
         }
         
         
         $this->cacheDir = __DIR__ . '/../../resources/cache';
         $this->cacheDir = __DIR__ . '/../../resources/cache';
+        $this->cacheTimeout = 604800;
         $this->dataAccess = new DataAccess();
         $this->dataAccess = new DataAccess();
     }
     }
 
 
+    /**
+     * Set cache settings:
+     *   - dir: cache directory
+     *   - timeout: in seconds
+     *
+     * @param array $args
+     */
     public function cache($args = array()) {
     public function cache($args = array()) {
         if (isset($args['dir'])) {
         if (isset($args['dir'])) {
             $this->cacheDir = $args['dir'];
             $this->cacheDir = $args['dir'];
         }
         }
 
 
         if (!empty($args['timeout'])) {
         if (!empty($args['timeout'])) {
-                $this->cacheTimeout = $args['timeout'];
-        } else {
-                $this->cacheTimeout = 0;
+            $this->cacheTimeout = $args['timeout'];
         }
         }
     }
     }
 
 
@@ -89,9 +97,6 @@ class Favicon
         $loop = TRUE;
         $loop = TRUE;
         while ($loop && $max_loop-- > 0) {
         while ($loop && $max_loop-- > 0) {
             $headers = $this->dataAccess->retrieveHeader($url);
             $headers = $this->dataAccess->retrieveHeader($url);
-            if (empty($headers)) {
-                return false;
-            }
             $exploded = explode(' ', $headers[0]);
             $exploded = explode(' ', $headers[0]);
             
             
             if( !isset($exploded[1]) ) { 
             if( !isset($exploded[1]) ) { 
@@ -102,7 +107,7 @@ class Favicon
             switch ($status) {
             switch ($status) {
                 case '301':
                 case '301':
                 case '302':
                 case '302':
-                    $url = isset($headers['location']) ? $headers['location'] : '';
+                    $url = $headers['Location'];
                     break;
                     break;
                 default:
                 default:
                     $loop = FALSE;
                     $loop = FALSE;
@@ -120,9 +125,16 @@ class Favicon
 
 
     /**
     /**
      * Find remote (or cached) favicon
      * Find remote (or cached) favicon
-     * @return favicon URL, false if nothing was found
-     **/
-    public function get($url = '')
+     *
+     * @param string $url  to look for a favicon
+     * @param int    $type type of retrieval (FaviconDLType):
+     *                       - HOTLINK_URL: returns remote URL
+     *                       - DL_FILE_PATH: returns file path of the favicon downloaded locally
+     *                       - RAW_IMAGE: returns the favicon image binary string
+     *
+     * @return string|bool favicon URL, false if nothing was found
+     */
+    public function get($url = '', $type = FaviconDLType::HOTLINK_URL)
     {
     {
         // URLs passed to this method take precedence.
         // URLs passed to this method take precedence.
         if (!empty($url)) {
         if (!empty($url)) {
@@ -130,25 +142,30 @@ class Favicon
         }
         }
 
 
         // Get the base URL without the path for clearer concatenations.
         // Get the base URL without the path for clearer concatenations.
-        $original = rtrim($this->baseUrl($this->url, true), '/');
-        $url = rtrim($this->endRedirect($this->baseUrl($this->url, false)), '/');
-
-        if(($favicon = $this->checkCache($url)) || ($favicon = $this->getFavicon($url))) {
-            $base = true;
-        }
-        elseif(($favicon = $this->checkCache($original)) || ($favicon = $this->getFavicon($original, false))) {
-            $base = false;    
+        $url = rtrim($this->baseUrl($this->url, true), '/');
+        $original = $url;
+        if (($favicon = $this->checkCache($original, self::$TYPE_CACHE_URL)) === false
+            && ! $favicon = $this->getFavicon($original, false)
+        ) {
+            $url = rtrim($this->endRedirect($this->baseUrl($this->url, false)), '/');
+            if (($favicon = $this->checkCache($url, self::$TYPE_CACHE_URL)) === false
+                && ! $favicon = $this->getFavicon($url)
+            ) {
+                $url = $original;
+            }
         }
         }
-        else
-            return false;
-            
-        // Save cache if necessary
-        $cache = $this->cacheDir . '/' . md5($base ? $url : $original);
-        if ($this->cacheTimeout && !file_exists($cache) || (is_writable($cache) && time() - filemtime($cache) > $this->cacheTimeout)) {
-            $this->dataAccess->saveCache($cache, $favicon);
+
+        $this->saveCache($url, $favicon, self::$TYPE_CACHE_URL);
+
+        switch ($type) {
+            case FaviconDLType::DL_FILE_PATH:
+                return $this->getImage($url, $favicon, false);
+            case FaviconDLType::RAW_IMAGE:
+                return $this->getImage($url, $favicon, true);
+            case FaviconDLType::HOTLINK_URL:
+            default:
+                return empty($favicon) ? false : $favicon;
         }
         }
-        
-        return $favicon;
     }
     }
     
     
     private function getFavicon($url, $checkDefault = true) {
     private function getFavicon($url, $checkDefault = true) {
@@ -179,13 +196,54 @@ class Favicon
         // Sometimes people lie, so check the status.
         // Sometimes people lie, so check the status.
         // And sometimes, it's not even an image. Sneaky bastards!
         // And sometimes, it's not even an image. Sneaky bastards!
         // If cacheDir isn't writable, that's not our problem
         // If cacheDir isn't writable, that's not our problem
-        if ($favicon && is_writable($this->cacheDir) && extension_loaded('fileinfo') && !$this->checkImageMType($favicon)) {
+        if ($favicon && is_writable($this->cacheDir) && !$this->checkImageMType($favicon)) {
             $favicon = false;
             $favicon = false;
         }
         }
 
 
         return $favicon;
         return $favicon;
     }
     }
-    
+
+    /**
+     * Find remote favicon and return it as an image
+     */
+    private function getImage($url, $faviconUrl = '', $image = false)
+    {
+        if (empty($faviconUrl)) {
+            return false;
+        }
+
+        $favicon = $this->checkCache($url, self::$TYPE_CACHE_IMG);
+        // Favicon not found in the cache
+        if( $favicon === false ) {
+            $favicon = $this->dataAccess->retrieveUrl($faviconUrl);
+            // Definitely not found
+            if (!$this->checkImageMTypeContent($favicon)) {
+                return false;
+            } else {
+                $this->saveCache($url, $favicon, self::$TYPE_CACHE_IMG);
+            }
+        }
+
+        if( $image ) {
+            return $favicon;
+        }
+        else
+            return self::$TYPE_CACHE_IMG . md5($url);
+    }
+
+    /**
+     * Display data as a PNG Favicon, then exit
+     * @param $data
+     */
+    private function displayFavicon($data) {
+        header('Content-Type: image/png');
+        header('Cache-Control: private, max-age=10800, pre-check=10800');
+        header('Pragma: private');
+        header('Expires: ' . date(DATE_RFC822,strtotime('7 day')));
+        echo $data;
+        exit;
+    }
+
     private function getInPage($url) {
     private function getInPage($url) {
         $html = $this->dataAccess->retrieveUrl("{$url}/");
         $html = $this->dataAccess->retrieveUrl("{$url}/");
         preg_match('!<head.*?>.*</head>!ims', $html, $match);
         preg_match('!<head.*?>.*</head>!ims', $html, $match);
@@ -197,7 +255,7 @@ class Favicon
         $head = $match[0];
         $head = $match[0];
         
         
         $dom = new \DOMDocument();
         $dom = new \DOMDocument();
-        // Use error supression, because the HTML might be too malformed.
+        // Use error suppression, because the HTML might be too malformed.
         if (@$dom->loadHTML($head)) {
         if (@$dom->loadHTML($head)) {
             $links = $dom->getElementsByTagName('link');
             $links = $dom->getElementsByTagName('link');
             foreach ($links as $link) {
             foreach ($links as $link) {
@@ -212,33 +270,51 @@ class Favicon
         }
         }
         return false;
         return false;
     }
     }
-    
-    private function checkCache($url) {
+
+    private function checkCache($url, $type) {
         if ($this->cacheTimeout) {
         if ($this->cacheTimeout) {
-            $cache = $this->cacheDir . '/' . md5($url);
-            if (file_exists($cache) && is_readable($cache) && (time() - filemtime($cache) < $this->cacheTimeout)) {
+            $cache = $this->cacheDir . '/'. $type . md5($url);
+            if (file_exists($cache) && is_readable($cache)
+                && ($this->cacheTimeout === -1 || time() - filemtime($cache) < $this->cacheTimeout)
+            ) {
                 return $this->dataAccess->readCache($cache);
                 return $this->dataAccess->readCache($cache);
             }
             }
-        } 
+        }
         return false;
         return false;
     }
     }
-    
+
+    /**
+     * Will save data in cacheDir if the directory writable and any previous cache is expired (cacheTimeout)
+     * @param $url
+     * @param $data
+     * @param $type
+     * @return string cache file path
+     */
+    private function saveCache($url, $data, $type) {
+        // Save cache if necessary
+        $cache = $this->cacheDir . '/'. $type . md5($url);
+        if ($this->cacheTimeout && !file_exists($cache)
+            || (is_writable($cache) && $this->cacheTimeout !== -1 && time() - filemtime($cache) > $this->cacheTimeout)
+        ) {
+            $this->dataAccess->saveCache($cache, $data);
+        }
+        return $cache;
+    }
+
     private function checkImageMType($url) {
     private function checkImageMType($url) {
-        $tmpFile = $this->cacheDir . '/tmp.ico';
         
         
         $fileContent = $this->dataAccess->retrieveUrl($url);
         $fileContent = $this->dataAccess->retrieveUrl($url);
-        $this->dataAccess->saveCache($tmpFile, $fileContent);
         
         
-        $isImage = true;
-        try {
-            $finfo = finfo_open(FILEINFO_MIME_TYPE);
-            $isImage = strpos(finfo_file($finfo, $tmpFile), 'image') !== false;
-            finfo_close($finfo);
-        } catch (Exception $e) {
-        }
+        return $this->checkImageMTypeContent($fileContent);
+    }
+
+    private function checkImageMTypeContent($content) {
+        if(empty($content)) return false;
+
+        $fInfo = finfo_open(FILEINFO_MIME_TYPE);
+        $isImage = strpos(finfo_buffer($fInfo, $content), 'image') !== false;
+        finfo_close($fInfo);
 
 
-        unlink($tmpFile);
-        
         return $isImage;
         return $isImage;
     }
     }
     
     
@@ -291,7 +367,7 @@ class Favicon
     }
     }
 
 
     /**
     /**
-     * @param DataAccess $dataAccess
+     * @param DataAccess|\PHPUnit_Framework_MockObject_MockObject $dataAccess
      */
      */
     public function setDataAccess($dataAccess)
     public function setDataAccess($dataAccess)
     {
     {

+ 23 - 0
lib/Favicon/FaviconDLType.php

@@ -0,0 +1,23 @@
+<?php
+
+
+namespace Favicon;
+
+
+interface FaviconDLType
+{
+    /**
+     * Retrieve remote favicon URL.
+     */
+    const HOTLINK_URL = 0;
+
+    /**
+     * Retrieve downloaded favicon path (requires cache).
+     */
+    const DL_FILE_PATH = 1;
+
+    /**
+     * Retrieve the image content as a binary string.
+     */
+    const RAW_IMAGE = 2;
+}