Caching of filters added (should work now)
[mailer.git] / inc / libs / cache_functions.php
1 <?php
2 /************************************************************************
3  * MXChange v0.2.1                                    Start: 10/11/2003 *
4  * ===============                              Last change: 10/11/2003 *
5  *                                                                      *
6  * -------------------------------------------------------------------- *
7  * File              : admins_functions.php                             *
8  * -------------------------------------------------------------------- *
9  * Short description : Functions for the admins extension               *
10  * -------------------------------------------------------------------- *
11  * Kurzbeschreibung  : Funktionen fuer die admins-Erweiterung           *
12  * -------------------------------------------------------------------- *
13  * $Revision::                                                        $ *
14  * $Date::                                                            $ *
15  * $Tag:: 0.2.1-FINAL                                                 $ *
16  * $Author::                                                          $ *
17  * Needs to be in all Files and every File needs "svn propset           *
18  * svn:keywords Date Revision" (autoprobset!) at least!!!!!!            *
19  * -------------------------------------------------------------------- *
20  * Copyright (c) 2003 - 2008 by Roland Haeder                           *
21  * For more information visit: http://www.mxchange.org                  *
22  *                                                                      *
23  * This program is free software; you can redistribute it and/or modify *
24  * it under the terms of the GNU General Public License as published by *
25  * the Free Software Foundation; either version 2 of the License, or    *
26  * (at your option) any later version.                                  *
27  *                                                                      *
28  * This program is distributed in the hope that it will be useful,      *
29  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
30  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
31  * GNU General Public License for more details.                         *
32  *                                                                      *
33  * You should have received a copy of the GNU General Public License    *
34  * along with this program; if not, write to the Free Software          *
35  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,               *
36  * MA  02110-1301  USA                                                  *
37  ************************************************************************/
38
39 // Some security stuff...
40 if (!defined('__SECURITY')) {
41         $INC = substr(dirname(__FILE__), 0, strpos(dirname(__FILE__), '/inc') + 4) . '/security.php';
42         require($INC);
43 }
44
45 // Caching class
46 class CacheSystem {
47         // Define variables
48         var $ret = 'init';
49         var $path = '';
50         var $inc = '';
51         var $fqfn = '';
52         var $pointer = false;
53         var $data = array();
54         var $version = array();
55         var $name = '';
56         var $rebuilt = array();
57         var $extension = '.cache';
58         var $statusDone = 'done';
59
60         // Constructor
61         function CacheSystem ($interval, $path, $tested) {
62                 // Failed is the default
63                 $this->ret = 'failed';
64
65                 // Remeber path
66                 $this->path = $path;
67
68                 // Check if path exists
69                 if ((isDirectory($path)) && ($tested === false)) {
70                         // Make FQFN for dummy file
71                         $fqfn = $path . 'dummy.tmp';
72
73                         // Check if we can create a file inside the path
74                         touch($fqfn, 'w');
75
76                         // Is the file there?
77                         if (isFileReadable($fqfn)) {
78                                 // Yes, we can do. So let's remove it
79                                 removeFile($fqfn);
80
81                                 // Is there a .htaccess file?
82                                 if (isFileReadable($path . '.htaccess')) {
83                                         // Update database that we have tested it
84                                         updateConfiguration('cache_tested', 1);
85
86                                         // All done!
87                                         $this->ret = $this->statusDone;
88                                 } else {
89                                         // Stop! Set a .htaccess file first
90                                         $this->ret = 'htaccess';
91                                 }
92                         }
93                 } elseif ($tested) {
94                         // System already tested
95                         $this->ret = $this->statusDone;
96                 }
97         }
98
99         // Checks validity of cache file and if content is given
100         function loadCacheFile ($cacheName, $forceContent = false) {
101                 // Remember cache file
102                 $this->name = $cacheName;
103
104                 // Construct include filename for loadIncludeOnce() call
105                 $this->inc = $this->path . $cacheName . $this->extension;
106
107                 // Construct FQFN (full qualified file name)
108                 $this->fqfn = constant('PATH') . $this->inc;
109
110                 // Check if file exists and if version matches
111                 $status = ($this->isCacheReadable() && (is_writeable($this->fqfn)) && ($this->extensionVersionMatches('cache')));
112
113                 // Return status
114                 return $status;
115         }
116
117         // Initializes the cache file
118         function init () {
119                 // This will destory an existing cache file!
120                 if ($this->ret == $this->statusDone) {
121                         // Create file
122                         if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
123                         $this->pointer = fopen($this->fqfn, 'w') or app_die(__METHOD__, __LINE__, "Cannot write to cache ".$this->fqfn." !");
124
125                         // Add open PHP tag
126                         fwrite($this->pointer, "<?php\n");
127
128                         // Add default depency
129                         $this->storeExtensionVersion('cache');
130                 } else {
131                         // Cannot create file
132                         addFatalMessage(__METHOD__, __LINE__, "(<font color=\"#0000aa\">".__LINE__."</font>): ".getMessage('CACHE_PROBLEMS_DETECTED'));
133                 }
134         }
135
136         function addRow ($data) {
137                 // Is the pointe rvalid?
138                 if (is_resource($this->pointer)) {
139                         // Write every array element to cache file
140                         foreach ($data as $k => $v) {
141                                 // Write global cache array for immediate access
142                                 if ((substr($k, 0, 4) == 'ext_') && (isset($data['ext_name'])) && (isset($data['ext_id']))) {
143                                         if ($k != 'ext_name') {
144                                                 $GLOBALS['cache_array']['extensions'][$k][$data['ext_name']] = $v;
145                                         } else {
146                                                 $GLOBALS['cache_array']['extensions'][$k][$data['ext_id']] = $v;
147                                         }
148                                         if (($k == 'ext_keep') && ($v == 'Y')) {
149                                                 $GLOBALS['cache_array']['active_extensions'][$data['ext_name']] = $v;
150                                         } // END - if
151                                 } elseif (is_array($v)) {
152                                         // Serialize and BASE64-encode the array
153                                         $v = base64_encode(serialize($v));
154                                 }
155
156                                 // Write cache line to file
157                                 fwrite($this->pointer, $this->rewriteEntry($k, $v));
158                         }
159                 } else {
160                         // Cannot create file
161                         addFatalMessage(__METHOD__, __LINE__, "(<font color=\"#0000aa\">".__LINE__."</font>): ".getMessage('CACHE_PROBLEMS_DETECTED'));
162                 }
163         }
164
165         function finalize () {
166                 // Quit function when no pointer is set
167                 if (is_resource($this->pointer)) {
168                         // Write footer
169                         fwrite($this->pointer, "?>\n");
170
171                         // Close file add destroy handler
172                         fclose($this->pointer);
173
174                         // Set rights
175                         if ($this->isCacheReadable()) changeMode($this->fqfn, 0666);
176
177                         // Remove pointer
178                         $this->pointer = false;
179                         //* DEBUG: */ print __METHOD__."(<font color=\"#0000aa\">".__LINE__."</font>): {$this->name} - FINALIZED!<br />\n";
180                 } // END - if
181         }
182
183         function getArrayFromCache () {
184                 // Is the cache already loaded?
185                 if (isset($this->data[$this->name])) {
186                         // Return it's content!
187                         return $this->data[$this->name];
188                 } // END - if
189
190                 // Is the cache file there?
191                 if ($this->isCacheReadable()) {
192                         // Prepare temporary array
193                         $data = array();
194                         $cache_version = null;
195
196                         // Load cache file
197                         require($this->inc);
198
199                         // Is there an array?
200                         if (is_array($data)) {
201                                 // Cache data
202                                 $this->data[$this->name] = $data;
203
204                                 // Cache version found?
205                                 if ((is_array($cache_version)) && (count($cache_version) > 0)) {
206                                         // Remember it as well...
207                                         $this->version[$this->name] = $cache_version;
208                                 } else {
209                                         // Invalid cache so destroy it
210                                         $this->destroyCacheFile();
211
212                                         // Clear cached data
213                                         $this->data[$this->name] = array();
214                                 }
215
216                                 // Return cache
217                                 return $this->data[$this->name];
218                         } else {
219                                 // Cache problem detected!
220                                 $this->destroyCacheFile();
221                         }
222                 } else {
223                         // Cache file not found or not readable
224                         addFatalMessage(__METHOD__, __LINE__, "(<font color=\"#0000aa\">".__LINE__."</font>): ".CACHE_CANNOT_LOAD_1.$this->fqfn.CACHE_CANNOT_LOAD_2);
225                 }
226         }
227
228         // Destroy an existing cache file
229         function destroyCacheFile ($removeArray = false, $force = false) {
230                 // Only run in regular output mode
231                 if ((getOutputMode() != 0) && ($force === false)) {
232                         // Debug message if allowed
233                         if (isDebugModeEnabled()) {
234                                 // Debug message
235                                 debug_report_bug('Not removing cache ' . $this->name . ' in output_mode=' . getOutputMode());
236                         } // END - if
237
238                         // Abort here
239                         return;
240                 } // END - if
241
242                 // Is the cache file there?
243                 if ((!isset($this->rebuilt[$this->name])) && ($this->isCacheReadable())) {
244                         // Close cache
245                         $this->finalize();
246
247                         // Debug-mode enabled?
248                         if (isDebugModeEnabled()) {
249                                 // Log removal of cache
250                                 DEBUG_LOG(__METHOD__, __LINE__, 'removing cache: ' . $this->name);
251                         } // END - if
252
253                         // Remove cache file from system
254                         removeFile($this->fqfn);
255
256                         // Shall we remove the array from memory?
257                         if ($removeArray === true) {
258                                 // Debug message if allowed
259                                 if (isDebugModeEnabled()) {
260                                         // Debug message
261                                         DEBUG_LOG(__METHOD__, __LINE__, 'removing array!');
262                                 } // END - if
263
264                                 // Remove it from memory
265                                 unset($GLOBALS['cache_array'][$this->name]);
266                         } // END - if
267
268                         // Is the file there?
269                         if (!$this->isCacheReadable()) {
270                                 // The cache does no longer exist so kill the content
271                                 unset($this->data[$this->name]);
272                                 unset($this->version[$this->name]);
273                                 $this->rebuilt[$this->name] = true;
274                         } else {
275                                 // Not removed!
276                                 addFatalMessage(__METHOD__, __LINE__, "(<font color=\"#0000aa\">".__LINE__."</font>): ".CACHE_CANNOT_UNLINK_1.$this->fqfn.CACHE_CANNOT_UNLINK_2);
277                         }
278                 } // END - if
279         }
280
281         // Unused method:
282         function removeEntry ($search, $data, $array) {
283                 if (($this->isCacheReadable()) && (is_writeable($this->fqfn))) {
284                         // Load cache into dummy array
285                         $dummy = $this->getArrayFromCache();
286
287                         // Search for key in array
288                         $key = array_search($data, $dummy[$search]);
289                         if (!empty($key)) {
290                                 // Key (hopefully) found?
291                                 foreach ($array as $a) {
292                                         // So we can remove all elements as requested
293                                         unset($dummy[$a][$key]);
294                                 } // END - foreach
295
296                                 // Flush array to cache file
297                                 $this->init();
298
299                                 // Write array out
300                                 $this->writeArray($dummy);
301
302                                 // Close cache file
303                                 $this->finalize();
304                         }
305                 } else {
306                         // Cannot write to cache!
307                         addFatalMessage(__METHOD__, __LINE__, "(<font color=\"#0000aa\">".__LINE__."</font>): ".getMessage('CACHE_PROBLEMS_DETECTED'));
308                 }
309         }
310
311         function writeArray ($array) {
312                 if (is_resource($this->pointer)) {
313                         foreach ($array as $k => $v) {
314                                 if (is_array($v)) {
315                                         // Multi line(s) found
316                                         $LINE = '';
317                                         foreach($v as $k2 => $v2) {
318                                                 // Put every array element in a row...
319                                                 $LINE .= $this->rewriteEntry($k, $v2);
320                                         }
321                                 } else {
322                                         // Single line found
323                                         $LINE = $this->rewriteEntry($k, $v);
324                                 }
325
326                                 // Write line(s)
327                                 fwrite($this->pointer, $LINE);
328                         } // END - foreach
329                 } else {
330                         // Cannot write array!
331                         addFatalMessage(__METHOD__, __LINE__, "(<font color=\"#0000aa\">".__LINE__."</font>): ".getMessage('CACHE_PROBLEMS_DETECTED'));
332                 }
333         }
334
335         // Unused method
336         function replaceEntry ($search, $replace, $search_key, $array) {
337                 if (($this->isCacheReadable()) && (is_writeable($this->fqfn))) {
338                         // Load cache into dummy array
339                         $dummy = $this->getArrayFromCache();
340
341                         // Check if $dummy is valid (prevents some errors)
342                         if ((is_array($dummy)) && (isset($dummy[$search])) && (is_array($dummy[$search]))) {
343                                 // Search for key in array
344                                 $key_found = array_key_exists($search_key, $dummy[$search]);
345                                 if ($key_found == true) {
346                                         $key = $search_key;
347                                         // Key (hopefully) found?
348                                         foreach ($dummy as $a => $v) {
349                                                 // So we can update all entries
350                                                 if ($a == $search) {
351                                                         // Update now...
352                                                         $dummy[$a][$search_key] = $replace;
353                                                 } // END - if
354                                         } // END - foreach
355
356                                         // Flush array to cache file
357                                         $this->init();
358
359                                         // Write array out
360                                         $this->writeArray($dummy);
361
362                                         // Close cache file
363                                         $this->finalize();
364                                 } // END - if
365                         } // END - if
366                 } else {
367                         // Cannot write to cache!
368                         addFatalMessage(__METHOD__, __LINE__, "(<font color=\"#0000aa\">".__LINE__."</font>): ".getMessage('CACHE_PROBLEMS_DETECTED'));
369                 }
370         }
371
372         // Writes the version of given extension to the cache file
373         function storeExtensionVersion ($ext_name) {
374                 // Valid cache pointer?
375                 if (is_resource($this->pointer)) {
376                         // Get extension version
377                         $ext_ver = GET_EXT_VERSION($ext_name);
378
379                         // Write cache line to file
380                         fwrite($this->pointer, "\$cache_version['".$ext_name."'] = \"".$ext_ver."\";\n");
381
382                         // Add the extension version to object (DO NOT REMOVE IT! Endless loop...)
383                         $this->version[$this->name][$ext_name] = $ext_ver;
384                         //* DEBUG: */ print __METHOD__."(<font color=\"#0000aa\">".__LINE__."</font>): {$this->name} - {$ext_name}={$ext_ver}<br />\n";
385                 } else {
386                         // Cannot create file
387                         addFatalMessage(__METHOD__, __LINE__, "(<font color=\"#0000aa\">".__LINE__."</font>): ".getMessage('CACHE_PROBLEMS_DETECTED'));
388                 }
389         }
390
391         // Checks wether versions from cache and extension matches
392         function extensionVersionMatches ($ext_name) {
393                 // Load cache (dummy)
394                 $this->getArrayFromCache();
395
396                 // Get extension version
397                 $ext_ver = GET_EXT_VERSION($ext_name);
398
399                 // Debug messages
400                 if (isset($this->version[$this->name][$ext_name])) {
401                         //* DEBUG: */ print __METHOD__."(<font color=\"#0000aa\">".__LINE__."</font>): cache={$this->name},ext_name={$ext_name} - {$ext_ver}/{$this->version[$this->name][$ext_name]}<br />\n";
402                 } else {
403                         // No cache version found!
404                         DEBUG_LOG(__METHOD__, __LINE__, "Cache {$this->name} has missing version entry for extension {$ext_name}!");
405                 }
406
407                 // Compare both
408                 return ((isset($this->version[$this->name][$ext_name])) && ($this->version[$this->name][$ext_name] == $ext_ver));
409         }
410
411         // Rewrit the entry so it can be stored in cache file
412         // @TODO Add support for more types which break in last else-block
413         function rewriteEntry ($key, $value) {
414                 // Init line
415                 $line = '';
416
417                 // String or non-string? ;-)
418                 if (is_string($value)) {
419                         // String...
420                         $line = "\$data['".$key."'][] = \"".$value."\";\n";
421                 } elseif (is_null($value)) {
422                         // Null
423                         $line = "\$data['".$key."'][] = null;\n";
424                 } elseif (is_bool($value)) {
425                         // Boolean value
426                         if ($value === true) {
427                                 // True
428                                 $line = "\$data['".$key."'][] = true;\n";
429                         } else {
430                                 // False
431                                 $line = "\$data['".$key."'][] = false;\n";
432                         }
433                 } else {
434                         // Non-string
435                         $line = "\$data['".$key."'][] = ".$value.";\n";
436                 }
437
438                 // Return line
439                 return $line;
440         }
441
442         // Getter for cache status
443         function getStatus () {
444                 return $this->ret;
445         }
446
447         // Checks wether the current cache file is readable
448         function isCacheReadable () {
449                 return isIncludeReadable($this->inc);
450         }
451
452 } // END - class
453
454 // Destroy the cache on extension changes
455 function FILTER_CACHE_DESTROY_ON_EXT_CHANGE ($data) {
456         // Return the data anyway if there is no cache extension
457         if (!isCacheInstanceValid()) return $data;
458
459         // Remove cache
460         if ($GLOBALS['cache_instance']->loadCacheFile('config'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
461         if ($GLOBALS['cache_instance']->loadCacheFile('extensions'))  $GLOBALS['cache_instance']->destroyCacheFile(false);
462         if ($GLOBALS['cache_instance']->loadCacheFile('modreg'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
463
464         // Return it
465         return $data;
466 }
467
468 // Destroy the cache on changing admin
469 function FILTER_CACHE_DESTROY_ON_ADMIN_CHANGE () {
470         // Skip this step if the cache instance is not there
471         if (!isCacheInstanceValid()) return false;
472
473         // Remove cache
474         if ($GLOBALS['cache_instance']->loadCacheFile('admins'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
475 }
476
477 // Destroy all cache files
478 function FILTER_CACHE_DESTROY_ALL () {
479         // Skip this step if the cache instance is not there
480         //* DEBUG: */ DEBUG_LOG(__FUNCTION__, __LINE__, 'Called!');
481         if (!isCacheInstanceValid()) return false;
482
483         // Remove cache files
484         if ($GLOBALS['cache_instance']->loadCacheFile('admins'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
485         if ($GLOBALS['cache_instance']->loadCacheFile('admins_acls')) $GLOBALS['cache_instance']->destroyCacheFile(false);
486         if ($GLOBALS['cache_instance']->loadCacheFile('config'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
487         if ($GLOBALS['cache_instance']->loadCacheFile('extensions'))  $GLOBALS['cache_instance']->destroyCacheFile(false);
488         if ($GLOBALS['cache_instance']->loadCacheFile('modreg'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
489         if ($GLOBALS['cache_instance']->loadCacheFile('refdepths'))   $GLOBALS['cache_instance']->destroyCacheFile(false);
490         if ($GLOBALS['cache_instance']->loadCacheFile('refsystem'))   $GLOBALS['cache_instance']->destroyCacheFile(false);
491         if ($GLOBALS['cache_instance']->loadCacheFile('themes'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
492         if ($GLOBALS['cache_instance']->loadCacheFile('revision'))    $GLOBALS['cache_instance']->destroyCacheFile(false);
493         if ($GLOBALS['cache_instance']->loadCacheFile('filter'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
494         //* DEBUG: */ DEBUG_LOG(__FUNCTION__, __LINE__, 'Done!');
495 }
496
497 // Filter for purging 'filter' cache
498 function FILTER_CACHE_DESTROY_FILTER () {
499         // Skip this step if the cache instance is not there
500         if ((!isCacheInstanceValid()) || (getConfig('update_filter_usage') == 'N')) return false;
501
502         // Remove cache files
503         if ($GLOBALS['cache_instance']->loadCacheFile('filter'))      $GLOBALS['cache_instance']->destroyCacheFile(false);
504 }
505
506 // Filter for purging entire admin menu cache
507 function FILTER_CACHE_PURGE_ADMIN_MENU () {
508         // Just call the function
509         cachePurgeAdminMenu();
510 }
511
512 //
513 ?>