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