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