2 /************************************************************************
3 * Mailer v0.2.1-FINAL Start: 10/22/2009 *
4 * =================== Last change: 10/22/2009 *
6 * -------------------------------------------------------------------- *
7 * File : cachesystem.class.php *
8 * -------------------------------------------------------------------- *
9 * Short description : CacheSystem class *
10 * -------------------------------------------------------------------- *
11 * Kurzbeschreibung : CacheSystem-Klasse *
12 * -------------------------------------------------------------------- *
15 * $Tag:: 0.2.1-FINAL $ *
17 * -------------------------------------------------------------------- *
18 * Copyright (c) 2003 - 2009 by Roland Haeder *
19 * Copyright (c) 2009 - 2013 by Mailer Developer Team *
20 * For more information visit: http://mxchange.org *
22 * This program is free software; you can redistribute it and/or modify *
23 * it under the terms of the GNU General Public License as published by *
24 * the Free Software Foundation; either version 2 of the License, or *
25 * (at your option) any later version. *
27 * This program is distributed in the hope that it will be useful, *
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
30 * GNU General Public License for more details. *
32 * You should have received a copy of the GNU General Public License *
33 * along with this program; if not, write to the Free Software *
34 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
36 ************************************************************************/
38 // Some security stuff...
39 if (!defined('__SECURITY')) {
46 var $statusCode = 'init';
48 // Full-qualified filename
51 // Resource to cache file
54 // Data array from cache
57 // Version data from cache
58 var $version = array();
62 var $rebuilt = array();
66 var $status = array();
67 var $readable = array();
71 function CacheSystem () {
73 $this->extension = getCacheExtension();
75 // Construct full path
76 $this->fullPath = getPath() . getCachePath();
78 // Failed is the default
79 $this->setStatusCode('failed');
81 // Check if path exists
82 if (isDirectory($this->fullPath)) {
83 // Is there a .htaccess file?
84 if (isFileReadable($this->fullPath . '.htaccess')) {
86 $this->setStatusCode('done');
88 // Stop! Set a .htaccess file first
89 $this->setStatusCode('htaccess');
94 // Checks validity of cache file and if content is given
95 function loadCacheFile ($cacheName) {
96 // Remember cache file
97 $this->name = $cacheName;
99 // Construct FQFN (full qualified file name)
100 $this->fqfn = $this->fullPath . $cacheName . $this->extension;
102 // Check if file exists and if version matches
103 if (!isset($this->status[$cacheName])) {
104 // Pre-fetch cache here if found
105 if ($this->isCacheReadable()) $this->getArrayFromCache();
107 //* DEBUG: */ debugOutput('cacheName='.$cacheName.',isCacheReadable='.intval($this->isCacheReadable()).',is_writeable='.intval(is_writeable($this->fqfn)).',extensionMatches='.intval($this->extensionVersionMatches('cache')));
108 $this->status[$cacheName] = ($this->isCacheReadable() && (is_writeable($this->fqfn)) && ($this->extensionVersionMatches('cache')));
110 //* DEBUG: */ debugOutput('cacheName='.$cacheName.',status='.intval($this->status[$cacheName]));
113 return $this->status[$cacheName];
116 // Initializes the cache file
118 // This will destory an existing cache file!
119 if ($this->getStatusCode() == 'done') {
121 $this->resetCacheReadStatus();
124 if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
125 $this->pointer = fopen($this->fqfn, 'w') or reportBug(__METHOD__, __LINE__, 'Cannot write to cache ' . $this->fqfn . ' !');
128 $this->writeLine('<?php');
130 // Cannot create file
131 reportBug(__METHOD__, __LINE__, 'Problems with cache directory detected, getStatusCode()=' . $this->getStatusCode());
136 * Writes a line to the pointer and adds a \n (new-line) to the end
140 function writeLine ($line) {
141 // Is the pointer a valid resource?
142 if (is_resource($this->pointer)) {
144 fwrite($this->pointer, $line . PHP_EOL);
146 // Something bad happened
147 reportBug(__METHOD__, __LINE__, 'Pointer type is ' . gettype($this->pointer) . ', expected is resource.');
151 // Reset the read status
152 function resetCacheReadStatus () {
153 unset($this->readable[$this->name]);
154 unset($GLOBALS['file_readable'][$this->fqfn]);
155 unset($this->status[$this->name]);
158 // Adds a data row (array) to cache file and global cache array
159 function addRow ($data) {
160 // Is the pointe rvalid?
161 if (is_resource($this->pointer)) {
162 // Write every array element to cache file
163 foreach ($data as $k => $v) {
164 // Write global cache array for immediate access
165 if ((substr($k, 0, 4) == 'ext_') && (isset($data['ext_name'])) && (isset($data['ext_id']))) {
166 if ($k == 'ext_name') {
167 $GLOBALS['cache_array']['extension'][$k][$data['ext_id']] = $v;
169 $GLOBALS['cache_array']['extension'][$k][$data['ext_name']] = $v;
171 if (($k == 'ext_keep') && ($v == 'Y')) {
172 $GLOBALS['cache_array']['always_active'][$data['ext_name']] = $v;
174 } elseif ($this->name == 'config') {
176 $GLOBALS['cache_array']['config'][$data['config']][$k] = $v;
177 } elseif ($this->name == 'filter') {
179 $GLOBALS['cache_array']['filter']['chains'][$data['filter_name']][$data['filter_function']] = $data['filter_active'];
180 $GLOBALS['cache_array']['filter']['counter'][$data['filter_name']][$data['filter_function']] = $data['filter_counter'];
181 $GLOBALS['cache_array']['filter']['loaded'][$data['filter_name']][$data['filter_function']] = TRUE;
182 } elseif ($this->name == 'modules') {
184 $GLOBALS['cache_array']['modules'][$k][$data['module']] = $v;
185 } elseif ($this->name == 'admin') {
187 if ($k == 'admin_id') {
188 $GLOBALS['cache_array']['admin'][$k][$data['login']] = $v;
190 $GLOBALS['cache_array']['admin'][$k][$data['admin_id']] = $v;
192 } elseif ($this->name == 'admin_acls') {
193 // Access control lines
194 array_push($GLOBALS['cache_array']['admin_acls'][$k][$data['admin_id']], $v);
195 } elseif ($this->name == 'refdepths') {
197 $GLOBALS['cache_array']['refdepths'][$k][$data['id']] = $v;
198 } elseif ($this->name == 'refsystem') {
200 $GLOBALS['cache_array']['refsystem'][$k][$data['id']] = $v;
201 } elseif ($this->name == 'revision') {
203 $GLOBALS['cache_array']['revision'][$k][0] = $v;
204 } elseif ($this->name == 'themes') {
206 if ($k == 'theme_path') {
207 $GLOBALS['cache_array']['themes'][$k][$data['id']] = $v;
209 $GLOBALS['cache_array']['themes'][$k][$data['theme_path']] = $v;
211 } elseif ($this->name == 'imprint') {
213 $GLOBALS['cache_array']['imprint'][$k][$data['imprint_id']] = $v;
214 } elseif ($this->name == 'points_data') {
215 // Table 'points_data'
216 $GLOBALS['cache_array']['points_data'][$k][$data['id']] = $v;
217 } elseif ($this->name == 'earning') {
219 $GLOBALS['cache_array']['earning'][$k][$data['earning_id']] = $v;
220 } elseif ($this->name == 'payments') {
222 $GLOBALS['cache_array']['payments'][$k][$data['id']] = $v;
223 } elseif (is_array($v)) {
224 // Serialize and BASE64-encode the array
225 $v = base64_encode(serialize($v));
227 // Finialize the cache and close it
231 $this->removeCacheFile(TRUE);
233 // Unsupported/unhandled cache detected
234 reportBug(__METHOD__, __LINE__, 'Unsupported cache ' . $this->name . ' detected, data=' . print_r($data, TRUE) . ',k=' . $k . ',v=' . $v);
237 // Write cache line to file
238 $this->writeLine($this->rewriteEntry($k, $v));
241 // Cannot create file
242 reportBug(__METHOD__, __LINE__, 'Problem with cache detected, no resource! pointer[]=' . gettype($this->pointer));
246 // Closes cache file with closing PHP tag
247 function finalize () {
248 // Quit function when no pointer is set
249 if (is_resource($this->pointer)) {
250 // Add default depency
251 $this->storeExtensionVersion('cache');
254 $this->writeLine('?>');
256 // Close file add destroy handler
257 fclose($this->pointer);
259 // Reset readable status
260 $this->resetCacheReadStatus();
263 if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
265 // Remove pointer and status
266 unset($this->status[$this->name]);
267 $this->pointer = FALSE;
268 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'name=' . $this->name . ' - FINALIZED!');
272 // Loads cache file and returns an array of the cached data
273 function getArrayFromCache () {
274 // Is the cache already loaded?
275 if (isset($this->data[$this->name])) {
276 // Return it's content!
277 return $this->data[$this->name];
280 // Is the cache file there?
281 if ($this->isCacheReadable()) {
283 include($this->fqfn);
285 // Is there an array?
286 if (isset($this->data[$this->name])) {
287 // Cache version found?
288 if (isset($this->version[$this->name])) {
289 // Check it here if cache matches ext-cache version
290 if (!$this->extensionVersionMatches('cache')) {
292 logDebugMessage(__METHOD__, __LINE__, 'Invalid cache data ' . $this->name . ' detected.');
293 $this->removeCacheFile();
297 // Return cache if detected
298 if (isset($this->data[$this->name])) {
299 return $this->data[$this->name];
302 logDebugMessage(__METHOD__, __LINE__, 'Possible damaged cache ' . $this->name . ' detected.');
303 $this->removeCacheFile();
307 // Cache file not found or not readable
308 reportBug(__METHOD__, __LINE__, '{%message,CACHE_CANNOT_LOAD=' . $this->name . '%}');
311 $this->removeCacheFile();
314 // Always return an empty array if we have trouble or no data
318 // Destroy an existing cache file
319 function removeCacheFile ($force = FALSE) {
321 $this->resetCacheReadStatus();
324 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, sprintf('%s should be removed.', $this->name));
326 // Is the cache file not yet rebuilt?
327 if ((!isset($this->rebuilt[$this->name])) && ($this->isCacheReadable())) {
328 // Only run in regular output mode
329 if ((!isHtmlOutputMode()) && ($force === FALSE)) {
330 // Debug message if allowed
331 if (isDebugModeEnabled()) {
333 logDebugMessage(__METHOD__, __LINE__, 'Not removing cache ' . $this->name . ' in output_mode=' . getScriptOutputMode());
343 // Debug-mode enabled?
344 if (isDebugModeEnabled()) {
345 // Log removal of cache
346 logDebugMessage(__METHOD__, __LINE__, 'removing cache: ' . $this->name);
349 // Remove cache file from system
350 //* DEBUG: */ reportBug(__METHOD__, __LINE__, 'About to remove ' . basename($this->fqfn) . '!');
351 removeFile($this->fqfn);
354 $this->resetCacheReadStatus();
356 // Is the file there?
357 if (!$this->isCacheReadable()) {
358 // The cache does no longer exist so kill the content
359 unset($this->data[$this->name]);
360 unset($this->version[$this->name]);
361 $this->rebuilt[$this->name] = TRUE;
364 reportBug(__METHOD__, __LINE__, '{%message,CACHE_CANNOT_UNLINK=' . $this->name . '%}');
370 function removeEntry ($search, $data, $array) {
371 if ($this->status[$this->name] == 'done') {
372 // Load cache into dummy array
373 $dummy = $this->getArrayFromCache();
375 // Search for key in array
376 $key = array_search($data, $dummy[$search]);
378 // Key (hopefully) found?
379 foreach ($array as $a) {
380 // So we can remove all elements as requested
381 unset($dummy[$a][$key]);
384 // Flush array to cache file
388 $this->writeArray($dummy);
394 // Cannot write to cache!
395 reportBug(__METHOD__, __LINE__, 'Problem with cache detected: Unexpected status ' . $this->status[$this->name]);
399 function writeArray ($array) {
400 if (is_resource($this->pointer)) {
401 foreach ($array as $k => $v) {
403 // Multi line(s) found
405 foreach ($v as $k2 => $v2) {
406 // Put every array element in a row...
407 $LINE .= $this->rewriteEntry($k, $v2);
411 $LINE = $this->rewriteEntry($k, $v);
415 $this->writeLine($LINE);
418 // Cannot write array!
419 reportBug(__METHOD__, __LINE__, 'Problem with cache detected: pointer is not resource! pointer[]=' . gettype($this->pointer));
424 function replaceEntry ($search, $replace, $search_key, $array) {
425 if ($this->status[$this->name] == 'done') {
426 // Load cache into dummy array
427 $dummy = $this->getArrayFromCache();
429 // Check if $dummy is valid (prevents some errors)
430 if ((is_array($dummy)) && (isset($dummy[$search])) && (is_array($dummy[$search]))) {
431 // Search for key in array
432 $key_found = array_key_exists($search_key, $dummy[$search]);
433 if ($key_found == TRUE) {
435 // Key (hopefully) found?
436 foreach ($dummy as $a => $v) {
437 // So we can update all entries
440 $dummy[$a][$search_key] = $replace;
444 // Flush array to cache file
448 $this->writeArray($dummy);
455 // Cannot write to cache!
456 reportBug(__METHOD__, __LINE__, 'Problem with cache detected: Unexpected status ' . $this->status[$this->name]);
460 // Writes the version of given extension to the cache file
461 function storeExtensionVersion ($ext_name) {
462 // Valid cache pointer?
463 if (is_resource($this->pointer)) {
464 // Write only if extension is installed
465 if (isExtensionInstalled($ext_name)) {
466 // Get extension version
467 $ext_ver = getExtensionVersion($ext_name);
469 // Add the extension version to object (DO NOT REMOVE IT! Endless loop...)
470 $this->version[$this->name][$ext_name] = $ext_ver;
472 // Write cache line to file
473 $this->writeLine($this->rewriteEntry($ext_name, $ext_ver, 'version', TRUE));
475 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'name=' . $this->name . ',ext_name=' . $ext_name . ',ext_ver=' . $ext_ver);
477 // Cannot create file
478 reportBug(__METHOD__, __LINE__, 'Problem with cache detected: pointer is not resource! pointer[]=' . gettype($this->pointer));
482 // Checks whether versions from cache and extension matches
483 function extensionVersionMatches ($ext_name) {
485 if (!isset($GLOBALS[__METHOD__][$ext_name])) {
486 // Does never match by default
487 $GLOBALS[__METHOD__][$ext_name] = FALSE;
489 // Compare only if installed
490 if (isExtensionInstalled($ext_name)) {
491 // Get extension version
492 $ext_ver = getExtensionVersion($ext_name);
495 if (isset($this->version[$this->name][$ext_name])) {
497 $GLOBALS[__METHOD__][$ext_name] = ($this->version[$this->name][$ext_name] == $ext_ver);
498 } elseif ($this->isCacheReadable()) {
499 // No cache version found
500 logDebugMessage(__METHOD__, __LINE__, 'Cache ' . $this->name . ' has missing version entry for extension ext-' . $ext_name . '! Purging cache...');
502 // Remove the cache file
503 $this->removeCacheFile(TRUE);
506 // Not installed, does always match
507 $GLOBALS[__METHOD__][$ext_name] = TRUE;
510 // Cache entry found, log debug message
511 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'ext_name=' . $ext_name . ', matches=' . intval($GLOBALS[__METHOD__][$ext_name]));
515 return $GLOBALS[__METHOD__][$ext_name];
518 // Rewrit the entry so it can be stored in cache file
519 // @TODO Add support for more types which break in last else-block
520 function rewriteEntry ($key, $value, $prefix = 'data', $single = FALSE) {
521 // Default is not single entry
524 // Add only for single array entry?
525 if ($single === TRUE) {
532 // String or non-string? ;-)
533 if (is_string($value)) {
535 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . " = '" . escapeQuotes($value) . "';";
536 } elseif (is_null($value)) {
538 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = NULL;';
539 } elseif (is_bool($value)) {
541 if ($value === TRUE) {
543 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = TRUE;';
546 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = FALSE;';
548 } elseif (isset($value[0])) {
549 // These lines needs fixing
550 reportBug(__METHOD__, __LINE__, 'Invalid entry with [0] found. key=' . $key);
553 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = ' . $value . ';';
560 // Getter for cache status code
561 function getStatusCode () {
562 return $this->statusCode;
565 // Setter for cache status code
566 function setStatusCode ($status) {
567 $this->statusCode = $status;
570 // Checks whether the current cache file is readable
571 function isCacheReadable () {
572 // Array entry found?
573 if (!isset($this->readable[$this->name])) {
574 // Not found, so create it
575 $this->readable[$this->name] = isFileReadable($this->fqfn);
579 return $this->readable[$this->name];
582 // Cloning not allowed
583 function __clone () {
584 // Please do not clone this class
585 reportBug(__METHOD__, __LINE__, 'Cloning of this class is not allowed.');