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