├── LICENSE ├── README.md ├── lib └── Cache │ ├── Lite.php │ └── Lite │ ├── File.php │ ├── Function.php │ ├── NestedOutput.php │ └── Output.php ├── proxy-simple.php └── proxy.php /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-reverse-proxy 2 | 3 | This is a tiny 'reverse proxy' PHP script with a file-based cache. 4 | 5 | 6 | ## Motivation 7 | 8 | While reverse proxy can be implemented as a server (nginx, squid, etc) or a server module (apache mod_proxy), installing or enabling them requires having root access to the host that is not available for end users of web-hosting services in general. 9 | 10 | For such end users, this implements a reverse proxy as a PHP script. 11 | 12 | 13 | ## License 14 | 15 | This script is licensed under the Apache License 2.0. Please note that `Cache_Lite` in `lib/` is not by the author. It is a part of PEAR. 16 | 17 | 18 | ## Installation 19 | 20 | Modify the line of `$url = "http://www.example.com/";` to your target host, and then upload `proxy.php` and `lib/` directory to your web site. If you do not need caching mechanism, use `proxy-simple.php` instead. `proxy-simple.php` does not require files in `lib/`. 21 | 22 | ```php 23 | /* Hard-coded target host */ 24 | $url = "http://www.example.com/"; 25 | ``` 26 | 27 | ## Usage 28 | 29 | Suppose your `proxy.php` is accessible as `http://www.aaa.com/bbb/proxy.php`, and you specified `http://www.ccc.com/` as `$url` in the script. To retrive `http://www.ccc.com/ddd.html` by the script, visit `http://www.aaa.com/bbb/proxy.php?ddd.html`. Here `www.aaa.com` and `www.ccc.com` serve as a frontend and backend servers respectively; meaning that `www.ccc.com` is not necessarily reachable from you. 30 | 31 | By using Apache `mod_rewrite`, you can map a directory of the backend server to the frontend server as follows. 32 | ```/.htaccess 33 | RewriteRule ^eee/?(.*)$ /bbb/proxy.php?/eee/$1 [L] 34 | ``` 35 | 36 | Here all accesses to `http://www.aaa.com/eee/*` are redirected to `http://www.ccc.com/eee/*`. Besides, the flag `[QSA]` will not work as expected. See the limitation below. 37 | 38 | 39 | ## Customization 40 | 41 | ### Proxy 42 | 43 | If your frontend server requires an HTTP proxy to access the backend server, you can uncomment the following part in `proxy.php` to modify the default parameter for `stream_get_contents()`. 44 | 45 | ```php:proxy.php 46 | /* # 0. Configure the proxy */ 47 | /* 48 | stream_context_set_default( 49 | array("http" => array( 50 | "proxy" => "tcp://your.proxy.com:8080", 51 | "request_fulluri" => TRUE, 52 | ), 53 | ) 54 | ); 55 | */ 56 | ``` 57 | 58 | 59 | ### `dst` parameter 60 | 61 | This reverse proxy is designed to allow users to access local servers by definition, and the script is implemented to use the hard-coded target host (i.e., `$url` above) for security; meaning that the script accesses the backend server which is specified explictly by yourself. 62 | 63 | This behavior can be changed by commenting out the line of `$url = ...`. The script parses `dst=` in the query string to know the backend server. 64 | ```/.htaccess 65 | RewriteRule ^eee/?(.*)$ /bbb/proxy.php?dst=http://www.ccc.com/eee/$1 [L] 66 | ``` 67 | 68 | This can be useful if you want to change the target host dynamically (by `mod_rewrite` for example), but can be a point of Server-Side Request Forgery (SSRF) flaws if the script is called directly with user-specified `dst` parameter. That is, if an attacker knows where your `proxy.php` is installed, they can directly access `proxy.php?dst=...` with specifying any target accessible from your server. Use this feature if and only if you know what you are doing. 69 | 70 | 71 | ## Limitations 72 | 73 | To keep the script as simple as possible, all the query strings are all proxied, as they are to the destination. 74 | 75 | So the call `http://www.aaa.com/bbb/proxy.php?bbb=444&dst=http://www.ccc.com/ddd.html&aaa=333` becames `http://www.ccc.com/ddd.html?bbb=444&aaa=333` 76 | -------------------------------------------------------------------------------- /lib/Cache/Lite.php: -------------------------------------------------------------------------------- 1 | 15 | * 16 | * Nota : A chinese documentation (thanks to RainX ) is 17 | * available at : 18 | * http://rainx.phpmore.com/manual/cache_lite.html 19 | * 20 | * @package Cache_Lite 21 | * @category Caching 22 | * @author Fabien MARTY 23 | * @author Markus Tacker 24 | */ 25 | 26 | define('CACHE_LITE_ERROR_RETURN', 1); 27 | define('CACHE_LITE_ERROR_DIE', 8); 28 | 29 | class Cache_Lite 30 | { 31 | 32 | // --- Private properties --- 33 | 34 | /** 35 | * Directory where to put the cache files 36 | * (make sure to add a trailing slash) 37 | * 38 | * @var string $_cacheDir 39 | */ 40 | var $_cacheDir = '/tmp/'; 41 | 42 | /** 43 | * Enable / disable caching 44 | * 45 | * (can be very usefull for the debug of cached scripts) 46 | * 47 | * @var boolean $_caching 48 | */ 49 | var $_caching = true; 50 | 51 | /** 52 | * Cache lifetime (in seconds) 53 | * 54 | * If null, the cache is valid forever. 55 | * 56 | * @var int $_lifeTime 57 | */ 58 | var $_lifeTime = 3600; 59 | 60 | /** 61 | * Enable / disable fileLocking 62 | * 63 | * (can avoid cache corruption under bad circumstances) 64 | * 65 | * @var boolean $_fileLocking 66 | */ 67 | var $_fileLocking = true; 68 | 69 | /** 70 | * Timestamp of the last valid cache 71 | * 72 | * @var int $_refreshTime 73 | */ 74 | var $_refreshTime; 75 | 76 | /** 77 | * File name (with path) 78 | * 79 | * @var string $_file 80 | */ 81 | var $_file; 82 | 83 | /** 84 | * File name (without path) 85 | * 86 | * @var string $_fileName 87 | */ 88 | var $_fileName; 89 | 90 | /** 91 | * Enable / disable write control (the cache is read just after writing to detect corrupt entries) 92 | * 93 | * Enable write control will lightly slow the cache writing but not the cache reading 94 | * Write control can detect some corrupt cache files but maybe it's not a perfect control 95 | * 96 | * @var boolean $_writeControl 97 | */ 98 | var $_writeControl = true; 99 | 100 | /** 101 | * Enable / disable read control 102 | * 103 | * If enabled, a control key is embeded in cache file and this key is compared with the one 104 | * calculated after the reading. 105 | * 106 | * @var boolean $_writeControl 107 | */ 108 | var $_readControl = true; 109 | 110 | /** 111 | * Type of read control (only if read control is enabled) 112 | * 113 | * Available values are : 114 | * 'md5' for a md5 hash control (best but slowest) 115 | * 'crc32' for a crc32 hash control (lightly less safe but faster, better choice) 116 | * 'strlen' for a length only test (fastest) 117 | * 118 | * @var boolean $_readControlType 119 | */ 120 | var $_readControlType = 'crc32'; 121 | 122 | /** 123 | * Pear error mode (when raiseError is called) 124 | * 125 | * (see PEAR doc) 126 | * 127 | * @see setToDebug() 128 | * @var int $_pearErrorMode 129 | */ 130 | var $_pearErrorMode = CACHE_LITE_ERROR_RETURN; 131 | 132 | /** 133 | * Current cache id 134 | * 135 | * @var string $_id 136 | */ 137 | var $_id; 138 | 139 | /** 140 | * Current cache group 141 | * 142 | * @var string $_group 143 | */ 144 | var $_group; 145 | 146 | /** 147 | * Enable / Disable "Memory Caching" 148 | * 149 | * NB : There is no lifetime for memory caching ! 150 | * 151 | * @var boolean $_memoryCaching 152 | */ 153 | var $_memoryCaching = false; 154 | 155 | /** 156 | * Enable / Disable "Only Memory Caching" 157 | * (be carefull, memory caching is "beta quality") 158 | * 159 | * @var boolean $_onlyMemoryCaching 160 | */ 161 | var $_onlyMemoryCaching = false; 162 | 163 | /** 164 | * Memory caching array 165 | * 166 | * @var array $_memoryCachingArray 167 | */ 168 | var $_memoryCachingArray = array(); 169 | 170 | /** 171 | * Memory caching counter 172 | * 173 | * @var int $memoryCachingCounter 174 | */ 175 | var $_memoryCachingCounter = 0; 176 | 177 | /** 178 | * Memory caching limit 179 | * 180 | * @var int $memoryCachingLimit 181 | */ 182 | var $_memoryCachingLimit = 1000; 183 | 184 | /** 185 | * File Name protection 186 | * 187 | * if set to true, you can use any cache id or group name 188 | * if set to false, it can be faster but cache ids and group names 189 | * will be used directly in cache file names so be carefull with 190 | * special characters... 191 | * 192 | * @var boolean $fileNameProtection 193 | */ 194 | var $_fileNameProtection = true; 195 | 196 | /** 197 | * Enable / disable automatic serialization 198 | * 199 | * it can be used to save directly datas which aren't strings 200 | * (but it's slower) 201 | * 202 | * @var boolean $_serialize 203 | */ 204 | var $_automaticSerialization = false; 205 | 206 | /** 207 | * Disable / Tune the automatic cleaning process 208 | * 209 | * The automatic cleaning process destroy too old (for the given life time) 210 | * cache files when a new cache file is written. 211 | * 0 => no automatic cache cleaning 212 | * 1 => systematic cache cleaning 213 | * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write 214 | * 215 | * @var int $_automaticCleaning 216 | */ 217 | var $_automaticCleaningFactor = 0; 218 | 219 | /** 220 | * Nested directory level 221 | * 222 | * Set the hashed directory structure level. 0 means "no hashed directory 223 | * structure", 1 means "one level of directory", 2 means "two levels"... 224 | * This option can speed up Cache_Lite only when you have many thousands of 225 | * cache file. Only specific benchs can help you to choose the perfect value 226 | * for you. Maybe, 1 or 2 is a good start. 227 | * 228 | * @var int $_hashedDirectoryLevel 229 | */ 230 | var $_hashedDirectoryLevel = 0; 231 | 232 | /** 233 | * Umask for hashed directory structure 234 | * 235 | * @var int $_hashedDirectoryUmask 236 | */ 237 | var $_hashedDirectoryUmask = 0700; 238 | 239 | /** 240 | * API break for error handling in CACHE_LITE_ERROR_RETURN mode 241 | * 242 | * In CACHE_LITE_ERROR_RETURN mode, error handling was not good because 243 | * for example save() method always returned a boolean (a PEAR_Error object 244 | * would be better in CACHE_LITE_ERROR_RETURN mode). To correct this without 245 | * breaking the API, this option (false by default) can change this handling. 246 | * 247 | * @var boolean 248 | */ 249 | var $_errorHandlingAPIBreak = false; 250 | 251 | var $_hashedDirectoryGroup = NULL; 252 | 253 | var $_cacheFileMode = NULL; 254 | 255 | var $_cacheFileGroup = NULL; 256 | 257 | // --- Public methods --- 258 | 259 | /** 260 | * Constructor 261 | * 262 | * $options is an assoc. Available options are : 263 | * $options = array( 264 | * 'cacheDir' => directory where to put the cache files (string), 265 | * 'caching' => enable / disable caching (boolean), 266 | * 'lifeTime' => cache lifetime in seconds (int), 267 | * 'fileLocking' => enable / disable fileLocking (boolean), 268 | * 'writeControl' => enable / disable write control (boolean), 269 | * 'readControl' => enable / disable read control (boolean), 270 | * 'readControlType' => type of read control 'crc32', 'md5', 'strlen' (string), 271 | * 'pearErrorMode' => pear error mode (when raiseError is called) (cf PEAR doc) (int), 272 | * 'memoryCaching' => enable / disable memory caching (boolean), 273 | * 'onlyMemoryCaching' => enable / disable only memory caching (boolean), 274 | * 'memoryCachingLimit' => max nbr of records to store into memory caching (int), 275 | * 'fileNameProtection' => enable / disable automatic file name protection (boolean), 276 | * 'automaticSerialization' => enable / disable automatic serialization (boolean), 277 | * 'automaticCleaningFactor' => distable / tune automatic cleaning process (int), 278 | * 'hashedDirectoryLevel' => level of the hashed directory system (int), 279 | * 'hashedDirectoryUmask' => umask for hashed directory structure (int), 280 | * 'errorHandlingAPIBreak' => API break for better error handling ? (boolean) 281 | * 'hashedDirectoryGroup' => group of hashed directory structure (int | string) (see function chgrp) 282 | * 'cacheFileMode' => filesystem mode of newly created cache files (int) 283 | * 'cacheFileGroup' => group of newly created cache files (int | string) (see function chgrp) 284 | * ); 285 | * 286 | * If sys_get_temp_dir() is available and the 287 | * 'cacheDir' option is not provided in the 288 | * constructor options array its output is used 289 | * to determine the suitable temporary directory. 290 | * 291 | * @see http://de.php.net/sys_get_temp_dir 292 | * @see http://pear.php.net/bugs/bug.php?id=18328 293 | * 294 | * @param array $options options 295 | * @access public 296 | */ 297 | function __construct($options = array(NULL)) 298 | { 299 | foreach($options as $key => $value) { 300 | $this->setOption($key, $value); 301 | } 302 | if (!isset($options['cacheDir']) && function_exists('sys_get_temp_dir')) { 303 | $this->setOption('cacheDir', sys_get_temp_dir() . DIRECTORY_SEPARATOR); 304 | } 305 | } 306 | 307 | /** 308 | * Generic way to set a Cache_Lite option 309 | * 310 | * see Cache_Lite constructor for available options 311 | * 312 | * @var string $name name of the option 313 | * @var mixed $value value of the option 314 | * @access public 315 | */ 316 | function setOption($name, $value) 317 | { 318 | $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode', 'hashedDirectoryGroup', 'cacheFileMode', 'cacheFileGroup'); 319 | if (in_array($name, $availableOptions)) { 320 | $property = '_'.$name; 321 | $this->$property = $value; 322 | } 323 | } 324 | 325 | /** 326 | * Test if a cache is available and (if yes) return it 327 | * 328 | * @param string $id cache id 329 | * @param string $group name of the cache group 330 | * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 331 | * @return string data of the cache (else : false) 332 | * @access public 333 | */ 334 | function get($id, $group = 'default', $doNotTestCacheValidity = false) 335 | { 336 | $this->_id = $id; 337 | $this->_group = $group; 338 | $data = false; 339 | if ($this->_caching) { 340 | $this->_setRefreshTime(); 341 | $this->_setFileName($id, $group); 342 | clearstatcache(); 343 | if ($this->_memoryCaching) { 344 | if (isset($this->_memoryCachingArray[$this->_file])) { 345 | if ($this->_automaticSerialization) { 346 | return unserialize($this->_memoryCachingArray[$this->_file]); 347 | } 348 | return $this->_memoryCachingArray[$this->_file]; 349 | } 350 | if ($this->_onlyMemoryCaching) { 351 | return false; 352 | } 353 | } 354 | if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) { 355 | if (file_exists($this->_file)) { 356 | $data = $this->_read(); 357 | } 358 | } else { 359 | if ((file_exists($this->_file)) && (@filemtime($this->_file) > $this->_refreshTime)) { 360 | $data = $this->_read(); 361 | } 362 | } 363 | if (($data) and ($this->_memoryCaching)) { 364 | $this->_memoryCacheAdd($data); 365 | } 366 | if (($this->_automaticSerialization) and (is_string($data))) { 367 | $data = unserialize($data); 368 | } 369 | return $data; 370 | } 371 | return false; 372 | } 373 | 374 | /** 375 | * Save some data in a cache file 376 | * 377 | * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on) 378 | * @param string $id cache id 379 | * @param string $group name of the cache group 380 | * @return boolean true if no problem (else : false or a PEAR_Error object) 381 | * @access public 382 | */ 383 | function save($data, $id = NULL, $group = 'default') 384 | { 385 | if ($this->_caching) { 386 | if ($this->_automaticSerialization) { 387 | $data = serialize($data); 388 | } 389 | if (isset($id)) { 390 | $this->_setFileName($id, $group); 391 | } 392 | if ($this->_memoryCaching) { 393 | $this->_memoryCacheAdd($data); 394 | if ($this->_onlyMemoryCaching) { 395 | return true; 396 | } 397 | } 398 | if ($this->_automaticCleaningFactor>0 && ($this->_automaticCleaningFactor==1 || mt_rand(1, $this->_automaticCleaningFactor)==1)) { 399 | $this->clean(false, 'old'); 400 | } 401 | if ($this->_writeControl) { 402 | $res = $this->_writeAndControl($data); 403 | if (is_bool($res)) { 404 | if ($res) { 405 | return true; 406 | } 407 | // if $res if false, we need to invalidate the cache 408 | @touch($this->_file, time() - 2*abs($this->_lifeTime)); 409 | return false; 410 | } 411 | } else { 412 | $res = $this->_write($data); 413 | } 414 | if (is_object($res)) { 415 | // $res is a PEAR_Error object 416 | if (!($this->_errorHandlingAPIBreak)) { 417 | return false; // we return false (old API) 418 | } 419 | } 420 | return $res; 421 | } 422 | return false; 423 | } 424 | 425 | /** 426 | * Remove a cache file 427 | * 428 | * @param string $id cache id 429 | * @param string $group name of the cache group 430 | * @param boolean $checkbeforeunlink check if file exists before removing it 431 | * @return boolean true if no problem 432 | * @access public 433 | */ 434 | function remove($id, $group = 'default', $checkbeforeunlink = false) 435 | { 436 | $this->_setFileName($id, $group); 437 | if ($this->_memoryCaching) { 438 | if (isset($this->_memoryCachingArray[$this->_file])) { 439 | unset($this->_memoryCachingArray[$this->_file]); 440 | $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; 441 | } 442 | if ($this->_onlyMemoryCaching) { 443 | return true; 444 | } 445 | } 446 | if ( $checkbeforeunlink ) { 447 | if (!file_exists($this->_file)) return true; 448 | } 449 | return $this->_unlink($this->_file); 450 | } 451 | 452 | /** 453 | * Clean the cache 454 | * 455 | * if no group is specified all cache files will be destroyed 456 | * else only cache files of the specified group will be destroyed 457 | * 458 | * @param string $group name of the cache group 459 | * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', 460 | * 'callback_myFunction' 461 | * @return boolean true if no problem 462 | * @access public 463 | */ 464 | function clean($group = false, $mode = 'ingroup') 465 | { 466 | return $this->_cleanDir($this->_cacheDir, $group, $mode); 467 | } 468 | 469 | /** 470 | * Set to debug mode 471 | * 472 | * When an error is found, the script will stop and the message will be displayed 473 | * (in debug mode only). 474 | * 475 | * @access public 476 | */ 477 | function setToDebug() 478 | { 479 | $this->setOption('pearErrorMode', CACHE_LITE_ERROR_DIE); 480 | } 481 | 482 | /** 483 | * Set a new life time 484 | * 485 | * @param int $newLifeTime new life time (in seconds) 486 | * @access public 487 | */ 488 | function setLifeTime($newLifeTime) 489 | { 490 | $this->_lifeTime = $newLifeTime; 491 | $this->_setRefreshTime(); 492 | } 493 | 494 | /** 495 | * Save the state of the caching memory array into a cache file cache 496 | * 497 | * @param string $id cache id 498 | * @param string $group name of the cache group 499 | * @access public 500 | */ 501 | function saveMemoryCachingState($id, $group = 'default') 502 | { 503 | if ($this->_caching) { 504 | $array = array( 505 | 'counter' => $this->_memoryCachingCounter, 506 | 'array' => $this->_memoryCachingArray 507 | ); 508 | $data = serialize($array); 509 | $this->save($data, $id, $group); 510 | } 511 | } 512 | 513 | /** 514 | * Load the state of the caching memory array from a given cache file cache 515 | * 516 | * @param string $id cache id 517 | * @param string $group name of the cache group 518 | * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 519 | * @access public 520 | */ 521 | function getMemoryCachingState($id, $group = 'default', $doNotTestCacheValidity = false) 522 | { 523 | if ($this->_caching) { 524 | if ($data = $this->get($id, $group, $doNotTestCacheValidity)) { 525 | $array = unserialize($data); 526 | $this->_memoryCachingCounter = $array['counter']; 527 | $this->_memoryCachingArray = $array['array']; 528 | } 529 | } 530 | } 531 | 532 | /** 533 | * Return the cache last modification time 534 | * 535 | * BE CAREFUL : THIS METHOD IS FOR HACKING ONLY ! 536 | * 537 | * @return int last modification time 538 | */ 539 | function lastModified() 540 | { 541 | return @filemtime($this->_file); 542 | } 543 | 544 | /** 545 | * Trigger a PEAR error 546 | * 547 | * To improve performances, the PEAR.php file is included dynamically. 548 | * The file is so included only when an error is triggered. So, in most 549 | * cases, the file isn't included and perfs are much better. 550 | * 551 | * @param string $msg error message 552 | * @param int $code error code 553 | * @access public 554 | */ 555 | function raiseError($msg, $code) 556 | { 557 | include_once('PEAR.php'); 558 | return PEAR::raiseError($msg, $code, $this->_pearErrorMode); 559 | } 560 | 561 | /** 562 | * Extend the life of a valid cache file 563 | * 564 | * see http://pear.php.net/bugs/bug.php?id=6681 565 | * 566 | * @access public 567 | */ 568 | function extendLife() 569 | { 570 | @touch($this->_file); 571 | } 572 | 573 | // --- Private methods --- 574 | 575 | /** 576 | * Compute & set the refresh time 577 | * 578 | * @access private 579 | */ 580 | function _setRefreshTime() 581 | { 582 | if (is_null($this->_lifeTime)) { 583 | $this->_refreshTime = null; 584 | } else { 585 | $this->_refreshTime = time() - $this->_lifeTime; 586 | } 587 | } 588 | 589 | /** 590 | * Remove a file 591 | * 592 | * @param string $file complete file path and name 593 | * @return boolean true if no problem 594 | * @access private 595 | */ 596 | function _unlink($file) 597 | { 598 | if (!@unlink($file)) { 599 | return $this->raiseError('Cache_Lite : Unable to remove cache !', -3); 600 | } 601 | return true; 602 | } 603 | 604 | /** 605 | * Recursive function for cleaning cache file in the given directory 606 | * 607 | * @param string $dir directory complete path (with a trailing slash) 608 | * @param string $group name of the cache group 609 | * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup', 610 | 'callback_myFunction' 611 | * @return boolean true if no problem 612 | * @access private 613 | */ 614 | function _cleanDir($dir, $group = false, $mode = 'ingroup') 615 | { 616 | if ($this->_fileNameProtection) { 617 | $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_'; 618 | } else { 619 | $motif = ($group) ? 'cache_'.$group.'_' : 'cache_'; 620 | } 621 | if ($this->_memoryCaching) { 622 | foreach($this->_memoryCachingArray as $key => $v) { 623 | if (strpos($key, $motif) !== false) { 624 | unset($this->_memoryCachingArray[$key]); 625 | $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; 626 | } 627 | } 628 | if ($this->_onlyMemoryCaching) { 629 | return true; 630 | } 631 | } 632 | if (!($dh = opendir($dir))) { 633 | return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); 634 | } 635 | $result = true; 636 | while (($file = readdir($dh)) !== false) { 637 | if (($file != '.') && ($file != '..')) { 638 | if (substr($file, 0, 6)=='cache_') { 639 | $file2 = $dir . $file; 640 | if (is_file($file2)) { 641 | switch (substr($mode, 0, 9)) { 642 | case 'old': 643 | // files older than lifeTime get deleted from cache 644 | if (!is_null($this->_lifeTime)) { 645 | if ((time() - @filemtime($file2)) > $this->_lifeTime) { 646 | $result = ($result and ($this->_unlink($file2))); 647 | } 648 | } 649 | break; 650 | case 'notingrou': 651 | if (strpos($file2, $motif) === false) { 652 | $result = ($result and ($this->_unlink($file2))); 653 | } 654 | break; 655 | case 'callback_': 656 | $func = substr($mode, 9, strlen($mode) - 9); 657 | if ($func($file2, $group)) { 658 | $result = ($result and ($this->_unlink($file2))); 659 | } 660 | break; 661 | case 'ingroup': 662 | default: 663 | if (strpos($file2, $motif) !== false) { 664 | $result = ($result and ($this->_unlink($file2))); 665 | } 666 | break; 667 | } 668 | } 669 | if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) { 670 | $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode))); 671 | } 672 | } 673 | } 674 | } 675 | return $result; 676 | } 677 | 678 | /** 679 | * Touch the cache file while are recreating it to avoid 680 | * launch this task more then once when necessary 681 | * When the cache recreated and Added in Cache Memory 682 | * @return void 683 | * @access private 684 | */ 685 | function _touchCacheFile(){ 686 | if (file_exists($this->_file)) { 687 | @touch($this->_file); 688 | } 689 | } 690 | /** 691 | * Add some date in the memory caching array 692 | * 693 | * @param string $data data to cache 694 | * @access private 695 | */ 696 | function _memoryCacheAdd($data) 697 | { 698 | $this->_touchCacheFile(); 699 | $this->_memoryCachingArray[$this->_file] = $data; 700 | if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) { 701 | list($key, ) = each($this->_memoryCachingArray); 702 | unset($this->_memoryCachingArray[$key]); 703 | } else { 704 | $this->_memoryCachingCounter = $this->_memoryCachingCounter + 1; 705 | } 706 | } 707 | 708 | /** 709 | * Make a file name (with path) 710 | * 711 | * @param string $id cache id 712 | * @param string $group name of the group 713 | * @access private 714 | */ 715 | function _setFileName($id, $group) 716 | { 717 | 718 | if ($this->_fileNameProtection) { 719 | $suffix = 'cache_'.md5($group).'_'.md5($id); 720 | } else { 721 | $suffix = 'cache_'.$group.'_'.$id; 722 | } 723 | $root = $this->_cacheDir; 724 | if ($this->_hashedDirectoryLevel>0) { 725 | $hash = md5($suffix); 726 | for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { 727 | $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; 728 | } 729 | } 730 | $this->_fileName = $suffix; 731 | $this->_file = $root.$suffix; 732 | } 733 | 734 | /** 735 | * Read the cache file and return the content 736 | * 737 | * @return string content of the cache file (else : false or a PEAR_Error object) 738 | * @access private 739 | */ 740 | function _read() 741 | { 742 | $fp = @fopen($this->_file, "rb"); 743 | if ($fp) { 744 | if ($this->_fileLocking) @flock($fp, LOCK_SH); 745 | clearstatcache(); 746 | $length = @filesize($this->_file); 747 | $mqr = get_magic_quotes_runtime(); 748 | if ($mqr) { 749 | set_magic_quotes_runtime(0); 750 | } 751 | if ($this->_readControl) { 752 | $hashControl = @fread($fp, 32); 753 | $length = $length - 32; 754 | } 755 | 756 | if ($length) { 757 | $data = ''; 758 | // See https://bugs.php.net/bug.php?id=30936 759 | // The 8192 magic number is the chunk size used internally by PHP. 760 | while(!feof($fp)) $data .= fread($fp, 8192); 761 | } else { 762 | $data = ''; 763 | } 764 | if ($mqr) { 765 | set_magic_quotes_runtime($mqr); 766 | } 767 | if ($this->_fileLocking) @flock($fp, LOCK_UN); 768 | @fclose($fp); 769 | if ($this->_readControl) { 770 | $hashData = $this->_hash($data, $this->_readControlType); 771 | if ($hashData != $hashControl) { 772 | if (!(is_null($this->_lifeTime))) { 773 | @touch($this->_file, time() - 2*abs($this->_lifeTime)); 774 | } else { 775 | @unlink($this->_file); 776 | } 777 | return false; 778 | } 779 | } 780 | return $data; 781 | } 782 | return $this->raiseError('Cache_Lite : Unable to read cache !', -2); 783 | } 784 | 785 | /** 786 | * Write the given data in the cache file 787 | * 788 | * @param string $data data to put in cache 789 | * @return boolean true if ok (a PEAR_Error object else) 790 | * @access private 791 | */ 792 | function _write($data) 793 | { 794 | if ($this->_hashedDirectoryLevel > 0) { 795 | $hash = md5($this->_fileName); 796 | $root = $this->_cacheDir; 797 | for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { 798 | $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; 799 | if (!(@is_dir($root))) { 800 | if (@mkdir($root)) 801 | { 802 | @chmod($root, $this->_hashedDirectoryUmask); 803 | if (! is_null($this->_hashedDirectoryGroup)) 804 | @chgrp($root, $this->_hashedDirectoryGroup); 805 | } 806 | } 807 | } 808 | } 809 | // if both _cacheFileMode and _cacheFileGroup is null, then we don't need to call 810 | // file_exists (see below: if ($is_newfile) ...) 811 | $is_newfile = (! is_null($this->_cacheFileMode) || !is_null($this->_cacheFileGroup)) 812 | && ! @file_exists($this->_file); 813 | $fp = @fopen($this->_file, "wb"); 814 | if ($fp) { 815 | if ($this->_fileLocking) @flock($fp, LOCK_EX); 816 | if ($is_newfile) 817 | { 818 | if (! is_null($this->_cacheFileMode)) 819 | @chmod($this->_file, $this->_cacheFileMode); 820 | if (! is_null($this->_cacheFileGroup)) 821 | @chgrp($this->_file, $this->_cacheFileGroup); 822 | } 823 | if ($this->_readControl) { 824 | @fwrite($fp, $this->_hash($data, $this->_readControlType), 32); 825 | } 826 | $mqr = get_magic_quotes_runtime(); 827 | if ($mqr) { 828 | set_magic_quotes_runtime(0); 829 | } 830 | @fwrite($fp, $data); 831 | if ($mqr) { 832 | set_magic_quotes_runtime($mqr); 833 | } 834 | if ($this->_fileLocking) @flock($fp, LOCK_UN); 835 | @fclose($fp); 836 | return true; 837 | } 838 | return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1); 839 | } 840 | 841 | /** 842 | * Write the given data in the cache file and control it just after to avoir corrupted cache entries 843 | * 844 | * @param string $data data to put in cache 845 | * @return boolean true if the test is ok (else : false or a PEAR_Error object) 846 | * @access private 847 | */ 848 | function _writeAndControl($data) 849 | { 850 | $result = $this->_write($data); 851 | if (is_object($result)) { 852 | return $result; # We return the PEAR_Error object 853 | } 854 | $dataRead = $this->_read(); 855 | if (is_object($dataRead)) { 856 | return $dataRead; # We return the PEAR_Error object 857 | } 858 | if ((is_bool($dataRead)) && (!$dataRead)) { 859 | return false; 860 | } 861 | return ($dataRead==$data); 862 | } 863 | 864 | /** 865 | * Make a control key with the string containing datas 866 | * 867 | * @param string $data data 868 | * @param string $controlType type of control 'md5', 'crc32' or 'strlen' 869 | * @return string control key 870 | * @access private 871 | */ 872 | function _hash($data, $controlType) 873 | { 874 | switch ($controlType) { 875 | case 'md5': 876 | return md5($data); 877 | case 'crc32': 878 | return sprintf('% 32d', crc32($data)); 879 | case 'strlen': 880 | return sprintf('% 32d', strlen($data)); 881 | default: 882 | return $this->raiseError('Unknown controlType ! (available values are only \'md5\', \'crc32\', \'strlen\')', -5); 883 | } 884 | } 885 | 886 | } 887 | -------------------------------------------------------------------------------- /lib/Cache/Lite/File.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | 17 | require_once('Cache/Lite.php'); 18 | 19 | class Cache_Lite_File extends Cache_Lite 20 | { 21 | 22 | // --- Private properties --- 23 | 24 | /** 25 | * Complete path of the file used for controlling the cache lifetime 26 | * 27 | * @var string $_masterFile 28 | */ 29 | var $_masterFile = ''; 30 | 31 | /** 32 | * Masterfile mtime 33 | * 34 | * @var int $_masterFile_mtime 35 | */ 36 | var $_masterFile_mtime = 0; 37 | 38 | // --- Public methods ---- 39 | 40 | /** 41 | * Constructor 42 | * 43 | * $options is an assoc. To have a look at availables options, 44 | * see the constructor of the Cache_Lite class in 'Cache_Lite.php' 45 | * 46 | * Comparing to Cache_Lite constructor, there is another option : 47 | * $options = array( 48 | * (...) see Cache_Lite constructor 49 | * 'masterFile' => complete path of the file used for controlling the cache lifetime(string) 50 | * ); 51 | * 52 | * @param array $options options 53 | * @access public 54 | */ 55 | function __construct($options = array(NULL)) 56 | { 57 | $options['lifetime'] = 0; 58 | $this->Cache_Lite($options); 59 | if (isset($options['masterFile'])) { 60 | $this->_masterFile = $options['masterFile']; 61 | } else { 62 | return $this->raiseError('Cache_Lite_File : masterFile option must be set !'); 63 | } 64 | if (!($this->_masterFile_mtime = @filemtime($this->_masterFile))) { 65 | return $this->raiseError('Cache_Lite_File : Unable to read masterFile : '.$this->_masterFile, -3); 66 | } 67 | } 68 | 69 | /** 70 | * Test if a cache is available and (if yes) return it 71 | * 72 | * @param string $id cache id 73 | * @param string $group name of the cache group 74 | * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 75 | * @return string data of the cache (else : false) 76 | * @access public 77 | */ 78 | function get($id, $group = 'default', $doNotTestCacheValidity = false) 79 | { 80 | if ($data = parent::get($id, $group, true)) { 81 | if ($filemtime = $this->lastModified()) { 82 | if ($filemtime > $this->_masterFile_mtime) { 83 | return $data; 84 | } 85 | } 86 | } 87 | return false; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /lib/Cache/Lite/Function.php: -------------------------------------------------------------------------------- 1 | 15 | * @author Fabien MARTY 16 | */ 17 | 18 | require_once('Cache/Lite.php'); 19 | 20 | class Cache_Lite_Function extends Cache_Lite 21 | { 22 | 23 | // --- Private properties --- 24 | 25 | /** 26 | * Default cache group for function caching 27 | * 28 | * @var string $_defaultGroup 29 | */ 30 | var $_defaultGroup = 'Cache_Lite_Function'; 31 | 32 | /** 33 | * Don't cache the method call when its output contains the string "NOCACHE" 34 | * 35 | * if set to true, the output of the method will never be displayed (because the output is used 36 | * to control the cache) 37 | * 38 | * @var boolean $_dontCacheWhenTheOutputContainsNOCACHE 39 | */ 40 | var $_dontCacheWhenTheOutputContainsNOCACHE = false; 41 | 42 | /** 43 | * Don't cache the method call when its result is false 44 | * 45 | * @var boolean $_dontCacheWhenTheResultIsFalse 46 | */ 47 | var $_dontCacheWhenTheResultIsFalse = false; 48 | 49 | /** 50 | * Don't cache the method call when its result is null 51 | * 52 | * @var boolean $_dontCacheWhenTheResultIsNull 53 | */ 54 | var $_dontCacheWhenTheResultIsNull = false; 55 | 56 | /** 57 | * Debug the Cache_Lite_Function caching process 58 | * 59 | * @var boolean $_debugCacheLiteFunction 60 | */ 61 | var $_debugCacheLiteFunction = false; 62 | 63 | // --- Public methods ---- 64 | 65 | /** 66 | * Constructor 67 | * 68 | * $options is an assoc. To have a look at availables options, 69 | * see the constructor of the Cache_Lite class in 'Cache_Lite.php' 70 | * 71 | * Comparing to Cache_Lite constructor, there is another option : 72 | * $options = array( 73 | * (...) see Cache_Lite constructor 74 | * 'debugCacheLiteFunction' => (bool) debug the caching process, 75 | * 'defaultGroup' => default cache group for function caching (string), 76 | * 'dontCacheWhenTheOutputContainsNOCACHE' => (bool) don't cache when the function output contains "NOCACHE", 77 | * 'dontCacheWhenTheResultIsFalse' => (bool) don't cache when the function result is false, 78 | * 'dontCacheWhenTheResultIsNull' => (bool don't cache when the function result is null 79 | * ); 80 | * 81 | * @param array $options options 82 | * @access public 83 | */ 84 | function __construct($options = array(NULL)) 85 | { 86 | $availableOptions = array('debugCacheLiteFunction', 'defaultGroup', 'dontCacheWhenTheOutputContainsNOCACHE', 'dontCacheWhenTheResultIsFalse', 'dontCacheWhenTheResultIsNull'); 87 | while (list($name, $value) = each($options)) { 88 | if (in_array($name, $availableOptions)) { 89 | $property = '_'.$name; 90 | $this->$property = $value; 91 | } 92 | } 93 | reset($options); 94 | $this->Cache_Lite($options); 95 | } 96 | 97 | /** 98 | * Calls a cacheable function or method (or not if there is already a cache for it) 99 | * 100 | * Arguments of this method are read with func_get_args. So it doesn't appear 101 | * in the function definition. Synopsis : 102 | * call('functionName', $arg1, $arg2, ...) 103 | * (arg1, arg2... are arguments of 'functionName') 104 | * 105 | * @return mixed result of the function/method 106 | * @access public 107 | */ 108 | function call() 109 | { 110 | $arguments = func_get_args(); 111 | $id = $this->_makeId($arguments); 112 | $data = $this->get($id, $this->_defaultGroup); 113 | if ($data !== false) { 114 | if ($this->_debugCacheLiteFunction) { 115 | echo "Cache hit !\n"; 116 | } 117 | $array = unserialize($data); 118 | $output = $array['output']; 119 | $result = $array['result']; 120 | } else { 121 | if ($this->_debugCacheLiteFunction) { 122 | echo "Cache missed !\n"; 123 | } 124 | ob_start(); 125 | ob_implicit_flush(false); 126 | $target = array_shift($arguments); 127 | if (is_array($target)) { 128 | // in this case, $target is for example array($obj, 'method') 129 | $object = $target[0]; 130 | $method = $target[1]; 131 | $result = call_user_func_array(array(&$object, $method), $arguments); 132 | } else { 133 | if (strstr($target, '::')) { // classname::staticMethod 134 | list($class, $method) = explode('::', $target); 135 | $result = call_user_func_array(array($class, $method), $arguments); 136 | } else if (strstr($target, '->')) { // object->method 137 | // use a stupid name ($objet_123456789 because) of problems where the object 138 | // name is the same as this var name 139 | list($object_123456789, $method) = explode('->', $target); 140 | global $$object_123456789; 141 | $result = call_user_func_array(array($$object_123456789, $method), $arguments); 142 | } else { // function 143 | $result = call_user_func_array($target, $arguments); 144 | } 145 | } 146 | $output = ob_get_contents(); 147 | ob_end_clean(); 148 | if ($this->_dontCacheWhenTheResultIsFalse) { 149 | if ((is_bool($result)) && (!($result))) { 150 | echo($output); 151 | return $result; 152 | } 153 | } 154 | if ($this->_dontCacheWhenTheResultIsNull) { 155 | if (is_null($result)) { 156 | echo($output); 157 | return $result; 158 | } 159 | } 160 | if ($this->_dontCacheWhenTheOutputContainsNOCACHE) { 161 | if (strpos($output, 'NOCACHE') > -1) { 162 | return $result; 163 | } 164 | } 165 | $array['output'] = $output; 166 | $array['result'] = $result; 167 | $this->save(serialize($array), $id, $this->_defaultGroup); 168 | } 169 | echo($output); 170 | return $result; 171 | } 172 | 173 | /** 174 | * Drop a cache file 175 | * 176 | * Arguments of this method are read with func_get_args. So it doesn't appear 177 | * in the function definition. Synopsis : 178 | * remove('functionName', $arg1, $arg2, ...) 179 | * (arg1, arg2... are arguments of 'functionName') 180 | * 181 | * @return boolean true if no problem 182 | * @access public 183 | */ 184 | function drop() 185 | { 186 | $id = $this->_makeId(func_get_args()); 187 | return $this->remove($id, $this->_defaultGroup); 188 | } 189 | 190 | /** 191 | * Make an id for the cache 192 | * 193 | * @var array result of func_get_args for the call() or the remove() method 194 | * @return string id 195 | * @access private 196 | */ 197 | function _makeId($arguments) 198 | { 199 | $id = serialize($arguments); // Generate a cache id 200 | if (!$this->_fileNameProtection) { 201 | $id = md5($id); 202 | // if fileNameProtection is set to false, then the id has to be hashed 203 | // because it's a very bad file name in most cases 204 | } 205 | return $id; 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /lib/Cache/Lite/NestedOutput.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | 11 | require_once('Cache/Lite/Output.php'); 12 | 13 | class Cache_Lite_NestedOutput extends Cache_Lite_Output 14 | { 15 | private $nestedIds = array(); 16 | private $nestedGroups = array(); 17 | 18 | /** 19 | * Start the cache 20 | * 21 | * @param string $id cache id 22 | * @param string $group name of the cache group 23 | * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 24 | * @return boolean|string false if the cache is not hit else the data 25 | * @access public 26 | */ 27 | function start($id, $group = 'default', $doNotTestCacheValidity = false) 28 | { 29 | $this->nestedIds[] = $id; 30 | $this->nestedGroups[] = $group; 31 | $data = $this->get($id, $group, $doNotTestCacheValidity); 32 | if ($data !== false) { 33 | return $data; 34 | } 35 | ob_start(); 36 | ob_implicit_flush(false); 37 | return false; 38 | } 39 | 40 | /** 41 | * Stop the cache 42 | * 43 | * @param boolen 44 | * @return string return contents of cache 45 | */ 46 | function end() 47 | { 48 | $data = ob_get_contents(); 49 | ob_end_clean(); 50 | $id = array_pop($this->nestedIds); 51 | $group = array_pop($this->nestedGroups); 52 | $this->save($data, $id, $group); 53 | return $data; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /lib/Cache/Lite/Output.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | 13 | require_once('Cache/Lite.php'); 14 | 15 | class Cache_Lite_Output extends Cache_Lite 16 | { 17 | 18 | // --- Public methods --- 19 | 20 | /** 21 | * Constructor 22 | * 23 | * $options is an assoc. To have a look at availables options, 24 | * see the constructor of the Cache_Lite class in 'Cache_Lite.php' 25 | * 26 | * @param array $options options 27 | * @access public 28 | */ 29 | function __construct($options) 30 | { 31 | $this->Cache_Lite($options); 32 | } 33 | 34 | /** 35 | * Start the cache 36 | * 37 | * @param string $id cache id 38 | * @param string $group name of the cache group 39 | * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested 40 | * @return boolean true if the cache is hit (false else) 41 | * @access public 42 | */ 43 | function start($id, $group = 'default', $doNotTestCacheValidity = false) 44 | { 45 | $data = $this->get($id, $group, $doNotTestCacheValidity); 46 | if ($data !== false) { 47 | echo($data); 48 | return true; 49 | } 50 | ob_start(); 51 | ob_implicit_flush(false); 52 | return false; 53 | } 54 | 55 | /** 56 | * Stop the cache 57 | * 58 | * @access public 59 | */ 60 | function end() 61 | { 62 | $data = ob_get_contents(); 63 | ob_end_clean(); 64 | $this->save($data, $this->_id, $this->_group); 65 | echo($data); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /proxy-simple.php: -------------------------------------------------------------------------------- 1 | array( 43 | "proxy" => "tcp://your.proxy.com:8080", 44 | "request_fulluri" => TRUE, 45 | ), 46 | ) 47 | ); 48 | */ 49 | 50 | 51 | /* Hard-coded target host */ 52 | $url = "http://www.example.com/"; 53 | 54 | /* 1. Check the URL to retrieve */ 55 | if( isset( $url ) ) { 56 | $url = $url . $_SERVER['QUERY_STRING']; 57 | } else { 58 | $my_GET = my_parse_str($_SERVER['QUERY_STRING']); 59 | if( FALSE == isset($my_GET["dst"]) ) { 60 | error("GET parameter dst is not set."); 61 | } else { 62 | $url = $my_GET["dst"]; 63 | } 64 | } 65 | 66 | 67 | /* 2. Add http:// to URL */ 68 | if(strpos($url, "http://") === 0 || strpos($url, "https://") === 0) { 69 | } else { 70 | $url = "http://" . $url; 71 | } 72 | 73 | 74 | /* 6. Retrieve the remote data */ 75 | $ret = file_get_contents($url, FALSE); 76 | if( FALSE === $ret ) { 77 | error("Cannot get $url ."); 78 | } 79 | 80 | /* 7. Set response_header to received header */ 81 | foreach ($http_response_header as $header) { 82 | header($header); 83 | } 84 | 85 | /* 8. Show */ 86 | echo $ret; 87 | 88 | ?> 89 | -------------------------------------------------------------------------------- /proxy.php: -------------------------------------------------------------------------------- 1 | "; 44 | } 45 | 46 | 47 | /* # 0. Configure the proxy */ 48 | /* 49 | stream_context_set_default( 50 | array("http" => array( 51 | "proxy" => "tcp://your.proxy.com:8080", 52 | "request_fulluri" => TRUE, 53 | ), 54 | ) 55 | ); 56 | */ 57 | 58 | 59 | /* Hard-coded target host */ 60 | $url = "http://www.example.com/"; 61 | 62 | /* 1. Check the URL to retrieve */ 63 | if( isset( $url ) ) { 64 | $url = $url . $_SERVER['QUERY_STRING']; 65 | } else { 66 | $my_GET = my_parse_str($_SERVER['QUERY_STRING']); 67 | if( FALSE == isset($my_GET["dst"]) ) { 68 | error("GET parameter dst is not set."); 69 | } else { 70 | $url = $my_GET["dst"]; 71 | 72 | $params = []; 73 | foreach ($_GET as $key => $value){ 74 | if($key == "dst") continue; 75 | array_push($params, $key.'='.$value); 76 | } 77 | $url .= '?' . join('&',$params); 78 | } 79 | } 80 | 81 | 82 | /* 2. Add http:// to URL */ 83 | if(strpos($url, "http://") === 0 || strpos($url, "https://") === 0) { 84 | } else { 85 | $url = "http://" . $url; 86 | } 87 | 88 | /* 3. Check the timestamp */ 89 | $headers = get_headers($url, 1); 90 | $ts_remote = null; 91 | if($headers) { 92 | if( array_key_exists('Last-Modified', $headers) ) { 93 | // most of web sites 94 | $ts_remote = strtotime($headers['Last-Modified']); 95 | } else if( array_key_exists('etag', $headers) ) { 96 | // dl.dropboxusercontent.com 97 | $ts_remote = $headers['etag']; 98 | } else if( array_key_exists('ETag', $headers) ) { 99 | $ts_remote = $headers['ETag']; 100 | } 101 | } 102 | header("X-Timestamp-Remote: $ts_remote"); 103 | 104 | if( array_key_exists('Content-Type', $headers) ) { 105 | header("Content-Type: " . $headers['Content-Type']); 106 | } 107 | 108 | 109 | /* 4. Init the cache */ 110 | $options = array( 111 | 'lifeTime' => null, 112 | 'pearErrorMode' => CACHE_LITE_ERROR_DIE 113 | ); 114 | $cache = new Cache_Lite($options); 115 | 116 | 117 | /* 5. Check if cached or not */ 118 | if($ts_remote != null) { 119 | $ts_local = $cache->get(timestamp_url_key($url)); 120 | if($ts_local) { 121 | header("X-Timestamp-Local: $ts_local"); 122 | if($ts_remote == $ts_local) { 123 | header("X-Cache-Used: Yes"); 124 | echo $cache->get($url); 125 | exit; 126 | } 127 | } 128 | } 129 | 130 | 131 | /* 6. Retrieve the remote data */ 132 | $ret = file_get_contents($url, FALSE); 133 | if( FALSE === $ret ) { 134 | error("Cannot get $url ."); 135 | } 136 | 137 | 138 | /* 7. Cache */ 139 | if( $ts_remote ) { 140 | header("X-Cache-Updated: Yes"); 141 | $cache->save($ret, $url); 142 | $cache->save($ts_remote, timestamp_url_key($url)); 143 | } 144 | 145 | 146 | /* 8. Show */ 147 | header("X-Cache-Used: No"); 148 | echo $ret; 149 | 150 | ?> 151 | --------------------------------------------------------------------------------