No more PHP4:
[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  * Copyright (c) 2003 - 2009 by Roland Haeder                           *
14  * Copyright (c) 2009 - 2016 by Mailer Developer Team                   *
15  * For more information visit: http://mxchange.org                      *
16  *                                                                      *
17  * This program is free software; you can redistribute it and/or modify *
18  * it under the terms of the GNU General Public License as published by *
19  * the Free Software Foundation; either version 2 of the License, or    *
20  * (at your option) any later version.                                  *
21  *                                                                      *
22  * This program is distributed in the hope that it will be useful,      *
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
25  * GNU General Public License for more details.                         *
26  *                                                                      *
27  * You should have received a copy of the GNU General Public License    *
28  * along with this program; if not, write to the Free Software          *
29  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,               *
30  * MA  02110-1301  USA                                                  *
31  ************************************************************************/
32
33 // Some security stuff...
34 if (!defined('__SECURITY')) {
35         die();
36 } // END - if
37
38 // Caching class
39 class CacheSystem {
40         // Status code
41         public $statusCode = 'init';
42
43         // Full-qualified filename
44         public $fqfn = '';
45
46         // Resource to cache file
47         public $pointer = FALSE;
48
49         // Data array from cache
50         public $data = array();
51
52         // Version data from cache
53         public $version = array();
54
55         // Cache name
56         public $name = '';
57         public $rebuilt = array();
58
59         // File extension
60         public $extension = '';
61         public $status = array();
62         public $readable = array();
63         public $fullPath = '';
64
65         // Constructor
66         public function __construct () {
67                 // Set extension
68                 $this->extension = getCacheExtension();
69
70                 // Construct full path
71                 $this->fullPath = getPath() . getCachePath();
72
73                 // Failed is the default
74                 $this->setStatusCode('failed');
75
76                 // Check if path exists
77                 if (isDirectory($this->fullPath)) {
78                         // Is there a .htaccess file?
79                         if (isFileReadable($this->fullPath . '.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 = $this->fullPath . $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 reportBug(__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                         reportBug(__METHOD__, __LINE__, 'Problems with cache directory detected, getStatusCode()=' . $this->getStatusCode());
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 . PHP_EOL);
140                 } else {
141                         // Something bad happened
142                         reportBug(__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         // Adds a data row (array) to cache file and global cache array
154         function addRow ($data) {
155                 // Is the pointe rvalid?
156                 if (is_resource($this->pointer)) {
157                         // Write every array element to cache file
158                         foreach ($data as $k => $v) {
159                                 // Write global cache array for immediate access
160                                 if ((substr($k, 0, 4) == 'ext_') && (isset($data['ext_name'])) && (isset($data['ext_id']))) {
161                                         if ($k == 'ext_name') {
162                                                 $GLOBALS['cache_array']['extension'][$k][$data['ext_id']] = $v;
163                                         } else {
164                                                 $GLOBALS['cache_array']['extension'][$k][$data['ext_name']] = $v;
165                                         }
166                                         if (($k == 'ext_keep') && ($v == 'Y')) {
167                                                 $GLOBALS['cache_array']['always_active'][$data['ext_name']] = $v;
168                                         } // END - if
169                                 } elseif ($this->name == 'config') {
170                                         // Configuration
171                                         $GLOBALS['cache_array']['config'][$data['config']][$k] = $v;
172                                 } elseif ($this->name == 'filter') {
173                                         // Filter
174                                         $GLOBALS['cache_array']['filter']['chains'][$data['filter_name']][$data['filter_function']]  = $data['filter_active'];
175                                         $GLOBALS['cache_array']['filter']['counter'][$data['filter_name']][$data['filter_function']] = $data['filter_counter'];
176                                         $GLOBALS['cache_array']['filter']['loaded'][$data['filter_name']][$data['filter_function']]  = TRUE;
177                                 } elseif ($this->name == 'modules') {
178                                         // Modules
179                                         $GLOBALS['cache_array']['modules'][$k][$data['module']] = $v;
180                                 } elseif ($this->name == 'admin') {
181                                         // Admin logins
182                                         if ($k == 'admin_id') {
183                                                 $GLOBALS['cache_array']['admin'][$k][$data['login']] = $v;
184                                         } else {
185                                                 $GLOBALS['cache_array']['admin'][$k][$data['admin_id']] = $v;
186                                         }
187                                 } elseif ($this->name == 'admin_acls') {
188                                         // Access control lines
189                                         array_push($GLOBALS['cache_array']['admin_acls'][$k][$data['admin_id']], $v);
190                                 } elseif ($this->name == 'refdepths') {
191                                         // Referral levels
192                                         $GLOBALS['cache_array']['refdepths'][$k][$data['id']] = $v;
193                                 } elseif ($this->name == 'refsystem') {
194                                         // Referral system
195                                         $GLOBALS['cache_array']['refsystem'][$k][$data['id']] = $v;
196                                 } elseif ($this->name == 'themes') {
197                                         // Themes
198                                         if ($k == 'theme_path') {
199                                                 $GLOBALS['cache_array']['themes'][$k][$data['id']] = $v;
200                                         } else {
201                                                 $GLOBALS['cache_array']['themes'][$k][$data['theme_path']] = $v;
202                                         }
203                                 } elseif ($this->name == 'imprint') {
204                                         // Imprint
205                                         $GLOBALS['cache_array']['imprint'][$k][$data['imprint_id']] = $v;
206                                 } elseif ($this->name == 'points_data') {
207                                         // Table 'points_data'
208                                         $GLOBALS['cache_array']['points_data'][$k][$data['id']] = $v;
209                                 } elseif ($this->name == 'earning') {
210                                         // Table 'earning'
211                                         $GLOBALS['cache_array']['earning'][$k][$data['earning_id']] = $v;
212                                 } elseif ($this->name == 'payments') {
213                                         // Table 'payments'
214                                         $GLOBALS['cache_array']['payments'][$k][$data['id']] = $v;
215                                 } elseif (is_array($v)) {
216                                         // Serialize and BASE64-encode the array
217                                         $v = base64_encode(serialize($v));
218                                 } else {
219                                         // Finialize the cache and close it
220                                         $this->finalize();
221
222                                         // Remove cache
223                                         $this->removeCacheFile(TRUE);
224
225                                         // Unsupported/unhandled cache detected
226                                         reportBug(__METHOD__, __LINE__, 'Unsupported cache ' . $this->name . ' detected, data=' . print_r($data, TRUE) . ',k=' . $k . ',v=' . $v);
227                                 }
228
229                                 // Write cache line to file
230                                 $this->writeLine($this->rewriteEntry($k, $v));
231                         } // END - foreach
232                 } else {
233                         // Cannot create file
234                         reportBug(__METHOD__, __LINE__, 'Problem with cache detected, no resource! pointer[]=' . gettype($this->pointer));
235                 }
236         }
237
238         // Closes cache file with closing PHP tag
239         function finalize () {
240                 // Quit function when no pointer is set
241                 if (is_resource($this->pointer)) {
242                         // Add default depency
243                         $this->storeExtensionVersion('cache');
244
245                         // Write footer
246                         $this->writeLine('?>');
247
248                         // Close file add destroy handler
249                         fclose($this->pointer);
250
251                         // Reset readable status
252                         $this->resetCacheReadStatus();
253
254                         // Set rights
255                         if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
256
257                         // Remove pointer and status
258                         unset($this->status[$this->name]);
259                         $this->pointer = FALSE;
260                         //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'name=' . $this->name . ' - FINALIZED!');
261                 } // END - if
262         }
263
264         // Loads cache file and returns an array of the cached data
265         function getArrayFromCache () {
266                 // Is the cache already loaded?
267                 if (isset($this->data[$this->name])) {
268                         // Return it's content!
269                         return $this->data[$this->name];
270                 } // END - if
271
272                 // Is the cache file there?
273                 if ($this->isCacheReadable()) {
274                         // Load cache file
275                         include($this->fqfn);
276
277                         // Is there an array?
278                         if (isset($this->data[$this->name])) {
279                                 // Cache version found?
280                                 if (isset($this->version[$this->name])) {
281                                         // Check it here if cache matches ext-cache version
282                                         if (!$this->extensionVersionMatches('cache')) {
283                                                 // Invalid
284                                                 logDebugMessage(__METHOD__, __LINE__, 'Invalid cache data ' . $this->name . ' detected.');
285                                                 $this->removeCacheFile();
286                                         } // END - if
287                                 } // END - if
288
289                                 // Return cache if detected
290                                 if (isset($this->data[$this->name])) {
291                                         return $this->data[$this->name];
292                                 } else {
293                                         // Damaged!
294                                         logDebugMessage(__METHOD__, __LINE__, 'Possible damaged cache ' . $this->name . ' detected.');
295                                         $this->removeCacheFile();
296                                 }
297                         } // END - if
298                 } else {
299                         // Cache file not found or not readable
300                         reportBug(__METHOD__, __LINE__, '{%message,CACHE_CANNOT_LOAD=' . $this->name . '%}');
301
302                         // Try to remove it
303                         $this->removeCacheFile();
304                 }
305
306                 // Always return an empty array if we have trouble or no data
307                 return array();
308         }
309
310         // Destroy an existing cache file
311         function removeCacheFile ($force = FALSE) {
312                 // Reset read status
313                 $this->resetCacheReadStatus();
314
315                 // Debug message
316                 //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, sprintf('%s should be removed.', $this->name));
317
318                 // Is the cache file not yet rebuilt?
319                 if ((!isset($this->rebuilt[$this->name])) && ($this->isCacheReadable())) {
320                         // Only run in regular output mode
321                         if ((!isHtmlOutputMode()) && ($force === FALSE)) {
322                                 // Debug message if allowed
323                                 if (isDebugModeEnabled()) {
324                                         // Debug message
325                                         logDebugMessage(__METHOD__, __LINE__, 'Not removing cache ' . $this->name . ' in output_mode=' . getScriptOutputMode());
326                                 } // END - if
327
328                                 // Abort here
329                                 return;
330                         } // END - if
331
332                         // Close cache
333                         $this->finalize();
334
335                         // Debug-mode enabled?
336                         if (isDebugModeEnabled()) {
337                                 // Log removal of cache
338                                 logDebugMessage(__METHOD__, __LINE__, 'removing cache: ' . $this->name);
339                         } // END - if
340
341                         // Remove cache file from system
342                         //* DEBUG: */ reportBug(__METHOD__, __LINE__, 'About to remove ' . basename($this->fqfn) . '!');
343                         removeFile($this->fqfn);
344
345                         // Reset read status
346                         $this->resetCacheReadStatus();
347
348                         // Is the file there?
349                         if (!$this->isCacheReadable()) {
350                                 // The cache does no longer exist so kill the content
351                                 unset($this->data[$this->name]);
352                                 unset($this->version[$this->name]);
353                                 $this->rebuilt[$this->name] = TRUE;
354                         } else {
355                                 // Not removed!
356                                 reportBug(__METHOD__, __LINE__, '{%message,CACHE_CANNOT_UNLINK=' . $this->name . '%}');
357                         }
358                 } // END - if
359         }
360
361         // Unused method:
362         function removeEntry ($search, $data, $array) {
363                 if ($this->status[$this->name] == 'done') {
364                         // Load cache into dummy array
365                         $dummy = $this->getArrayFromCache();
366
367                         // Search for key in array
368                         $key = array_search($data, $dummy[$search]);
369                         if (!empty($key)) {
370                                 // Key (hopefully) found?
371                                 foreach ($array as $a) {
372                                         // So we can remove all elements as requested
373                                         unset($dummy[$a][$key]);
374                                 } // END - foreach
375
376                                 // Flush array to cache file
377                                 $this->init();
378
379                                 // Write array out
380                                 $this->writeArray($dummy);
381
382                                 // Close cache file
383                                 $this->finalize();
384                         } // END - if
385                 } else {
386                         // Cannot write to cache!
387                         reportBug(__METHOD__, __LINE__, 'Problem with cache detected: Unexpected status ' . $this->status[$this->name]);
388                 }
389         }
390
391         function writeArray ($array) {
392                 if (is_resource($this->pointer)) {
393                         foreach ($array as $k => $v) {
394                                 if (is_array($v)) {
395                                         // Multi line(s) found
396                                         $LINE = '';
397                                         foreach ($v as $k2 => $v2) {
398                                                 // Put every array element in a row...
399                                                 $LINE .= $this->rewriteEntry($k, $v2);
400                                         } // END - foreach
401                                 } else {
402                                         // Single line found
403                                         $LINE = $this->rewriteEntry($k, $v);
404                                 }
405
406                                 // Write line(s)
407                                 $this->writeLine($LINE);
408                         } // END - foreach
409                 } else {
410                         // Cannot write array!
411                         reportBug(__METHOD__, __LINE__, 'Problem with cache detected: pointer is not resource! pointer[]=' . gettype($this->pointer));
412                 }
413         }
414
415         // Unused method
416         function replaceEntry ($search, $replace, $search_key, $array) {
417                 if ($this->status[$this->name] == 'done') {
418                         // Load cache into dummy array
419                         $dummy = $this->getArrayFromCache();
420
421                         // Check if $dummy is valid (prevents some errors)
422                         if ((is_array($dummy)) && (isset($dummy[$search])) && (is_array($dummy[$search]))) {
423                                 // Search for key in array
424                                 $key_found = array_key_exists($search_key, $dummy[$search]);
425                                 if ($key_found == TRUE) {
426                                         $key = $search_key;
427                                         // Key (hopefully) found?
428                                         foreach ($dummy as $a => $v) {
429                                                 // So we can update all entries
430                                                 if ($a == $search) {
431                                                         // Update now...
432                                                         $dummy[$a][$search_key] = $replace;
433                                                 } // END - if
434                                         } // END - foreach
435
436                                         // Flush array to cache file
437                                         $this->init();
438
439                                         // Write array out
440                                         $this->writeArray($dummy);
441
442                                         // Close cache file
443                                         $this->finalize();
444                                 } // END - if
445                         } // END - if
446                 } else {
447                         // Cannot write to cache!
448                         reportBug(__METHOD__, __LINE__, 'Problem with cache detected: Unexpected status ' . $this->status[$this->name]);
449                 }
450         }
451
452         // Writes the version of given extension to the cache file
453         function storeExtensionVersion ($ext_name) {
454                 // Valid cache pointer?
455                 if (is_resource($this->pointer)) {
456                         // Write only if extension is installed
457                         if (isExtensionInstalled($ext_name)) {
458                                 // Get extension version
459                                 $ext_ver = getExtensionVersion($ext_name);
460
461                                 // Add the extension version to object (DO NOT REMOVE IT! Endless loop...)
462                                 $this->version[$this->name][$ext_name] = $ext_ver;
463
464                                 // Write cache line to file
465                                 $this->writeLine($this->rewriteEntry($ext_name, $ext_ver, 'version', TRUE));
466                         } // END - if
467                         //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'name=' . $this->name . ',ext_name=' . $ext_name . ',ext_ver=' . $ext_ver);
468                 } else {
469                         // Cannot create file
470                         reportBug(__METHOD__, __LINE__, 'Problem with cache detected: pointer is not resource! pointer[]=' . gettype($this->pointer));
471                 }
472         }
473
474         // Checks whether versions from cache and extension matches
475         function extensionVersionMatches ($ext_name) {
476                 // Check cache
477                 if (!isset($GLOBALS[__METHOD__][$ext_name])) {
478                         // Does never match by default
479                         $GLOBALS[__METHOD__][$ext_name] = FALSE;
480
481                         // Compare only if installed
482                         if (isExtensionInstalled($ext_name)) {
483                                 // Get extension version
484                                 $ext_ver = getExtensionVersion($ext_name);
485
486                                 // Debug messages
487                                 if (isset($this->version[$this->name][$ext_name])) {
488                                         // Does it match?
489                                         $GLOBALS[__METHOD__][$ext_name] = ($this->version[$this->name][$ext_name] == $ext_ver);
490                                 } elseif ($this->isCacheReadable()) {
491                                         // No cache version found
492                                         logDebugMessage(__METHOD__, __LINE__, 'Cache ' . $this->name . ' has missing version entry for extension ext-' . $ext_name . '! Purging cache...');
493         
494                                         // Remove the cache file
495                                         $this->removeCacheFile(TRUE);
496                                 }
497                         } else {
498                                 // Not installed, does always match
499                                 $GLOBALS[__METHOD__][$ext_name] = TRUE;
500                         }
501                 } else {
502                         // Cache entry found, log debug message
503                         //* DEBUG: */ logDebugMessage(__METHOD__, __LINE__, 'ext_name=' . $ext_name . ', matches=' . intval($GLOBALS[__METHOD__][$ext_name]));
504                 }
505
506                 // Compare both
507                 return $GLOBALS[__METHOD__][$ext_name];
508         }
509
510         // Rewrit the entry so it can be stored in cache file
511         // @TODO Add support for more types which break in last else-block
512         function rewriteEntry ($key, $value, $prefix = 'data', $single = FALSE) {
513                 // Default is not single entry
514                 $extender = '[]';
515
516                 // Add only for single array entry?
517                 if ($single === TRUE) {
518                         $extender = '';
519                 } // END - if
520
521                 // Init line
522                 $line = '';
523
524                 // String or non-string? ;-)
525                 if (is_string($value)) {
526                         // String...
527                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . " = '" . escapeQuotes($value) . "';";
528                 } elseif (is_null($value)) {
529                         // Null
530                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = NULL;';
531                 } elseif (is_bool($value)) {
532                         // Boolean value
533                         if ($value === TRUE) {
534                                 // True
535                                 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = TRUE;';
536                         } else {
537                                 // False
538                                 $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = FALSE;';
539                         }
540                 } elseif (isset($value[0])) {
541                         // These lines needs fixing
542                         reportBug(__METHOD__, __LINE__, 'Invalid entry with [0] found. key=' . $key);
543                 } else {
544                         // Non-string
545                         $line = '$this->' . $prefix . "['" . $this->name . "']['" . $key . "']" . $extender . ' = ' . $value . ';';
546                 }
547
548                 // Return line
549                 return $line;
550         }
551
552         // Getter for cache status code
553         function getStatusCode () {
554                 return $this->statusCode;
555         }
556
557         // Setter for cache status code
558         function setStatusCode ($status) {
559                 $this->statusCode = $status;
560         }
561
562         // Checks whether the current cache file is readable
563         function isCacheReadable () {
564                 // Array entry found?
565                 if (!isset($this->readable[$this->name])) {
566                         // Not found, so create it
567                         $this->readable[$this->name] = isFileReadable($this->fqfn);
568                 } // END - if
569
570                 // Return result
571                 return $this->readable[$this->name];
572         }
573
574         // Cloning not allowed
575         function __clone () {
576                 // Please do not clone this class
577                 reportBug(__METHOD__, __LINE__, 'Cloning of this class is not allowed.');
578         }
579 } // END - class
580
581 // [EOF]
582 ?>