OpenSencillo  2015.009
Long live the simplicity of PHP
 All Data Structures Namespaces Files Functions Pages
cache_main.php
1 <?php
2 /*~ cache_main.php
3 .---------------------------------------------------------------------------.
4 | Software: SencilloCache |
5 | Version: 2015.003 |
6 | Contact: ph@mastery.sk |
7 | ------------------------------------------------------------------------- |
8 | Author: Bc. Peter Horváth |
9 | Copyright (c) 2014-2015, Bc. Peter Horváth. All Rights Reserved. |
10 | ------------------------------------------------------------------------- |
11 | License: Distributed under the General Public License (GPL) |
12 | http://www.gnu.org/licenses/gpl-3.0.html |
13 | This program is distributed in the hope that it will be useful - WITHOUT |
14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
15 | FITNESS FOR A PARTICULAR PURPOSE. |
16 '---------------------------------------------------------------------------'
17 ~*/
18 
19 /* Take a wild guess... */
20 function quickcache_debug($s) {
21  static $quickcache_debugline;
22 
23  if ($GLOBALS["QUICKCACHE_DEBUG"]) {
24  $quickcache_debugline++;
25  header("X-CacheDebug-$quickcache_debugline: $s");
26  }
27 }
28 
29 /* quickcache_key()
30  * Returns a hashvalue for the current. Maybe md5 is too heavy,
31  * so you can implement your own hashing-function.
32  */
33 function quickcache_key() {
34  if ($GLOBALS["QUICKCACHE_CLEANKEYS"]) {
35  $key = eregi_replace("[^A-Z,0-9,=]", "_", quickcache_scriptkey());
36  $key .= ".".eregi_replace("[^A-Z,0-9,=]", "_", quickcache_varkey());
37  if (strlen($key) > 255) {
38  // Too large, fallback to md5!
39  $key = md5(quickcache_scriptkey().quickcache_varkey());
40  }
41  } else {
42  $key = md5(quickcache_scriptkey().quickcache_varkey());
43  }
44  quickcache_debug("Cachekey is set to $key");
45  return $key;
46 }
47 
48 /* quickcache_varkey()
49  * Returns a serialized version of POST & GET vars
50  * If you want to take cookies into account in the varkey too,
51  * add them inhere.
52  */
53 function quickcache_varkey() {
54  $varkey = "";
55  if ($GLOBALS["QUICKCACHE_POST"]) {
56  $varkey = "POST=".serialize($_POST);
57  }
58  $varkey .= "GET=".serialize($_GET);
59  quickcache_debug("Cache varkey is set to $varkey");
60  return $varkey;
61 }
62 
63 /* quickcache_scriptkey()
64  * Returns the script-identifier for the request
65  */
66 function quickcache_scriptkey() {
67  // These should be available, unless running commandline
68  if ($GLOBALS["QUICKCACHE_IGNORE_DOMAIN"]) {
69  $name=$_SERVER["PHP_SELF"];
70  } else {
71  $name=$_SERVER["SCRIPT_URI"];
72  }
73 
74  // Commandline mode will also fail this one, I'm afraid, as there is no
75  // way to determine the scriptname
76  if ($name=="") {
77  $name="http://".$_SERVER["SERVER_NAME"].$_SERVER["SCRIPT_NAME"];
78  }
79 
80  quickcache_debug("Cache scriptkey is set to $name");
81  return $name;
82 }
83 
84 
85 /* quickcache_check() */
86 function quickcache_check() {
87  if (!$GLOBALS["QUICKCACHE_ON"]) {
88  quickcache_debug("Cache has been disabled!");
89  return false;
90  }
91 
92  // We need to set this global, as ob_start only calls the given method
93  // with no parameters.
94  $GLOBALS["quickcache_key"] = quickcache_key();
95 
96  // Can we read the cached data for this key ?
97  if (quickcache_restore()) {
98  quickcache_debug("Cachedata for ".$GLOBALS["quickcache_key"]." found, data restored");
99  return true;
100  } else {
101  // No cache data (yet) or unable to read
102  quickcache_debug("No (valid) cachedata for ".$GLOBALS["quickcache_key"]);
103  return false;
104  }
105 }
106 
107 /* quickcache_encoding()
108  * Are we capable of receiving gzipped data ?
109  * Returns the encoding that is accepted. Maybe additional check for Mac ?
110  */
111 function quickcache_encoding() {
112  if (headers_sent() || connection_aborted()) {
113  return false;
114  }
115  if (strpos($_SERVER["HTTP_ACCEPT_ENCODING"],'x-gzip') !== false) {
116  return "x-gzip";
117  }
118  if (strpos($_SERVER["HTTP_ACCEPT_ENCODING"],'gzip') !== false) {
119  return "gzip";
120  }
121  return false;
122 }
123 
124 /* quickcache_init()
125  * Checks some global variables and might decide to disable caching
126  */
127 function quickcache_init() {
128  // Override default QUICKCACHE_TIME ?
129  if (isset($GLOBALS["cachetimeout"])) {
130  $GLOBALS["QUICKCACHE_TIME"]=$GLOBALS["cachetimeout"];
131  }
132 
133  // Force gzip off if gzcompress does not exist
134  if (!function_exists('gzcompress')) {
135  $GLOBALS["QUICKCACHE_USE_GZIP"] = 0;
136  }
137 
138  // Force cache off when POST occured when you don't want it cached
139  if (!$GLOBALS["QUICKCACHE_POST"] && (count($_POST) > 0)) {
140  $GLOBALS["QUICKCACHE_ON"] = 0;
141  $GLOBALS["QUICKCACHE_TIME"] = -1;
142  }
143 
144  // A cachetimeout of -1 disables writing, only ETag and content encoding
145  if ($GLOBALS["QUICKCACHE_TIME"] == -1) {
146  $GLOBALS["QUICKCACHE_ON"] = 0;
147  }
148 
149  // Output header to recognize version
150  header("X-Cache: QuickCache v".$GLOBALS["QUICKCACHE_VERSION"].
151  " - ".$GLOBALS["QUICKCACHE_TYPE"]);
152 }
153 
154 /* quickcache_gc()
155  * Checks if garbagecollection is needed.
156  */
157 function quickcache_gc() {
158  // Should we garbage collect ?
159  if ($GLOBALS["QUICKCACHE_GC"]>0) {
160  mt_srand(time(NULL));
161  $precision=100000;
162  // Garbagecollection probability
163  if (((mt_rand()%$precision)/$precision) <=
164  ($GLOBALS["QUICKCACHE_GC"]/100))
165  {
166  quickcache_debug("GarbageCollection hit!");
167  quickcache_do_gc();
168  }
169  }
170 }
171 
172 /* quickcache_start()
173  * Sets the handler for callback
174  */
175 function quickcache_start() {
176  // Initialize cache
177  quickcache_init();
178 
179  // Handle type-specific additional code if required
180  quickcache_do_start();
181 
182  // Check cache
183  if (quickcache_check()) {
184  // Cache is valid and restored: flush it!
185  print quickcache_flush($GLOBALS["quickcachedata_gzdata"],
186  $GLOBALS["quickcachedata_datasize"],
187  $GLOBALS["quickcachedata_datacrc"]);
188  // Handle type-specific additional code if required
189  quickcache_do_end();
190  exit;
191  } else {
192  // if we came here, cache is invalid: go generate page
193  // and wait for quickcache_end() which will be called automagically
194 
195  // Check garbagecollection
196  quickcache_gc();
197 
198  // Go generate page and wait for callback
199  ob_start("quickcache_end");
200  ob_implicit_flush(0);
201  }
202 }
203 
204 /* quickcache_end()
205  * This one is called by the callback-funtion of the ob_start.
206  */
207 function quickcache_end($contents) {
208  quickcache_debug("Callback happened");
209 
210  $datasize = strlen($contents);
211  $datacrc = crc32($contents);
212 
213  if ($GLOBALS["QUICKCACHE_USE_GZIP"]) {
214  $gzdata = gzcompress($contents, $GLOBALS["QUICKCACHE_GZIP_LEVEL"]);
215  } else {
216  $gzdata = $contents;
217  }
218 
219  // If the connection was aborted, do not write the cache.
220  // We don't know if the data we have is valid, as the user
221  // has interupted the generation of the page.
222  // Also check if quickcache is not disabled
223  if ((!connection_aborted()) &&
224  $GLOBALS["QUICKCACHE_ON"] &&
225  ($GLOBALS["QUICKCACHE_TIME"] >= 0))
226  {
227  quickcache_debug("Writing cached data to storage");
228  // write the cache with the current data
229  quickcache_write($gzdata, $datasize, $datacrc);
230  }
231 
232  // Handle type-specific additional code if required
233  quickcache_do_end();
234 
235  // Return flushed data
236  return quickcache_flush($gzdata, $datasize, $datacrc);
237 }
238 
239 /* quickcache_flush()
240  * Responsible for final flushing everything.
241  * Sets ETag-headers and returns "Not modified" when possible
242  * When ETag doesn't match (or is invalid), it is tried to send
243  * the gzipped data. If that is also not possible, we sadly have to
244  * uncompress (assuming QUICKCACHE_USE_GZIP is on)
245  */
246 function quickcache_flush($gzdata, $datasize, $datacrc) {
247  // First check if we can send last-modified
248  $myETag = "\"qcd-$datacrc.$datasize\"";
249  header("ETag: $myETag");
250  $foundETag = isset($_SERVER["HTTP_IF_NONE_MATCH"]) ? stripslashes($_SERVER["HTTP_IF_NONE_MATCH"]) : "";
251  $ret = NULL;
252 
253  if (strstr($foundETag, $myETag)) {
254  // Not modified!
255  if(stristr($_SERVER["SERVER_SOFTWARE"], "microsoft")) {
256  // IIS has already sent a HTTP/1.1 200 by this stage for
257  // some strange reason
258  header("Status: 304 Not Modified");
259  } else {
260  if ( $QUICKCACHE_ISCGI ) {
261  header('Status: 304 Not Modified');
262  } else {
263  header('HTTP/1.0 304');
264  }
265  }
266  } else {
267  // Are we gzipping ?
268  if ($GLOBALS["QUICKCACHE_USE_GZIP"]) {
269  $ENCODING = quickcache_encoding();
270  if ($ENCODING) {
271  // compressed output: set header. Need to modify, as
272  // in some versions, the gzipped content is not what
273  // your browser expects.
274  header("Content-Encoding: $ENCODING");
275  $ret = "\x1f\x8b\x08\x00\x00\x00\x00\x00";
276  $ret .= substr($gzdata, 0, strlen($gzdata) - 4);
277  $ret .= pack('V',$datacrc);
278  $ret .= pack('V',$datasize);
279  } else {
280  // Darn, we need to uncompress :(
281  $ret = gzuncompress($gzdata);
282  }
283  } else {
284  // So content isn't gzipped either
285  $ret=$gzdata;
286  }
287  }
288  return $ret;
289 }
290 
291 ?>