PageRenderTime 31ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/popoon/sitemap/outputcache.php

http://github.com/chregu/planet-php
PHP | 239 lines | 136 code | 19 blank | 84 comment | 26 complexity | 952712cf6fc0edc2eb3a1737282a0c77 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | popoon |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 2001,2002,2003,2004 Bitflux GmbH |
  6. // +----------------------------------------------------------------------+
  7. // | Licensed under the Apache License, Version 2.0 (the "License"); |
  8. // | you may not use this file except in compliance with the License. |
  9. // | You may obtain a copy of the License at |
  10. // | http://www.apache.org/licenses/LICENSE-2.0 |
  11. // | Unless required by applicable law or agreed to in writing, software |
  12. // | distributed under the License is distributed on an "AS IS" BASIS, |
  13. // | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
  14. // | implied. See the License for the specific language governing |
  15. // | permissions and limitations under the License. |
  16. // +----------------------------------------------------------------------+
  17. // | Author: Christian Stocker <chregu@bitflux.ch> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: outputcache.php 3605 2005-02-06 12:46:28Z bitflux $
  21. /**
  22. * Class for doing the sitemap parsing stuff
  23. *
  24. * @author Christian Stocker <chregu@bitflux.ch>
  25. * @version $Id: outputcache.php 3605 2005-02-06 12:46:28Z bitflux $
  26. * @package popoon
  27. */
  28. /**
  29. * Some words about 304 caching:
  30. *
  31. * It has now quite sophisticated 304 detecting mechanism built in
  32. *
  33. * C-Etag = Cached Etag (md5($content))
  34. * B-Etag = Browser sent etag
  35. * C-LM = Cached Last Modified
  36. * B-LM = Browser Last Modified
  37. * M-LM = Last Modified in oc.meta
  38. *
  39. * oc.meta = outputcache.meta. Dies directory contains metadata about LM and Etag of
  40. * the last cached url.
  41. * This allows to delete the cache directory (outputcache) completely, but we still now
  42. * the Etag (md5 of content) and LM of the last generated page.
  43. *
  44. * If url exits in cache and C-ETag = B-Etag and C-LM <= B-LM
  45. * send 304
  46. *
  47. * If url does not exist in cache and not in outputcache.meta
  48. * generate content
  49. * generate C-Etag = md5($content)
  50. * if C-Etag = B-Etag
  51. * send 304
  52. * save content
  53. * save oc.meta
  54. * print content
  55. * // this method saves us sending the whole content, when the browser already cached this content
  56. * // it does not make it faster, since we have to generate the content anyway, we just save bandwidth
  57. *
  58. * If url does not exist in cache but in outputcache.meta
  59. * generate content
  60. * generate C-Etag = md5($content)
  61. * if C-Etag = M-Etag
  62. * set LM to M-LM
  63. * if C-Etag = B-Etag
  64. * send 304
  65. * if C-LM <= B-LM
  66. * send 304
  67. * save content
  68. * print content
  69. *
  70. * //this method saves us also sending the whole content as above, it additionally keeps the LM if the
  71. * // md5 did not change. this is more cosmetics than really saving resources as the above does save
  72. * // the resources anyway (except the browser doesn't understand Etags..)
  73. *
  74. * Honestly said, the later 2 parts in the 304 handler is maybe overkill... It's just cosmetics more or
  75. * less and will not save much resources. But with newsreaders, which read the rss file every half an
  76. * hour or so, it could sum up and save some bandwidth.
  77. *
  78. * Maybe I will make the later part with the extra oc.meta files optional... But for Google it's certainly
  79. * nice to know, when the page was really last modified and not, when the cache was last created ;)
  80. */
  81. class popoon_sitemap_outputcache {
  82. public $compression = true;
  83. function __construct(popoon_classes_config $options = NULL) {
  84. require_once('Cache/Output.php');
  85. $this->options = $options;
  86. $options->cacheParams['max_userdata_linelength'] = 0;
  87. $this->cache = new Cache_Output($options->cacheContainer, $options->cacheParams );
  88. if ($this->compression) {
  89. $this->compression = $this->getSupportedCompression();
  90. }
  91. }
  92. function start($uri) {
  93. $idParams = $_GET;
  94. if (isset($idParams['SID']))
  95. {
  96. unset($idParams['SID']);
  97. }
  98. $this->id = str_replace("/","_",$uri).$this->cache->generateID($idParams).$this->compression;
  99. $this->cacheGroup = 'outputcache';
  100. if ( $content = $this->cache->start($this->id,$this->cacheGroup) ) {
  101. if ($this->options->outputCacheSave === true ) {
  102. $header = unserialize($this->cache->getUserdata($this->id,$this->cacheGroup));
  103. $etag = $header['ETag'];
  104. if (isset($header['_file-location'])) {
  105. if (isset($header['_file-location'])) {
  106. if (strtotime($header['Last-Modified']) < filemtime($header['_file-location'])) {
  107. header("X-Popoon-Cache-Status: File is newer than cache");
  108. return false;
  109. }
  110. }
  111. }
  112. foreach ($header as $key => $value) {
  113. if (substr($key,0,1) != "_") {
  114. header("$key: $value");
  115. }
  116. }
  117. if ($this->check304($etag, $header['Last-Modified'])) {
  118. header('HTTP/1.1 304 Not Modified' );
  119. header("X-Popoon-Cache-Status: 304");
  120. die();
  121. }
  122. header("X-Popoon-Cache-Status: true");
  123. print $content;
  124. die();
  125. }
  126. }
  127. }
  128. function check304($etag, $lastModified) {
  129. if (isset($_SERVER["HTTP_IF_NONE_MATCH"]) && $_SERVER["HTTP_IF_NONE_MATCH"] != 'None') {
  130. if (trim($etag,"\"' \t") == trim($_SERVER["HTTP_IF_NONE_MATCH"],"\"' \t")) {
  131. return true;
  132. }
  133. }
  134. else if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) )
  135. {
  136. if (strtotime($lastModified) <= strtotime($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
  137. return true;
  138. }
  139. }
  140. return false;
  141. }
  142. function end(&$sitemap, $expire = 3600) {
  143. $content = ob_get_contents();
  144. ob_end_clean();
  145. $etag = md5($content);
  146. $sitemap->setHeader("ETag", '"'.$etag.'"');
  147. if ($this->options->outputCacheSave !== 304) {
  148. $metadata = $this->cache->get($this->id.'.meta','outputcache.meta');
  149. $lastModified = null;
  150. if ($metadata) {
  151. if (isset($metadata['Etag'] )&& $metadata['Etag'] == $etag) {
  152. $sitemap->setHeaderIfNotExists("Last-Modified",$metadata['Last-Modified']);
  153. $lastModified = true;
  154. }
  155. }
  156. if (!$lastModified) {
  157. $metadata['Etag'] = $etag;
  158. $sitemap->setHeaderIfNotExists("Last-Modified",gmdate('D, d M Y H:i:s T'));
  159. $metadata['Last-Modified'] = $sitemap->header['Last-Modified'];
  160. $this->cache->container->save($this->id.'.meta', $metadata, 0 ,"outputcache.meta","" );
  161. }
  162. } else {
  163. if (isset( $sitemap->header['Last-Modified'])) {
  164. $sitemap->setHeaderIfNotExists("Last-Modified", $sitemap->header['Last-Modified']);
  165. }
  166. }
  167. // we don't want phps cache-control stuff, when we do caching
  168. // there is a "problem" if session_start is used, then PHP adds no-cache http headers
  169. // we do not want that in outputCaching.
  170. // Drawback: OutputCAching with sites relying on different sessions-values do not work
  171. header("Pragma: ");
  172. header("Cache-Control: ");
  173. header("Expires: ");
  174. if ($this->compression) {
  175. $sitemap->setHeader("Content-Encoding",$this->compression);
  176. $sitemap->setHeader("Vary","Accept-Encoding");
  177. }
  178. foreach ($sitemap->header as $key => $value) {
  179. if (substr($key,0,1) != "_") {
  180. header("$key: $value");
  181. }
  182. }
  183. if (isset($sitemap->header['Last-Modified']) && $this->check304($etag, $sitemap->header['Last-Modified'])) {
  184. header( 'HTTP/1.1 304 Not Modified' );
  185. if ($this->options->outputCacheSave !== 304) {
  186. $this->cache->extSave($this->id, $this->compressContent($content), serialize($sitemap->header), $expire ,$this->cacheGroup);
  187. }
  188. die();
  189. } else {
  190. $content = $this->compressContent($content);
  191. print $content;
  192. if ($this->options->outputCacheSave !== 304) {
  193. $this->cache->extSave($this->id, $content, serialize($sitemap->header), $expire ,$this->cacheGroup);
  194. }
  195. }
  196. }
  197. function compressContent($content) {
  198. if ($this->compression) {
  199. $len = strlen($content);
  200. $crc = crc32($content);
  201. $content = gzcompress($content, 9);
  202. return "\x1f\x8b\x08\x00\x00\x00\x00\x00" . substr($content, 0, strlen($content) - 4) . pack('V', $crc) . pack('V', $len);
  203. }
  204. return $content;
  205. }
  206. function getSupportedCompression() {
  207. // check what the client accepts
  208. if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  209. if (false !== strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) {
  210. if (false !== strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip')) {
  211. return 'x-gzip';
  212. }
  213. return 'gzip';
  214. }
  215. }
  216. // no compression
  217. return '';
  218. }
  219. }