Overview

Namespaces

  • Ludato

Classes

  • Ludato\HyperCache
  • Overview
  • Namespace
  • Class
  1: <?php
  2: 
  3:  /*
  4:   * The MIT License
  5:   *
  6:   * Copyright 2015 LUDATO.
  7:   *
  8:   * Permission is hereby granted, free of charge, to any person obtaining a copy
  9:   * of this software and associated documentation files (the "Software"), to deal
 10:   * in the Software without restriction, including without limitation the rights
 11:   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12:   * copies of the Software, and to permit persons to whom the Software is
 13:   * furnished to do so, subject to the following conditions:
 14:   *
 15:   * The above copyright notice and this permission notice shall be included in
 16:   * all copies or substantial portions of the Software.
 17:   *
 18:   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19:   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20:   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21:   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22:   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23:   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24:   * THE SOFTWARE.
 25:   */
 26: 
 27:  namespace Ludato;
 28: 
 29:  /**
 30:   * LUDATO HyperCache
 31:   * @author David Kostal
 32:   * @license http://opensource.org/licenses/MIT MIT
 33:   */
 34:  Class HyperCache {
 35: 
 36:      private $fullDirectory;
 37:      private $fullFilename;
 38:      private $cacheDirectory;
 39:      private $pagePath;
 40:      private $fullPrependPath;
 41:      private $fullAppendPath;
 42:      private $dev;
 43: 
 44:      /**
 45:       * @var string Valid PHP code (without opening tags) to be prepended to cache
 46:       */
 47:      public $prepend;
 48: 
 49:      /**
 50:       * @var string Valid PHP code (without opening tags) to be appended to cache
 51:       */
 52:      public $append;
 53: 
 54:      /**
 55:       *
 56:       * @var boolean Determines if prepended code should be eval()'ed when generating cache (alternative - put code before startCache())
 57:       */
 58:      public $evalPrepend;
 59: 
 60:      /**
 61:       *
 62:       * @var boolean Determines if appended code should be eval()'ed when generating cache (alternative - put code after saveCache())
 63:       */
 64:      public $evalAppend;
 65: 
 66:      /**
 67:       * Saving params and configuring
 68:       * Note: Paths are saved url-encoded
 69:       * @param string $directory Path to directory for caching
 70:       * @param string|null $page Full page name, including extension (if you want it determined automatically using PHP_SELF, use NULL)
 71:       * @param string $param Parameters of caching (eg. for details.php it would be ID). You can combine whatever you want here
 72:       * @param boolean $dev Determines if development texts are shown
 73:       */
 74:      function __construct($directory, $page = NULL, $param = "default", $dev = FALSE) {
 75:          mb_internal_encoding("UTF-8");
 76:          if ($page === NULL) {
 77:              $pathinfo = pathinfo($_SERVER['PHP_SELF']);
 78:              $page = $pathinfo['basename'];
 79:          }
 80:          /*
 81:            if (mb_strpos($directory, "./") === 1) {
 82:            $directory = "./" . $directory;
 83:            } */
 84:          if (mb_substr($directory, -1) === DIRECTORY_SEPARATOR) {
 85:              $directory = rtrim($directory, DIRECTORY_SEPARATOR);
 86:          }
 87:          $this->fullDirectory = urlencode($directory) . DIRECTORY_SEPARATOR . urlencode($page) . DIRECTORY_SEPARATOR . urlencode($param) . DIRECTORY_SEPARATOR;
 88: 
 89:          $this->cacheDirectory = urlencode($directory);
 90:          $this->pagePath = urlencode($directory) . DIRECTORY_SEPARATOR . urlencode($page) . DIRECTORY_SEPARATOR;
 91: 
 92:          if ($dev) {
 93:              $this->dev = TRUE;
 94:          } else {
 95:              $this->dev = FALSE;
 96:          }
 97: 
 98:          $this->fullFilename = $this->fullDirectory . "cached";
 99:          $this->fullPrependPath = $this->fullDirectory . "prepend" . ".php";
100:          $this->fullAppendPath = $this->fullDirectory . "append" . ".php";
101:      }
102: 
103:      /**
104:       * Starts caching
105:       * @return void
106:       */
107:      function startCache() {
108:          if ($this->evalPrepend) {
109:              eval($this->prepend);
110:          }
111:          flush();
112:          ob_start();
113:      }
114: 
115:      /**
116:       * Saves cached file and flushes the buffer (shows output)
117:       * @return boolean
118:       */
119:      function saveCache() {
120:          if (is_writable($this->fullDirectory)) {
121:              $dirmade = TRUE;
122:          } else {
123:              $dirmade = @mkdir($this->fullDirectory, 0777, TRUE);
124:          }
125: 
126:          if (!$dirmade) {
127:              throw new \Exception("Caching directory not writeable", 0);
128:          }
129: 
130:          if (!is_file($this->cacheDirectory . ".htaccess")) {
131:              $hw = fopen($this->cacheDirectory . ".htaccess", "w");
132:              $htaccess = <<<EOT
133: Order deny,allow
134: Deny from all
135: EOT;
136:              fputs($hw, $htaccess, strlen($htaccess));
137:              fclose($hw);
138:          }
139: 
140:          $page = ob_get_contents();
141:          ob_end_clean();
142:          //$time = time();
143:          @unlink($this->fullFilename);
144:          @unlink($this->fullPrependPath);
145:          @unlink($this->fullAppendPath);
146:          //chmod($file, 0777);
147: 
148:          $fw = fopen($this->fullFilename, "w");
149:          fputs($fw, $page, strlen($page));
150:          fclose($fw);
151: 
152:          if ($this->prepend) {
153:              $fwp = fopen($this->fullPrependPath, "w");
154:              $prepend = "<?php " . $this->prepend . "?>";
155:              fputs($fwp, $prepend, strlen($prepend));
156:              fclose($fwp);
157:          }
158: 
159:          if ($this->append) {
160:              $fwp = fopen($this->fullAppendPath, "w");
161:              $append = "<?php " . $this->append . "?>";
162:              fputs($fwp, $append, strlen($append));
163:              fclose($fwp);
164:          }
165: 
166:          echo $page;
167:          if ($this->dev) {
168:              echo 'cache generated';
169:          }
170:          if ($this->evalAppend) {
171:              eval($this->append);
172:          }
173:          return TRUE;
174:      }
175: 
176:      /**
177:       * Loads cached file
178:       * @return void
179:       */
180:      function getCache() {
181:          if ($this->dev) {
182:              $time_pre = microtime(true);
183:          }
184: 
185:          if (is_file($this->fullPrependPath)) {
186:              require $this->fullPrependPath;
187:          }
188: 
189:          readfile($this->fullFilename);
190: 
191:          if (is_file($this->fullAppendPath)) {
192:              require $this->fullAppendPath;
193:          }
194: 
195:          if ($this->dev) {
196:              $time_post = microtime(true);
197:              $exec_time = ($time_post - $time_pre) * 1000;
198:              echo "Loaded from cache ({$this->fullFilename} in {$exec_time} ms)"; //DEBUG!
199:          }
200:      }
201: 
202:      /**
203:       * Shows cache and if file is cached, otherwise starts caching and executing the rest of the file
204:       * @return void
205:       */
206:      function autoLoadCache() {
207:          if ($this->isCached()) {
208:              $this->getCache();
209:              die();
210:          } else {
211:              $this->startCache();
212:          }
213:      }
214: 
215:      /**
216:       * Automaically ending and saving cache
217:       * @return void
218:       */
219:      function autoEndCache() {
220:          try {
221:              $this->saveCache();
222:          } catch (\Exception $e) {
223:              echo "\n" . $e . "\n";
224:              die();
225:          }
226:      }
227: 
228:      /**
229:       * Returns if file is cached
230:       * @return boolean Returns true if cached, else returns false.
231:       */
232:      function isCached() {
233:          if (is_readable($this->fullFilename)) {
234:              return TRUE;
235:          } else {
236:              return FALSE;
237:          }
238:      }
239: 
240:      /**
241:       * Deletes cache for current page and params (eg. You have CMS and publish a new article. You need to purge just article_list.php, not article_details.php. If user publishes comment, purge just article_details.php with param of article name/ID)
242:       * @return void
243:       */
244:      function purgeCurrent() {
245:          $this->recursiveDelete($this->fullDirectory);
246:      }
247: 
248:      /**
249:       * Purges all files specified cacing directory
250:       * @return void
251:       */
252:      function purgeAll() {
253:          $this->recursiveDelete($this->cacheDirectory);
254:      }
255: 
256:      /**
257:       * Purges file for current page (eg. You have CMS and publish a new article. You need to purge just article_list.php, not article_details.php. If user publishes comment, purge just article_details.php with param of article name/ID)
258:       * @return void
259:       */
260:      function purgePage() {
261:          $this->recursiveDelete($this->pagePath);
262:      }
263: 
264:      /**
265:       * Purge cache from specified directory, for specific page (and params)
266:       * @param string $directory Caching directory
267:       * @param string $page Page
268:       * @param string $param Params (opt.)
269:       * @deprecated 0.1.0
270:       */
271:      function purgeCustom($directory, $page, $param = NULL) {
272:          if (!(mb_substr($directory, -1) === DIRECTORY_SEPARATOR)) {
273:              $directory = $directory . DIRECTORY_SEPARATOR;
274:          }
275:          if ($param !== NULL) {
276: 
277:              if (!(mb_substr($param, -1) === DIRECTORY_SEPARATOR)) {
278:                  $param = $param . DIRECTORY_SEPARATOR;
279:              }
280: 
281:              $dir = urlencode($directory) . urlencode($page) . DIRECTORY_SEPARATOR . urlencode($param);
282:          } else {
283:              $dir = urlencode($directory) . urlencode($page) . DIRECTORY_SEPARATOR;
284:          }
285:          $this->recursiveDelete($dir);
286:      }
287: 
288:      /**
289:       * Delete a file or recursively delete a directory
290:       * @param string $str Path to file or directory
291:       * @return void
292:       */
293:      private function recursiveDelete($str) {
294:          try {
295:              if (is_file($str)) {
296:                  return @unlink($str);
297:              } elseif (is_dir($str)) {
298:                  $scan = glob(rtrim($str, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . '*');
299:                  foreach ($scan as $path) {
300:                      $this->recursiveDelete($path);
301:                  }
302:                  return @rmdir($str);
303:              } else {
304:                  throw new \Exception("Invalid path", 0);
305:              }
306:          } catch (\Exception $e) {
307:              echo $e;
308:              echo "Internal HyperCache error";
309:              die();
310:          }
311:      }
312: 
313:  }
314: 
315:  /**
316:   * @todo finih this
317:   * Sanitizes folder/file name
318:   * @param string $str Input string
319:   * @return string Sanitized string
320:   */
321:  /*
322:    function cache_filename_sanitize($str) {
323:    preg_replace("/[\/]")
324:    }
325:   */
326: 
API documentation generated by ApiGen