ed67f4a32789ad60ce93fc3491e8fd924a2b52d5
[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                         $this->writeLine('<?php');
113                 } else {
114                         // Cannot create file
115                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
116                 }
117         }
118
119         /**
120          * Writes a line to the pointer and adds a \n (new-line) to the end
121          *
122          * @access private
123          */
124         function writeLine ($line) {
125                 // Is the pointer a valid resource?
126                 if (is_resource($this->pointer)) {
127                         // Write the line
128                         fwrite($this->pointer, $line . "\n");
129                 } else {
130                         // Something bad happened
131                         debug_report_bug(__METHOD__, __LINE__, 'Pointer type is ' . gettype($this->pointer) . ', expected is resource.');
132                 }
133         }
134
135         // Reset the read status
136         function resetCacheReadStatus () {
137                 unset($this->readable[$this->name]);
138                 unset($GLOBALS['file_readable'][$this->fqfn]);
139                 unset($this->status[$this->name]);
140         }
141
142         function addRow ($data) {
143                 // Is the pointe rvalid?
144                 if (is_resource($this->pointer)) {
145                         // Write every array element to cache file
146                         foreach ($data as $k => $v) {
147                                 // Write global cache array for immediate access
148                                 if ((substr($k, 0, 4) == 'ext_') && (isset($data['ext_name'])) && (isset($data['ext_id']))) {
149                                         if ($k == 'ext_name') {
150                                                 $GLOBALS['cache_array']['extension'][$k][$data['ext_id']] = $v;
151                                         } else {
152                                                 $GLOBALS['cache_array']['extension'][$k][$data['ext_name']] = $v;
153                                         }
154                                         if (($k == 'ext_keep') && ($v == 'Y')) {
155                                                 $GLOBALS['cache_array']['always_active'][$data['ext_name']] = $v;
156                                         } // END - if
157                                 } elseif ($this->name == 'config') {
158                                         // Configuration
159                                         $GLOBALS['cache_array']['config'][$data['config']][$k] = $v;
160                                 } elseif ($this->name == 'filter') {
161                                         // Filter
162                                         $GLOBALS['cache_array']['filter']['chains'][$data['filter_name']][$data['filter_function']] = $data['filter_active'];
163                                         $GLOBALS['cache_array']['filter']['counter'][$data['filter_name']][$data['filter_function']] = $data['filter_counter'];
164                                         $GLOBALS['cache_array']['filter']['loaded'][$data['filter_name']][$data['filter_function']] = true;
165                                 } elseif ($this->name == 'modules') {
166                                         // Modules
167                                         $GLOBALS['cache_array']['modules'][$k][$data['module']] = $v;
168                                 } elseif ($this->name == 'admin') {
169                                         // Admin logins
170                                         if ($k == 'admin_id') {
171                                                 $GLOBALS['cache_array']['admin'][$k][$data['login']] = $v;
172                                         } else {
173                                                 $GLOBALS['cache_array']['admin'][$k][$data['admin_id']] = $v;
174                                         }
175                                 } elseif ($this->name == 'refdepths') {
176                                         // Referal levels
177                                         $GLOBALS['cache_array']['refdepths'][$k][$data['id']] = $v;
178                                 } elseif ($this->name == 'refsystem') {
179                                         // Referal system
180                                         $GLOBALS['cache_array']['refsystem'][$k][$data['id']] = $v;
181                                 } elseif ($this->name == 'revision') {
182                                         // Revision data
183                                         $GLOBALS['cache_array']['revision'][$k][0] = $v;
184                                 } elseif ($this->name == 'themes') {
185                                         // Themes
186                                         if ($k == 'theme_path') {
187                                                 $GLOBALS['cache_array']['themes'][$k][$data['id']] = $v;
188                                         } else {
189                                                 $GLOBALS['cache_array']['themes'][$k][$data['theme_path']] = $v;
190                                         }
191                                 } elseif ($this->name == 'imprint') {
192                                         // Imprint
193                                         $GLOBALS['cache_array']['imprint'][$k][$data['imprint_id']] = $v;
194                                 } elseif (is_array($v)) {
195                                         // Serialize and BASE64-encode the array
196                                         $v = base64_encode(serialize($v));
197                                 } else {
198                                         // Finialize the cache and close it
199                                         $this->finalize();
200
201                                         // Remove cache
202                                         $this->removeCacheFile(true);
203
204                                         // Unsupported cache found!
205                                         debug_report_bug(__METHOD__, __LINE__, 'Unsupported cache ' . $this->name . ' detected.');
206                                 }
207
208                                 // Write cache line to file
209                                 $this->writeLine($this->rewriteEntry($k, $v));
210                         } // END - foreach
211                 } else {
212                         // Cannot create file
213                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
214                 }
215         }
216
217         function finalize () {
218                 // Quit function when no pointer is set
219                 if (is_resource($this->pointer)) {
220                         // Add default depency
221                         $this->storeExtensionVersion('cache');
222
223                         // Write footer
224                         $this->writeLine('?>');
225
226                         // Close file add destroy handler
227                         fclose($this->pointer);
228
229                         // Reset readable status
230                         $this->resetCacheReadStatus();
231
232                         // Set rights
233                         if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
234
235                         // Remove pointer and status
236                         unset($this->status[$this->name]);
237                         $this->pointer = false;
238                         //* DEBUG: */ debugOutput(__METHOD__ . '(<font color="#0000aa">' . __LINE__.'</font>): '.$this->name.' - FINALIZED!');
239                 } // END - if
240         }
241
242         function getArrayFromCache () {
243                 // Is the cache already loaded?
244                 if (isset($this->data[$this->name])) {
245                         // Return it's content!
246                         return $this->data[$this->name];
247                 } // END - if
248
249                 // Is the cache file there?
250                 if ($this->isCacheReadable()) {
251                         // Load cache file
252                         include($this->fqfn);
253
254                         // Is there an array?
255                         if (isset($this->data[$this->name])) {
256                                 // Cache version found?
257                                 if (isset($this->version[$this->name])) {
258                                         // Check it here if cache matches ext-cache version
259                                         if (!$this->extensionVersionMatches('cache')) {
260                                                 // Invalid
261                                                 logDebugMessage(__METHOD__, __LINE__, 'Invalid cache data ' . $this->name . ' detected.');
262                                                 $this->removeCacheFile();
263                                         } // END - if
264                                 } // END - if
265
266                                 // Return cache if detected
267                                 if (isset($this->data[$this->name])) {
268                                         return $this->data[$this->name];
269                                 } else {
270                                         // Damaged!
271                                         logDebugMessage(__METHOD__, __LINE__, 'Possible damaged cache ' . $this->name . ' detected.');
272                                         $this->removeCacheFile();
273                                 }
274                         } // END - if
275                 } else {
276                         // Cache file not found or not readable
277                         debug_report_bug(__METHOD__, __LINE__, $this->name);
278                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): ' . getMaskedMessage('CACHE_CANNOT_LOAD', $this->fqfn));
279
280                         // Try to remove it
281                         $this->removeCacheFile();
282                 }
283
284                 // Always return an empty array if we have trouble or no data
285                 return array();
286         }
287
288         // Destroy an existing cache file
289         function removeCacheFile ($force = false) {
290                 // Reset read status
291                 $this->resetCacheReadStatus();
292
293                 // Debug message
294                 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, sprintf("%s should be removed.", $this->name));
295
296                 // Is the cache file not yet rebuilt?
297                 if ((!isset($this->rebuilt[$this->name])) && ($this->isCacheReadable())) {
298                         // Only run in regular output mode
299                         if ((!isHtmlOutputMode()) && ($force === false)) {
300                                 // Debug message if allowed
301                                 if (isDebugModeEnabled()) {
302                                         // Debug message
303                                         logDebugMessage(__METHOD__, __LINE__, 'Not removing cache ' . $this->name . ' in output_mode=' . getScriptOutputMode());
304                                 } // END - if
305
306                                 // Abort here
307                                 return;
308                         } // END - if
309
310                         // Close cache
311                         $this->finalize();
312
313                         // Debug-mode enabled?
314                         if (isDebugModeEnabled()) {
315                                 // Log removal of cache
316                                 logDebugMessage(__METHOD__, __LINE__, 'removing cache: ' . $this->name);
317                         } // END - if
318
319                         // Remove cache file from system
320                         removeFile($this->fqfn);
321
322                         // Reset read status
323                         $this->resetCacheReadStatus();
324
325                         // Is the file there?
326                         if (!$this->isCacheReadable()) {
327                                 // The cache does no longer exist so kill the content
328                                 unset($this->data[$this->name]);
329                                 unset($this->version[$this->name]);
330                                 $this->rebuilt[$this->name] = true;
331                         } else {
332                                 // Not removed!
333                                 addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): ' . getMaskedMessage('CACHE_CANNOT_UNLINK', $this->fqfn));
334                         }
335                 } // END - if
336         }
337
338         // Unused method:
339         function removeEntry ($search, $data, $array) {
340                 if ($this->status[$this->name] == $this->statusDone) {
341                         // Load cache into dummy array
342                         $dummy = $this->getArrayFromCache();
343
344                         // Search for key in array
345                         $key = array_search($data, $dummy[$search]);
346                         if (!empty($key)) {
347                                 // Key (hopefully) found?
348                                 foreach ($array as $a) {
349                                         // So we can remove all elements as requested
350                                         unset($dummy[$a][$key]);
351                                 } // END - foreach
352
353                                 // Flush array to cache file
354                                 $this->init();
355
356                                 // Write array out
357                                 $this->writeArray($dummy);
358
359                                 // Close cache file
360                                 $this->finalize();
361                         }
362                 } else {
363                         // Cannot write to cache!
364                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
365                 }
366         }
367
368         function writeArray ($array) {
369                 if (is_resource($this->pointer)) {
370                         foreach ($array as $k => $v) {
371                                 if (is_array($v)) {
372                                         // Multi line(s) found
373                                         $LINE = '';
374                                         foreach($v as $k2 => $v2) {
375                                                 // Put every array element in a row...
376                                                 $LINE .= $this->rewriteEntry($k, $v2);
377                                         }
378                                 } else {
379                                         // Single line found
380                                         $LINE = $this->rewriteEntry($k, $v);
381                                 }
382
383                                 // Write line(s)
384                                 $this->writeLine($LINE);
385                         } // END - foreach
386                 } else {
387                         // Cannot write array!
388                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
389                 }
390         }
391
392         // Unused method
393         function replaceEntry ($search, $replace, $search_key, $array) {
394                 if ($this->status[$this->name] == $this->statusDone) {
395                         // Load cache into dummy array
396                         $dummy = $this->getArrayFromCache();
397
398                         // Check if $dummy is valid (prevents some errors)
399                         if ((is_array($dummy)) && (isset($dummy[$search])) && (is_array($dummy[$search]))) {
400                                 // Search for key in array
401                                 $key_found = array_key_exists($search_key, $dummy[$search]);
402                                 if ($key_found == true) {
403                                         $key = $search_key;
404                                         // Key (hopefully) found?
405                                         foreach ($dummy as $a => $v) {
406                                                 // So we can update all entries
407                                                 if ($a == $search) {
408                                                         // Update now...
409                                                         $dummy[$a][$search_key] = $replace;
410                                                 } // END - if
411                                         } // END - foreach
412
413                                         // Flush array to cache file
414                                         $this->init();
415
416                                         // Write array out
417                                         $this->writeArray($dummy);
418
419                                         // Close cache file
420                                         $this->finalize();
421                                 } // END - if
422                         } // END - if
423                 } else {
424                         // Cannot write to cache!
425                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
426                 }
427         }
428
429         // Writes the version of given extension to the cache file
430         function storeExtensionVersion ($ext_name) {
431                 // Valid cache pointer?
432                 if (is_resource($this->pointer)) {
433                         // Write only if extension is installed
434                         if (isExtensionInstalled($ext_name)) {
435                                 // Get extension version
436                                 $ext_ver = getExtensionVersion($ext_name);
437
438                                 // Add the extension version to object (DO NOT REMOVE IT! Endless loop...)
439                                 $this->version[$this->name][$ext_name] = $ext_ver;
440
441                                 // Write cache line to file
442                                 $this->writeLine($this->rewriteEntry($ext_name, $ext_ver, 'version', true));
443                         } // END - if
444                         //* DEBUG: */ debugOutput(__METHOD__ . '(<font color="#0000aa">' . __LINE__ . '</font>): '.$this->name.' - '.$ext_name.'='.$ext_ver);
445                 } else {
446                         // Cannot create file
447                         addFatalMessage(__METHOD__, __LINE__, '(<font color="#0000aa">' . __LINE__ . '</font>): {--CACHE_PROBLEMS_DETECTED');
448                 }
449         }
450
451         // Checks wether versions from cache and extension matches
452         function extensionVersionMatches ($ext_name) {
453                 // Check cache
454                 if (!isset($GLOBALS[__METHOD__][$ext_name])) {
455                         // Does never match by default
456                         $GLOBALS[__METHOD__][$ext_name] = false;
457
458                         // Compare only if installed
459                         if (isExtensionInstalled($ext_name)) {
460                                 // Get extension version
461                                 $ext_ver = getExtensionVersion($ext_name);
462
463                                 // Debug messages
464                                 if (isset($this->version[$this->name][$ext_name])) {
465                                         // Does it match?
466                                         $GLOBALS[__METHOD__][$ext_name] = ((isset($this->version[$this->name][$ext_name])) && ($this->version[$this->name][$ext_name] == $ext_ver));
467                                 } elseif ($this->isCacheReadable()) {
468                                         // No cache version found!
469                                         logDebugMessage(__METHOD__, __LINE__, 'Cache ' . $this->name . ' has missing version entry for extension ' . $ext_name . '! Purging cache...');
470         
471                                         // Remove the cache file
472                                         $this->removeCacheFile(true);
473                                 }
474                         } else {
475                                 // Not installed, does always match
476                                 $GLOBALS[__METHOD__][$ext_name] = true;
477                         }
478                 } else {
479                         // Cache entry found, log debug message
480                         //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'ext_name=' . $ext_name . ', matches=' . intval($GLOBALS[__METHOD__][$ext_name]));
481                 }
482
483                 // Compare both
484                 return $GLOBALS[__METHOD__][$ext_name];
485         }
486
487         // Rewrit the entry so it can be stored in cache file
488         // @TODO Add support for more types which break in last else-block
489         function rewriteEntry ($key, $value, $prefix = 'data', $single = false) {
490                 // Default is not single entry
491                 $extender = '[]';
492
493                 // Add only for single array entry?
494                 if ($single === true) {
495                         $extender = '';
496                 } // END - if
497
498                 // Init line
499                 $line = '';
500
501                 // String or non-string? ;-)
502                 if (is_string($value)) {
503                         // String...
504                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . " = '" . escapeQuotes($value) . "';";
505                 } elseif (is_null($value)) {
506                         // Null
507                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = null;';
508                 } elseif (is_bool($value)) {
509                         // Boolean value
510                         if ($value === true) {
511                                 // True
512                                 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = true;';
513                         } else {
514                                 // False
515                                 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = false;';
516                         }
517                 } elseif (isset($value[0])) {
518                         // These lines needs fixing
519                         debug_report_bug(__METHOD__, __LINE__, 'Invalid entry with [0] found. key=' . $key);
520                 } else {
521                         // Non-string
522                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = ' . $value . ';';
523                 }
524
525                 // Return line
526                 return $line;
527         }
528
529         // Getter for cache status
530         function getStatus () {
531                 return $this->ret;
532         }
533
534         // Checks wether the current cache file is readable
535         function isCacheReadable () {
536                 // Array entry found?
537                 if (!isset($this->readable[$this->name])) {
538                         // Not found, so create it
539                         $this->readable[$this->name] = isFileReadable($this->fqfn);
540                 } // END - if
541
542                 // Return result
543                 return $this->readable[$this->name];
544         }
545
546         // Cloning not allowed
547         function __clone () {
548                 // Please do not clone this class
549                 debug_report_bug(__METHOD__, __LINE__, 'Cloning of this class is not allowed.');
550         }
551 } // END - class
552
553 // [EOF]
554 ?>