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