Favicon.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. namespace PicoFeed\Reader;
  3. use DOMXPath;
  4. use PicoFeed\Base;
  5. use PicoFeed\Client\Client;
  6. use PicoFeed\Client\ClientException;
  7. use PicoFeed\Client\Url;
  8. use PicoFeed\Logging\Logger;
  9. use PicoFeed\Parser\XmlParser;
  10. /**
  11. * Favicon class.
  12. *
  13. * https://en.wikipedia.org/wiki/Favicon
  14. *
  15. * @author Frederic Guillot
  16. */
  17. class Favicon extends Base
  18. {
  19. /**
  20. * Valid types for favicon (supported by browsers).
  21. *
  22. * @var array
  23. */
  24. private $types = array(
  25. 'image/png',
  26. 'image/gif',
  27. 'image/x-icon',
  28. 'image/jpeg',
  29. 'image/jpg',
  30. 'image/svg+xml',
  31. );
  32. /**
  33. * Icon binary content.
  34. *
  35. * @var string
  36. */
  37. private $content = '';
  38. /**
  39. * Icon content type.
  40. *
  41. * @var string
  42. */
  43. private $content_type = '';
  44. /**
  45. * Get the icon file content (available only after the download).
  46. *
  47. * @return string
  48. */
  49. public function getContent()
  50. {
  51. return $this->content;
  52. }
  53. /**
  54. * Get the icon file type (available only after the download).
  55. *
  56. * @return string
  57. */
  58. public function getType()
  59. {
  60. foreach ($this->types as $type) {
  61. if (strpos($this->content_type, $type) === 0) {
  62. return $type;
  63. }
  64. }
  65. return 'image/x-icon';
  66. }
  67. /**
  68. * Get data URI (http://en.wikipedia.org/wiki/Data_URI_scheme).
  69. *
  70. * @return string
  71. */
  72. public function getDataUri()
  73. {
  74. if (empty($this->content)) {
  75. return '';
  76. }
  77. return sprintf(
  78. 'data:%s;base64,%s',
  79. $this->getType(),
  80. base64_encode($this->content)
  81. );
  82. }
  83. /**
  84. * Download and check if a resource exists.
  85. *
  86. * @param string $url URL
  87. * @return \PicoFeed\Client\Client Client instance
  88. */
  89. public function download($url)
  90. {
  91. $client = Client::getInstance();
  92. $client->setConfig($this->config);
  93. Logger::setMessage(get_called_class().' Download => '.$url);
  94. try {
  95. $client->execute($url);
  96. } catch (ClientException $e) {
  97. Logger::setMessage(get_called_class().' Download Failed => '.$e->getMessage());
  98. }
  99. return $client;
  100. }
  101. /**
  102. * Check if a remote file exists.
  103. *
  104. * @param string $url URL
  105. * @return bool
  106. */
  107. public function exists($url)
  108. {
  109. return $this->download($url)->getContent() !== '';
  110. }
  111. /**
  112. * Get the icon link for a website.
  113. *
  114. * @param string $website_link URL
  115. * @param string $favicon_link optional URL
  116. * @return string
  117. */
  118. public function find($website_link, $favicon_link = '')
  119. {
  120. $website = new Url($website_link);
  121. if ($favicon_link !== '') {
  122. $icons = array($favicon_link);
  123. } else {
  124. $icons = $this->extract($this->download($website->getBaseUrl('/'))->getContent());
  125. $icons[] = $website->getBaseUrl('/favicon.ico');
  126. }
  127. foreach ($icons as $icon_link) {
  128. $icon_link = Url::resolve($icon_link, $website);
  129. $resource = $this->download($icon_link);
  130. $this->content = $resource->getContent();
  131. $this->content_type = $resource->getContentType();
  132. if ($this->content !== '') {
  133. return $icon_link;
  134. } elseif ($favicon_link !== '') {
  135. return $this->find($website_link);
  136. }
  137. }
  138. return '';
  139. }
  140. /**
  141. * Extract the icon links from the HTML.
  142. *
  143. * @param string $html HTML
  144. * @return array
  145. */
  146. public function extract($html)
  147. {
  148. $icons = array();
  149. if (empty($html)) {
  150. return $icons;
  151. }
  152. $dom = XmlParser::getHtmlDocument($html);
  153. $xpath = new DOMXpath($dom);
  154. $elements = $xpath->query('//link[@rel="icon" or @rel="shortcut icon" or @rel="Shortcut Icon" or @rel="icon shortcut"]');
  155. for ($i = 0; $i < $elements->length; ++$i) {
  156. $icons[] = $elements->item($i)->getAttribute('href');
  157. }
  158. return $icons;
  159. }
  160. }