Removed comment introduced by Profi-Concept, this comment should fine (in a much...
[mailer.git] / inc / classes / cachesystem.class.php
1 <?php
2 /************************************************************************
3  * Mailer v0.2.1-FINAL                                Start: 10/22/2009 *
4  * ===================                          Last change: 10/22/2009 *
5  *                                                                      *
6  * -------------------------------------------------------------------- *
7  * File              : cachesystem.class.php                            *
8  * -------------------------------------------------------------------- *
9  * Short description : CacheSystem class                                *
10  * -------------------------------------------------------------------- *
11  * Kurzbeschreibung  : CacheSystem-Klasse                               *
12  * -------------------------------------------------------------------- *
13  * $Revision::                                                        $ *
14  * $Date::                                                            $ *
15  * $Tag:: 0.2.1-FINAL                                                 $ *
16  * $Author::                                                          $ *
17  * -------------------------------------------------------------------- *
18  * Copyright (c) 2003 - 2009 by Roland Haeder                           *
19  * Copyright (c) 2009, 2010 by Mailer Developer Team                    *
20  * For more information visit: http://www.mxchange.org                  *
21  *                                                                      *
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.                                  *
26  *                                                                      *
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.                         *
31  *                                                                      *
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,               *
35  * MA  02110-1301  USA                                                  *
36  ************************************************************************/
37
38 // Some security stuff...
39 if (!defined('__SECURITY')) {
40         die();
41 } // END - if
42
43 // Caching class
44 class CacheSystem {
45         // Define variables
46         var $ret = 'init';
47         var $fqfn = '';
48         var $pointer = false;
49         var $data = array();
50         var $version = array();
51         var $name = '';
52         var $rebuilt = array();
53         var $extension = '.cache';
54         var $statusDone = 'done';
55         var $status = array();
56         var $readable = array();
57
58         // Constructor
59         function CacheSystem () {
60                 // Failed is the default
61                 $this->ret = 'failed';
62
63                 // Remeber path
64
65                 // Check if path exists
66                 if (isDirectory(getCachePath())) {
67                         // Is there a .htaccess file?
68                         if (isFileReadable(getCachePath() . '.htaccess')) {
69                                 // All done!
70                                 $this->ret = $this->statusDone;
71                         } else {
72                                 // Stop! Set a .htaccess file first
73                                 $this->ret = 'htaccess';
74                         }
75                 } // END - if
76         }
77
78         // Checks validity of cache file and if content is given
79         function loadCacheFile ($cacheName) {
80                 // Remember cache file
81                 $this->name = $cacheName;
82
83                 // Construct FQFN (full qualified file name)
84                 $this->fqfn = getCachePath() . $cacheName . $this->extension;
85
86                 // Check if file exists and if version matches
87                 if (!isset($this->status[$cacheName])) {
88                         // Pre-fetch cache here if found
89                         if ($this->isCacheReadable()) $this->getArrayFromCache();
90
91                         //* DEBUG: */ debugOutput('cacheName='.$cacheName.',isCacheReadable='.intval($this->isCacheReadable()).',is_writeable='.intval(is_writeable($this->fqfn)).',extensionMatches='.intval($this->extensionVersionMatches('cache')));
92                         $this->status[$cacheName] = ($this->isCacheReadable() && (is_writeable($this->fqfn)) && ($this->extensionVersionMatches('cache')));
93                 } // END - if
94                 //* DEBUG: */ debugOutput('cacheName='.$cacheName.',status='.intval($this->status[$cacheName]));
95
96                 // Return status
97                 return $this->status[$cacheName];
98         }
99
100         // Initializes the cache file
101         function init () {
102                 // This will destory an existing cache file!
103                 if ($this->ret == $this->statusDone) {
104                         // Reset read status
105                         $this->resetCacheReadStatus();
106
107                         // Create file
108                         if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
109                         $this->pointer = fopen($this->fqfn, 'w') or debug_report_bug(__METHOD__, __LINE__, 'Cannot write to cache ' . $this->fqfn . ' !');
110
111                         // Add open PHP tag
112                         fwrite($this->pointer, "<?php\n");
113                 } else {
114                         // Cannot create file
115                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
116                 }
117         }
118
119         // Reset the read status
120         function resetCacheReadStatus () {
121                 unset($this->readable[$this->name]);
122                 unset($GLOBALS['file_readable'][$this->fqfn]);
123                 unset($this->status[$this->name]);
124         }
125
126         function addRow ($data) {
127                 // Is the pointe rvalid?
128                 if (is_resource($this->pointer)) {
129                         // Write every array element to cache file
130                         foreach ($data as $k => $v) {
131                                 // Write global cache array for immediate access
132                                 if ((substr($k, 0, 4) == 'ext_') && (isset($data['ext_name'])) && (isset($data['ext_id']))) {
133                                         if ($k == 'ext_name') {
134                                                 $GLOBALS['cache_array']['extension'][$k][$data['ext_id']] = $v;
135                                         } else {
136                                                 $GLOBALS['cache_array']['extension'][$k][$data['ext_name']] = $v;
137                                         }
138                                         if (($k == 'ext_keep') && ($v == 'Y')) {
139                                                 $GLOBALS['cache_array']['always_active'][$data['ext_name']] = $v;
140                                         } // END - if
141                                 } elseif ($this->name == 'config') {
142                                         // Configuration
143                                         $GLOBALS['cache_array']['config'][$data['config']][$k] = $v;
144                                 } elseif ($this->name == 'filter') {
145                                         // Filter
146                                         $GLOBALS['cache_array']['filter']['chains'][$data['filter_name']][$data['filter_function']] = $data['filter_active'];
147                                         $GLOBALS['cache_array']['filter']['counter'][$data['filter_name']][$data['filter_function']] = $data['filter_counter'];
148                                         $GLOBALS['cache_array']['filter']['loaded'][$data['filter_name']][$data['filter_function']] = true;
149                                 } elseif ($this->name == 'modules') {
150                                         // Modules
151                                         $GLOBALS['cache_array']['modules'][$k][$data['module']] = $v;
152                                 } elseif ($this->name == 'admin') {
153                                         // Admin logins
154                                         if ($k == 'admin_id') {
155                                                 $GLOBALS['cache_array']['admin'][$k][$data['login']] = $v;
156                                         } else {
157                                                 $GLOBALS['cache_array']['admin'][$k][$data['admin_id']] = $v;
158                                         }
159                                 } elseif ($this->name == 'refdepths') {
160                                         // Referal levels
161                                         $GLOBALS['cache_array']['refdepths'][$k][$data['id']] = $v;
162                                 } elseif ($this->name == 'refsystem') {
163                                         // Referal system
164                                         $GLOBALS['cache_array']['refsystem'][$k][$data['id']] = $v;
165                                 } elseif ($this->name == 'revision') {
166                                         // Revision data
167                                         $GLOBALS['cache_array']['revision'][$k][0] = $v;
168                                 } elseif ($this->name == 'themes') {
169                                         // Themes
170                                         if ($k == 'theme_path') {
171                                                 $GLOBALS['cache_array']['themes'][$k][$data['id']] = $v;
172                                         } else {
173                                                 $GLOBALS['cache_array']['themes'][$k][$data['theme_path']] = $v;
174                                         }
175                                 } elseif ($this->name == 'imprint') {
176                                         // Imprint
177                                         $GLOBALS['cache_array']['imprint'][$k][$data['imprint_id']] = $v;
178                                 } elseif (is_array($v)) {
179                                         // Serialize and BASE64-encode the array
180                                         $v = base64_encode(serialize($v));
181                                 } else {
182                                         // Finialize the cache and close it
183                                         $this->finalize();
184
185                                         // Remove cache
186                                         $this->removeCacheFile(true);
187
188                                         // Unsupported cache found!
189                                         debug_report_bug(__METHOD__, __LINE__, 'Unsupported cache ' . $this->name . ' detected.');
190                                 }
191
192                                 // Write cache line to file
193                                 fwrite($this->pointer, $this->rewriteEntry($k, $v));
194                         } // END - foreach
195                 } else {
196                         // Cannot create file
197                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
198                 }
199         }
200
201         function finalize () {
202                 // Quit function when no pointer is set
203                 if (is_resource($this->pointer)) {
204                         // Add default depency
205                         $this->storeExtensionVersion('cache');
206
207                         // Write footer
208                         fwrite($this->pointer, "?>\n");
209
210                         // Close file add destroy handler
211                         fclose($this->pointer);
212
213                         // Reset readable status
214                         $this->resetCacheReadStatus();
215
216                         // Set rights
217                         if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
218
219                         // Remove pointer and status
220                         unset($this->status[$this->name]);
221                         $this->pointer = false;
222                         //* DEBUG: */ debugOutput(__METHOD__ . '(<font color="#0000aa">' . __LINE__.'</font>): '.$this->name.' - FINALIZED!');
223                 } // END - if
224         }
225
226         function getArrayFromCache () {
227                 // Is the cache already loaded?
228                 if (isset($this->data[$this->name])) {
229                         // Return it's content!
230                         return $this->data[$this->name];
231                 } // END - if
232
233                 // Is the cache file there?
234                 if ($this->isCacheReadable()) {
235                         // Load cache file
236                         include($this->fqfn);
237
238                         // Is there an array?
239                         if (isset($this->data[$this->name])) {
240                                 // Cache version found?
241                                 if (isset($this->version[$this->name])) {
242                                         // Check it here if cache matches ext-cache version
243                                         if (!$this->extensionVersionMatches('cache')) {
244                                                 // Invalid
245                                                 logDebugMessage(__METHOD__, __LINE__, 'Invalid cache data ' . $this->name . ' detected.');
246                                                 $this->removeCacheFile();
247                                         } // END - if
248                                 } // END - if
249
250                                 // Return cache if detected
251                                 if (isset($this->data[$this->name])) {
252                                         return $this->data[$this->name];
253                                 } else {
254                                         // Damaged!
255                                         logDebugMessage(__METHOD__, __LINE__, 'Possible damaged cache ' . $this->name . ' detected.');
256                                         $this->removeCacheFile();
257                                 }
258                         } // END - if
259                 } else {
260                         // Cache file not found or not readable
261                         debug_report_bug(__METHOD__, __LINE__, $this->name);
262                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): ' . getMaskedMessage('CACHE_CANNOT_LOAD', $this->fqfn));
263
264                         // Try to remove it
265                         $this->removeCacheFile();
266                 }
267
268                 // Always return an empty array if we have trouble or no data
269                 return array();
270         }
271
272         // Destroy an existing cache file
273         function removeCacheFile ($force = false) {
274                 // Reset read status
275                 $this->resetCacheReadStatus();
276
277                 // Debug message
278                 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, sprintf("%s should be removed.", $this->name));
279
280                 // Is the cache file not yet rebuilt?
281                 if ((!isset($this->rebuilt[$this->name])) && ($this->isCacheReadable())) {
282                         // Only run in regular output mode
283                         if ((!isHtmlOutputMode()) && ($force === false)) {
284                                 // Debug message if allowed
285                                 if (isDebugModeEnabled()) {
286                                         // Debug message
287                                         logDebugMessage(__METHOD__, __LINE__, 'Not removing cache ' . $this->name . ' in output_mode=' . getScriptOutputMode());
288                                 } // END - if
289
290                                 // Abort here
291                                 return;
292                         } // END - if
293
294                         // Close cache
295                         $this->finalize();
296
297                         // Debug-mode enabled?
298                         if (isDebugModeEnabled()) {
299                                 // Log removal of cache
300                                 logDebugMessage(__METHOD__, __LINE__, 'removing cache: ' . $this->name);
301                         } // END - if
302
303                         // Remove cache file from system
304                         removeFile($this->fqfn);
305
306                         // Reset read status
307                         $this->resetCacheReadStatus();
308
309                         // Is the file there?
310                         if (!$this->isCacheReadable()) {
311                                 // The cache does no longer exist so kill the content
312                                 unset($this->data[$this->name]);
313                                 unset($this->version[$this->name]);
314                                 $this->rebuilt[$this->name] = true;
315                         } else {
316                                 // Not removed!
317                                 addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): ' . getMaskedMessage('CACHE_CANNOT_UNLINK', $this->fqfn));
318                         }
319                 } // END - if
320         }
321
322         // Unused method:
323         function removeEntry ($search, $data, $array) {
324                 if ($this->status[$this->name] == $this->statusDone) {
325                         // Load cache into dummy array
326                         $dummy = $this->getArrayFromCache();
327
328                         // Search for key in array
329                         $key = array_search($data, $dummy[$search]);
330                         if (!empty($key)) {
331                                 // Key (hopefully) found?
332                                 foreach ($array as $a) {
333                                         // So we can remove all elements as requested
334                                         unset($dummy[$a][$key]);
335                                 } // END - foreach
336
337                                 // Flush array to cache file
338                                 $this->init();
339
340                                 // Write array out
341                                 $this->writeArray($dummy);
342
343                                 // Close cache file
344                                 $this->finalize();
345                         }
346                 } else {
347                         // Cannot write to cache!
348                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
349                 }
350         }
351
352         function writeArray ($array) {
353                 if (is_resource($this->pointer)) {
354                         foreach ($array as $k => $v) {
355                                 if (is_array($v)) {
356                                         // Multi line(s) found
357                                         $LINE = '';
358                                         foreach($v as $k2 => $v2) {
359                                                 // Put every array element in a row...
360                                                 $LINE .= $this->rewriteEntry($k, $v2);
361                                         }
362                                 } else {
363                                         // Single line found
364                                         $LINE = $this->rewriteEntry($k, $v);
365                                 }
366
367                                 // Write line(s)
368                                 fwrite($this->pointer, $LINE);
369                         } // END - foreach
370                 } else {
371                         // Cannot write array!
372                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
373                 }
374         }
375
376         // Unused method
377         function replaceEntry ($search, $replace, $search_key, $array) {
378                 if ($this->status[$this->name] == $this->statusDone) {
379                         // Load cache into dummy array
380                         $dummy = $this->getArrayFromCache();
381
382                         // Check if $dummy is valid (prevents some errors)
383                         if ((is_array($dummy)) && (isset($dummy[$search])) && (is_array($dummy[$search]))) {
384                                 // Search for key in array
385                                 $key_found = array_key_exists($search_key, $dummy[$search]);
386                                 if ($key_found == true) {
387                                         $key = $search_key;
388                                         // Key (hopefully) found?
389                                         foreach ($dummy as $a => $v) {
390                                                 // So we can update all entries
391                                                 if ($a == $search) {
392                                                         // Update now...
393                                                         $dummy[$a][$search_key] = $replace;
394                                                 } // END - if
395                                         } // END - foreach
396
397                                         // Flush array to cache file
398                                         $this->init();
399
400                                         // Write array out
401                                         $this->writeArray($dummy);
402
403                                         // Close cache file
404                                         $this->finalize();
405                                 } // END - if
406                         } // END - if
407                 } else {
408                         // Cannot write to cache!
409                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
410                 }
411         }
412
413         // Writes the version of given extension to the cache file
414         function storeExtensionVersion ($ext_name) {
415                 // Valid cache pointer?
416                 if (is_resource($this->pointer)) {
417                         // Write only if extension is installed
418                         if (isExtensionInstalled($ext_name)) {
419                                 // Get extension version
420                                 $ext_ver = getExtensionVersion($ext_name);
421
422                                 // Add the extension version to object (DO NOT REMOVE IT! Endless loop...)
423                                 $this->version[$this->name][$ext_name] = $ext_ver;
424
425                                 // Write cache line to file
426                                 fwrite($this->pointer, $this->rewriteEntry($ext_name, $ext_ver, 'version', true));
427                         } // END - if
428                         //* DEBUG: */ debugOutput(__METHOD__ . '(<font color="#0000aa">' . __LINE__ . '</font>): '.$this->name.' - '.$ext_name.'='.$ext_ver);
429                 } else {
430                         // Cannot create file
431                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
432                 }
433         }
434
435         // Checks wether versions from cache and extension matches
436         function extensionVersionMatches ($ext_name) {
437                 // Check cache
438                 if (!isset($GLOBALS[__METHOD__][$ext_name])) {
439                         // Does never match by default
440                         $GLOBALS[__METHOD__][$ext_name] = false;
441
442                         // Compare only if installed
443                         if (isExtensionInstalled($ext_name)) {
444                                 // Get extension version
445                                 $ext_ver = getExtensionVersion($ext_name);
446
447                                 // Debug messages
448                                 if (isset($this->version[$this->name][$ext_name])) {
449                                         // Does it match?
450                                         $GLOBALS[__METHOD__][$ext_name] = ((isset($this->version[$this->name][$ext_name])) && ($this->version[$this->name][$ext_name] == $ext_ver));
451                                 } elseif ($this->isCacheReadable()) {
452                                         // No cache version found!
453                                         logDebugMessage(__METHOD__, __LINE__, 'Cache ' . $this->name . ' has missing version entry for extension ' . $ext_name . '! Purging cache...');
454         
455                                         // Remove the cache file
456                                         $this->removeCacheFile(true);
457                                 }
458                         } else {
459                                 // Not installed, does always match
460                                 $GLOBALS[__METHOD__][$ext_name] = true;
461                         }
462                 } else {
463                         // Cache entry found, log debug message
464                         //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'ext_name=' . $ext_name . ', matches=' . intval($GLOBALS[__METHOD__][$ext_name]));
465                 }
466
467                 // Compare both
468                 return $GLOBALS[__METHOD__][$ext_name];
469         }
470
471         // Rewrit the entry so it can be stored in cache file
472         // @TODO Add support for more types which break in last else-block
473         function rewriteEntry ($key, $value, $prefix = 'data', $single = false) {
474                 // Default is not single entry
475                 $extender = '[]';
476
477                 // Add only for single array entry?
478                 if ($single === true) $extender = '';
479
480                 // Init line
481                 $line = '';
482
483                 // String or non-string? ;-)
484                 if (is_string($value)) {
485                         // String...
486                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . " = '" . escapeQuotes($value) . "';\n";
487                 } elseif (is_null($value)) {
488                         // Null
489                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . " = null;\n";
490                 } elseif (is_bool($value)) {
491                         // Boolean value
492                         if ($value === true) {
493                                 // True
494                                 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . " = true;\n";
495                         } else {
496                                 // False
497                                 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . " = false;\n";
498                         }
499                 } elseif (isset($value[0])) {
500                         // These lines needs fixing
501                         debug_report_bug(__METHOD__, __LINE__, 'Invalid entry with [0] found. key=' . $key);
502                 } else {
503                         // Non-string
504                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = ' . $value . ";\n";
505                 }
506
507                 // Return line
508                 return $line;
509         }
510
511         // Getter for cache status
512         function getStatus () {
513                 return $this->ret;
514         }
515
516         // Checks wether the current cache file is readable
517         function isCacheReadable () {
518                 // Array entry found?
519                 if (!isset($this->readable[$this->name])) {
520                         // Not found, so create it
521                         $this->readable[$this->name] = isFileReadable($this->fqfn);
522                 } // END - if
523
524                 // Return result
525                 return $this->readable[$this->name];
526         }
527
528 } // END - class
529
530 // [EOF]
531 ?>