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 * -------------------------------------------------------------------- *
13 * Copyright (c) 2003 - 2009 by Roland Haeder *
14 * Copyright (c) 2009 - 2015 by Mailer Developer Team *
15 * For more information visit: http://mxchange.org *
17 * This program is free software; you can redistribute it and/or modify *
18 * it under the terms of the GNU General Public License as published by *
19 * the Free Software Foundation; either version 2 of the License, or *
20 * (at your option) any later version. *
22 * This program is distributed in the hope that it will be useful, *
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
25 * GNU General Public License for more details. *
27 * You should have received a copy of the GNU General Public License *
28 * along with this program; if not, write to the Free Software *
29 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
31 ************************************************************************/
33 // Some security stuff...
34 if (!defined('__SECURITY')) {
41 var $statusCode = 'init';
43 // Full-qualified filename
46 // Resource to cache file
49 // Data array from cache
52 // Version data from cache
53 var $version = array();
57 var $rebuilt = array();
61 var $status = array();
62 var $readable = array();
66 function CacheSystem () {
68 $this->extension = getCacheExtension();
70 // Construct full path
71 $this->fullPath = getPath() . getCachePath();
73 // Failed is the default
74 $this->setStatusCode('failed');
76 // Check if path exists
77 if (isDirectory($this->fullPath)) {
78 // Is there a .htaccess file?
79 if (isFileReadable($this->fullPath . '.htaccess')) {
81 $this->setStatusCode('done');
83 // Stop! Set a .htaccess file first
84 $this->setStatusCode('htaccess');
89 // Checks validity of cache file and if content is given
90 function loadCacheFile ($cacheName) {
91 // Remember cache file
92 $this->name = $cacheName;
94 // Construct FQFN (full qualified file name)
95 $this->fqfn = $this->fullPath . $cacheName . $this->extension;
97 // Check if file exists and if version matches
98 if (!isset($this->status[$cacheName])) {
99 // Pre-fetch cache here if found
100 if ($this->isCacheReadable()) $this->getArrayFromCache();
102 //* DEBUG: */ debugOutput('cacheName='.$cacheName.',isCacheReadable='.intval($this->isCacheReadable()).',is_writeable='.intval(is_writeable($this->fqfn)).',extensionMatches='.intval($this->extensionVersionMatches('cache')));
103 $this->status[$cacheName] = ($this->isCacheReadable() && (is_writeable($this->fqfn)) && ($this->extensionVersionMatches('cache')));
105 //* DEBUG: */ debugOutput('cacheName='.$cacheName.',status='.intval($this->status[$cacheName]));
108 return $this->status[$cacheName];
111 // Initializes the cache file
113 // This will destory an existing cache file!
114 if ($this->getStatusCode() == 'done') {
116 $this->resetCacheReadStatus();
119 if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
120 $this->pointer = fopen($this->fqfn, 'w') or reportBug(__METHOD__, __LINE__, 'Cannot write to cache ' . $this->fqfn . ' !');
123 $this->writeLine('<?php');
125 // Cannot create file
126 reportBug(__METHOD__, __LINE__, 'Problems with cache directory detected, getStatusCode()=' . $this->getStatusCode());
131 * Writes a line to the pointer and adds a \n (new-line) to the end
135 function writeLine ($line) {
136 // Is the pointer a valid resource?
137 if (is_resource($this->pointer)) {
139 fwrite($this->pointer, $line . PHP_EOL);
141 // Something bad happened
142 reportBug(__METHOD__, __LINE__, 'Pointer type is ' . gettype($this->pointer) . ', expected is resource.');
146 // Reset the read status
147 function resetCacheReadStatus () {
148 unset($this->readable[$this->name]);
149 unset($GLOBALS['file_readable'][$this->fqfn]);
150 unset($this->status[$this->name]);
153 // Adds a data row (array) to cache file and global cache array
154 function addRow ($data) {
155 // Is the pointe rvalid?
156 if (is_resource($this->pointer)) {
157 // Write every array element to cache file
158 foreach ($data as $k => $v) {
159 // Write global cache array for immediate access
160 if ((substr($k, 0, 4) == 'ext_') && (isset($data['ext_name'])) && (isset($data['ext_id']))) {
161 if ($k == 'ext_name') {
162 $GLOBALS['cache_array']['extension'][$k][$data['ext_id']] = $v;
164 $GLOBALS['cache_array']['extension'][$k][$data['ext_name']] = $v;
166 if (($k == 'ext_keep') && ($v == 'Y')) {
167 $GLOBALS['cache_array']['always_active'][$data['ext_name']] = $v;
169 } elseif ($this->name == 'config') {
171 $GLOBALS['cache_array']['config'][$data['config']][$k] = $v;
172 } elseif ($this->name == 'filter') {
174 $GLOBALS['cache_array']['filter']['chains'][$data['filter_name']][$data['filter_function']] = $data['filter_active'];
175 $GLOBALS['cache_array']['filter']['counter'][$data['filter_name']][$data['filter_function']] = $data['filter_counter'];
176 $GLOBALS['cache_array']['filter']['loaded'][$data['filter_name']][$data['filter_function']] = TRUE;
177 } elseif ($this->name == 'modules') {
179 $GLOBALS['cache_array']['modules'][$k][$data['module']] = $v;
180 } elseif ($this->name == 'admin') {
182 if ($k == 'admin_id') {
183 $GLOBALS['cache_array']['admin'][$k][$data['login']] = $v;
185 $GLOBALS['cache_array']['admin'][$k][$data['admin_id']] = $v;
187 } elseif ($this->name == 'admin_acls') {
188 // Access control lines
189 array_push($GLOBALS['cache_array']['admin_acls'][$k][$data['admin_id']], $v);
190 } elseif ($this->name == 'refdepths') {
192 $GLOBALS['cache_array']['refdepths'][$k][$data['id']] = $v;
193 } elseif ($this->name == 'refsystem') {
195 $GLOBALS['cache_array']['refsystem'][$k][$data['id']] = $v;
196 } elseif ($this->name == 'themes') {
198 if ($k == 'theme_path') {
199 $GLOBALS['cache_array']['themes'][$k][$data['id']] = $v;
201 $GLOBALS['cache_array']['themes'][$k][$data['theme_path']] = $v;
203 } elseif ($this->name == 'imprint') {
205 $GLOBALS['cache_array']['imprint'][$k][$data['imprint_id']] = $v;
206 } elseif ($this->name == 'points_data') {
207 // Table 'points_data'
208 $GLOBALS['cache_array']['points_data'][$k][$data['id']] = $v;
209 } elseif ($this->name == 'earning') {
211 $GLOBALS['cache_array']['earning'][$k][$data['earning_id']] = $v;
212 } elseif ($this->name == 'payments') {
214 $GLOBALS['cache_array']['payments'][$k][$data['id']] = $v;
215 } elseif (is_array($v)) {
216 // Serialize and BASE64-encode the array
217 $v = base64_encode(serialize($v));
219 // Finialize the cache and close it
223 $this->removeCacheFile(TRUE);
225 // Unsupported/unhandled cache detected
226 reportBug(__METHOD__, __LINE__, 'Unsupported cache ' . $this->name . ' detected, data=' . print_r($data, TRUE) . ',k=' . $k . ',v=' . $v);
229 // Write cache line to file
230 $this->writeLine($this->rewriteEntry($k, $v));
233 // Cannot create file
234 reportBug(__METHOD__, __LINE__, 'Problem with cache detected, no resource! pointer[]=' . gettype($this->pointer));
238 // Closes cache file with closing PHP tag
239 function finalize () {
240 // Quit function when no pointer is set
241 if (is_resource($this->pointer)) {
242 // Add default depency
243 $this->storeExtensionVersion('cache');
246 $this->writeLine('?>');
248 // Close file add destroy handler
249 fclose($this->pointer);
251 // Reset readable status
252 $this->resetCacheReadStatus();
255 if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
257 // Remove pointer and status
258 unset($this->status[$this->name]);
259 $this->pointer = FALSE;
260 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'name=' . $this->name . ' - FINALIZED!');
264 // Loads cache file and returns an array of the cached data
265 function getArrayFromCache () {
266 // Is the cache already loaded?
267 if (isset($this->data[$this->name])) {
268 // Return it's content!
269 return $this->data[$this->name];
272 // Is the cache file there?
273 if ($this->isCacheReadable()) {
275 include($this->fqfn);
277 // Is there an array?
278 if (isset($this->data[$this->name])) {
279 // Cache version found?
280 if (isset($this->version[$this->name])) {
281 // Check it here if cache matches ext-cache version
282 if (!$this->extensionVersionMatches('cache')) {
284 logDebugMessage(__METHOD__, __LINE__, 'Invalid cache data ' . $this->name . ' detected.');
285 $this->removeCacheFile();
289 // Return cache if detected
290 if (isset($this->data[$this->name])) {
291 return $this->data[$this->name];
294 logDebugMessage(__METHOD__, __LINE__, 'Possible damaged cache ' . $this->name . ' detected.');
295 $this->removeCacheFile();
299 // Cache file not found or not readable
300 reportBug(__METHOD__, __LINE__, '{%message,CACHE_CANNOT_LOAD=' . $this->name . '%}');
303 $this->removeCacheFile();
306 // Always return an empty array if we have trouble or no data
310 // Destroy an existing cache file
311 function removeCacheFile ($force = FALSE) {
313 $this->resetCacheReadStatus();
316 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, sprintf('%s should be removed.', $this->name));
318 // Is the cache file not yet rebuilt?
319 if ((!isset($this->rebuilt[$this->name])) && ($this->isCacheReadable())) {
320 // Only run in regular output mode
321 if ((!isHtmlOutputMode()) && ($force === FALSE)) {
322 // Debug message if allowed
323 if (isDebugModeEnabled()) {
325 logDebugMessage(__METHOD__, __LINE__, 'Not removing cache ' . $this->name . ' in output_mode=' . getScriptOutputMode());
335 // Debug-mode enabled?
336 if (isDebugModeEnabled()) {
337 // Log removal of cache
338 logDebugMessage(__METHOD__, __LINE__, 'removing cache: ' . $this->name);
341 // Remove cache file from system
342 //* DEBUG: */ reportBug(__METHOD__, __LINE__, 'About to remove ' . basename($this->fqfn) . '!');
343 removeFile($this->fqfn);
346 $this->resetCacheReadStatus();
348 // Is the file there?
349 if (!$this->isCacheReadable()) {
350 // The cache does no longer exist so kill the content
351 unset($this->data[$this->name]);
352 unset($this->version[$this->name]);
353 $this->rebuilt[$this->name] = TRUE;
356 reportBug(__METHOD__, __LINE__, '{%message,CACHE_CANNOT_UNLINK=' . $this->name . '%}');
362 function removeEntry ($search, $data, $array) {
363 if ($this->status[$this->name] == 'done') {
364 // Load cache into dummy array
365 $dummy = $this->getArrayFromCache();
367 // Search for key in array
368 $key = array_search($data, $dummy[$search]);
370 // Key (hopefully) found?
371 foreach ($array as $a) {
372 // So we can remove all elements as requested
373 unset($dummy[$a][$key]);
376 // Flush array to cache file
380 $this->writeArray($dummy);
386 // Cannot write to cache!
387 reportBug(__METHOD__, __LINE__, 'Problem with cache detected: Unexpected status ' . $this->status[$this->name]);
391 function writeArray ($array) {
392 if (is_resource($this->pointer)) {
393 foreach ($array as $k => $v) {
395 // Multi line(s) found
397 foreach ($v as $k2 => $v2) {
398 // Put every array element in a row...
399 $LINE .= $this->rewriteEntry($k, $v2);
403 $LINE = $this->rewriteEntry($k, $v);
407 $this->writeLine($LINE);
410 // Cannot write array!
411 reportBug(__METHOD__, __LINE__, 'Problem with cache detected: pointer is not resource! pointer[]=' . gettype($this->pointer));
416 function replaceEntry ($search, $replace, $search_key, $array) {
417 if ($this->status[$this->name] == 'done') {
418 // Load cache into dummy array
419 $dummy = $this->getArrayFromCache();
421 // Check if $dummy is valid (prevents some errors)
422 if ((is_array($dummy)) && (isset($dummy[$search])) && (is_array($dummy[$search]))) {
423 // Search for key in array
424 $key_found = array_key_exists($search_key, $dummy[$search]);
425 if ($key_found == TRUE) {
427 // Key (hopefully) found?
428 foreach ($dummy as $a => $v) {
429 // So we can update all entries
432 $dummy[$a][$search_key] = $replace;
436 // Flush array to cache file
440 $this->writeArray($dummy);
447 // Cannot write to cache!
448 reportBug(__METHOD__, __LINE__, 'Problem with cache detected: Unexpected status ' . $this->status[$this->name]);
452 // Writes the version of given extension to the cache file
453 function storeExtensionVersion ($ext_name) {
454 // Valid cache pointer?
455 if (is_resource($this->pointer)) {
456 // Write only if extension is installed
457 if (isExtensionInstalled($ext_name)) {
458 // Get extension version
459 $ext_ver = getExtensionVersion($ext_name);
461 // Add the extension version to object (DO NOT REMOVE IT! Endless loop...)
462 $this->version[$this->name][$ext_name] = $ext_ver;
464 // Write cache line to file
465 $this->writeLine($this->rewriteEntry($ext_name, $ext_ver, 'version', TRUE));
467 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'name=' . $this->name . ',ext_name=' . $ext_name . ',ext_ver=' . $ext_ver);
469 // Cannot create file
470 reportBug(__METHOD__, __LINE__, 'Problem with cache detected: pointer is not resource! pointer[]=' . gettype($this->pointer));
474 // Checks whether versions from cache and extension matches
475 function extensionVersionMatches ($ext_name) {
477 if (!isset($GLOBALS[__METHOD__][$ext_name])) {
478 // Does never match by default
479 $GLOBALS[__METHOD__][$ext_name] = FALSE;
481 // Compare only if installed
482 if (isExtensionInstalled($ext_name)) {
483 // Get extension version
484 $ext_ver = getExtensionVersion($ext_name);
487 if (isset($this->version[$this->name][$ext_name])) {
489 $GLOBALS[__METHOD__][$ext_name] = ($this->version[$this->name][$ext_name] == $ext_ver);
490 } elseif ($this->isCacheReadable()) {
491 // No cache version found
492 logDebugMessage(__METHOD__, __LINE__, 'Cache ' . $this->name . ' has missing version entry for extension ext-' . $ext_name . '! Purging cache...');
494 // Remove the cache file
495 $this->removeCacheFile(TRUE);
498 // Not installed, does always match
499 $GLOBALS[__METHOD__][$ext_name] = TRUE;
502 // Cache entry found, log debug message
503 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'ext_name=' . $ext_name . ', matches=' . intval($GLOBALS[__METHOD__][$ext_name]));
507 return $GLOBALS[__METHOD__][$ext_name];
510 // Rewrit the entry so it can be stored in cache file
511 // @TODO Add support for more types which break in last else-block
512 function rewriteEntry ($key, $value, $prefix = 'data', $single = FALSE) {
513 // Default is not single entry
516 // Add only for single array entry?
517 if ($single === TRUE) {
524 // String or non-string? ;-)
525 if (is_string($value)) {
527 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . " = '" . escapeQuotes($value) . "';";
528 } elseif (is_null($value)) {
530 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = NULL;';
531 } elseif (is_bool($value)) {
533 if ($value === TRUE) {
535 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = TRUE;';
538 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = FALSE;';
540 } elseif (isset($value[0])) {
541 // These lines needs fixing
542 reportBug(__METHOD__, __LINE__, 'Invalid entry with [0] found. key=' . $key);
545 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = ' . $value . ';';
552 // Getter for cache status code
553 function getStatusCode () {
554 return $this->statusCode;
557 // Setter for cache status code
558 function setStatusCode ($status) {
559 $this->statusCode = $status;
562 // Checks whether the current cache file is readable
563 function isCacheReadable () {
564 // Array entry found?
565 if (!isset($this->readable[$this->name])) {
566 // Not found, so create it
567 $this->readable[$this->name] = isFileReadable($this->fqfn);
571 return $this->readable[$this->name];
574 // Cloning not allowed
575 function __clone () {
576 // Please do not clone this class
577 reportBug(__METHOD__, __LINE__, 'Cloning of this class is not allowed.');