]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/FileFinder.php
* optimize increaseHitCount, esp. for mysql.
[SourceForge/phpwiki.git] / lib / FileFinder.php
1 <?php rcs_id('$Id: FileFinder.php,v 1.30 2004-11-10 19:32:21 rurban Exp $');
2
3 require_once(dirname(__FILE__).'/stdlib.php');
4
5 /**
6  * A class for finding files.
7  * 
8  * This should really provided by pear. We don't want really to mess around 
9  * with all the lousy systems. (WindowsNT, Win95, Mac, VMS, ...)
10  * But pear has only System and File, which do nothing.
11  * Anyway, in good PHP style we ignore the rest of the world and try to behave 
12  * as on unix only. That means we use / as pathsep in all our constants.
13  */
14 class FileFinder
15 {
16     //var $_pathsep, $_path;
17
18     /**
19      * Constructor.
20      *
21      * @param $path array A list of directories in which to search for files.
22      */
23     function FileFinder ($path = false) {
24         $this->_pathsep = $this->_get_syspath_separator();
25         if (!isset($this->_path) and $path === false)
26             $path = $this->_get_include_path();
27         $this->_path = $path;
28     }
29
30     /**
31      * Find file.
32      *
33      * @param $file string File to search for.
34      * @return string The filename (including path), if found, otherwise false.
35      */
36     function findFile ($file, $missing_okay = false) {
37         if ($this->_is_abs($file)) {
38             if (file_exists($file))
39                 return $file;
40         }
41         elseif ( ($dir = $this->_search_path($file)) ) {
42             return $dir . $this->_use_path_separator($dir) . $file;
43         }
44         return $missing_okay ? false : $this->_not_found($file);
45     }
46
47     /**
48      * Unify used pathsep character.
49      * Accepts array of paths also.
50      * This might not work on Windows95 or FAT volumes. (not tested)
51      */
52     function slashifyPath ($path) {
53         return $this->forcePathSlashes($path, $this->_pathsep);
54     }
55
56     /**
57      * Force using '/' as path seperator.
58      */
59     function forcePathSlashes ($path, $sep='/') {
60         if (is_array($path)) {
61             $result = array();
62             foreach ($path as $dir) { $result[] = $this->forcePathSlashes($dir,$sep); }
63             return $result;
64         } else {
65             if (isWindows() or $this->_isOtherPathsep()) {
66                 if (isMac()) $from = ":";
67                 elseif (isWindows()) $from = "\\";
68                 else $from = "\\";
69                 // PHP is stupid enough to use \\ instead of \
70                 if (isWindows()) {
71                     if (substr($path,0,2) != '\\\\')
72                         $path = str_replace('\\\\','\\',$path);
73                     else // UNC paths
74                         $path = '\\\\' . str_replace('\\\\','\\',substr($path,2));
75                 }
76                 return strtr($path, $from, $sep);
77             } else 
78                 return $path;
79         }
80     }
81
82     /**
83      * Try to include file.
84      *
85      * If file is found in the path, then the files directory is added
86      * to PHP's include_path (if it's not already there.) Then the
87      * file is include_once()'d.
88      *
89      * @param $file string File to include.
90      * @return bool True if file was successfully included.
91      */
92     function includeOnce ($file) {
93         if ( ($ret = @include_once($file)) )
94             return $ret;
95
96         if (!$this->_is_abs($file)) {
97             if ( ($dir = $this->_search_path($file)) && is_file($dir . $this->_pathsep . $file)) {
98                 $this->_append_to_include_path($dir);
99                 return include_once($file);
100             }
101         }
102         return $this->_not_found($file);
103     }
104
105     function _isOtherPathsep() {
106         return $this->_pathsep != '/';
107     }
108
109     /**
110      * The system-dependent path-separator character. 
111      * UNIX,WindowsNT,MacOSX: /
112      * Windows95: \
113      * Mac:       :
114      *
115      * @access private
116      * @return string path_separator.
117      */
118     function _get_syspath_separator () {
119         if (!empty($this->_pathsep)) return $this->_pathsep;
120         elseif (isWindowsNT()) return "/"; // we can safely use '/'
121         elseif (isWindows()) return "\\";  // FAT might use '\'
122         elseif (isMac()) return ':';    // MacOsX is /
123         // VMS or LispM is really weird, we ignore it.
124         else return '/';
125     }
126
127     /**
128      * The path-separator character of the given path. 
129      * Windows accepts "/" also, but gets confused with mixed path_separators,
130      * e.g "C:\Apache\phpwiki/locale/button"
131      * > dir "C:\Apache\phpwiki/locale/button" => 
132      *       Parameterformat nicht korrekt - "locale"
133      * So if there's any '\' in the path, either fix them to '/' (not in Win95 or FAT?) 
134      * or use '\' for ours.
135      *
136      * @access private
137      * @return string path_separator.
138      */
139     function _use_path_separator ($path) {
140         if (isWindows95()) {
141             if (empty($path)) return "\\";
142             else return (strchr($path,"\\")) ? "\\" : '/';
143         } elseif (isMac()) {
144             if (empty($path)) return ":";
145             else return (strchr($path,":")) ? ":" : '/';
146         } else {
147             return $this->_get_syspath_separator();
148         }
149     }
150
151     /**
152      * Determine if path is absolute.
153      *
154      * @access private
155      * @param $path string Path.
156      * @return bool True if path is absolute. 
157      */
158     function _is_abs($path) {
159         if (ereg('^/', $path)) return true;
160         elseif (isWindows() and (eregi('^[a-z]:[/\\]', $path))) return true;
161         else return false;
162     }
163
164     /**
165      * Strip ending '/' or '\' from path.
166      *
167      * @access private
168      * @param $path string Path.
169      * @return bool New path (destructive)
170      */
171     function _strip_last_pathchar(&$path) {
172         if (isMac()) {
173             if (substr($path,-1) == ':' or substr($path,-1) == "/") 
174                 $path = substr($path,0,-1);
175         } else {
176             if (substr($path,-1) == '/' or substr($path,-1) == "\\") 
177                 $path = substr($path,0,-1);
178         }
179         return $path;
180     }
181
182     /**
183      * Report a "file not found" error.
184      *
185      * @access private
186      * @param $file string Name of missing file.
187      * @return bool false.
188      */
189     function _not_found($file) {
190         if (function_exists("_"))
191             trigger_error(sprintf(_("%s: file not found"), $file), E_USER_ERROR);
192         else
193             trigger_error(sprintf("%s: file not found", $file), E_USER_ERROR);
194         return false;
195     }
196
197
198     /**
199      * Search our path for a file.
200      *
201      * @access private
202      * @param $file string File to find.
203      * @return string Directory which contains $file, or false.
204      * [5x,44ms]
205      */
206     function _search_path ($file) {
207         foreach ($this->_path as $dir) {
208             // ensure we use the same pathsep
209             if ($this->_isOtherPathsep()) {
210                 $dir = $this->slashifyPath($dir);
211                 $file = $this->slashifyPath($file);
212                 if (file_exists($dir . $this->_pathsep . $file))
213                     return $dir;
214             } elseif (@file_exists($dir . $this->_pathsep . $file))
215                 return $dir;
216         }
217         return false;
218     }
219
220     /**
221      * The system-dependent path-separator character. On UNIX systems,
222      * this character is ':'; on Win32 systems it is ';'.
223      * Fixme:
224      * On Mac it cannot be : because this is the seperator there!
225      *
226      * @access private
227      * @return string path_separator.
228      */
229     function _get_ini_separator () {
230         return isWindows() ? ';' : ':';
231         // return preg_match('/^Windows/', php_uname()) 
232     }
233
234     /**
235      * Get the value of PHP's include_path.
236      *
237      * @access private
238      * @return array Include path.
239      */
240     function _get_include_path() {
241         if (defined("INCLUDE_PATH"))
242             $path = INCLUDE_PATH;
243         else {
244             $path = @get_cfg_var('include_path'); // FIXME: report warning
245             if (empty($path)) $path = @ini_get('include_path');
246         }
247         if (empty($path))
248             $path = '.';
249         return explode($this->_get_ini_separator(), $this->slashifyPath($path));
250     }
251
252     /**
253      * Add a directory to the end of PHP's include_path.
254      *
255      * The directory is appended only if it is not already listed in
256      * the include_path.
257      *
258      * @access private
259      * @param $dir string Directory to add.
260      */
261     function _append_to_include_path ($dir) {
262         $dir = $this->slashifyPath($dir);
263         if (!in_array($dir, $this->_path)) {
264             $this->_path[] = $dir;
265         }
266         /*
267          * Some (buggy) PHP's (notable SourceForge's PHP 4.0.6)
268          * sometimes don't seem to heed their include_path.
269          * I.e. sometimes a file is not found even though it seems to
270          * be in the current include_path. A simple
271          * ini_set('include_path', ini_get('include_path')) seems to
272          * be enough to fix the problem
273          *
274          * This following line should be in the above if-block, but we
275          * put it here, as it seems to work-around the bug.
276          */
277         ini_set('include_path', implode($this->_get_ini_separator(), $this->_path));
278     }
279
280     /**
281      * Add a directory to the front of PHP's include_path.
282      *
283      * The directory is prepended, and removed from the tail if already existing.
284      *
285      * @access private
286      * @param $dir string Directory to add.
287      */
288     function _prepend_to_include_path ($dir) {
289         $dir = $this->slashifyPath($dir);
290         // remove duplicates
291         if ($i = array_search($dir, $this->_path) !== false) {
292             array_splice($this->_path, $i, 1);
293         }
294         array_unshift($this->_path, $dir);
295         ini_set('include_path', implode($this->_path, $this->_get_ini_separator()));
296     }
297
298     // Return all the possible shortened locale specifiers for the given locale.
299     // Most specific first.
300     // de_DE.iso8859-1@euro => de_DE.iso8859-1, de_DE, de
301     // This code might needed somewhere else also.
302     function locale_versions ($lang) {
303         // Try less specific versions of the locale
304         $langs[] = $lang;
305         foreach (array('@', '.', '_') as $sep) {
306             if ( ($tail = strchr($lang, $sep)) )
307                 $langs[] = substr($lang, 0, -strlen($tail));
308         }
309         return $langs;
310     }
311
312     /**
313      * Try to figure out the appropriate value for $LANG.
314      *
315      *@access private
316      *@return string The value of $LANG.
317      */
318     function _get_lang() {
319         if (!empty($GLOBALS['LANG']))
320             return $GLOBALS['LANG'];
321
322         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
323             $lang = setlocale(constant($var), 0);
324             if (!empty($lang))
325                 return $lang;
326         }
327             
328         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
329             $lang = getenv($var);
330             if (!empty($lang))
331                 return $lang;
332         }
333
334         return "C";
335     }
336 }
337
338 /**
339  * A class for finding PEAR code.
340  *
341  * This is a subclass of FileFinder which searches a standard list of
342  * directories where PEAR code is likely to be installed.
343  *
344  * Example usage:
345  *
346  * <pre>
347  *   $pearFinder = new PearFileFinder;
348  *   $pearFinder->includeOnce('DB.php');
349  * </pre>
350  *
351  * The above code will look for 'DB.php', if found, the directory in
352  * which it was found will be added to PHP's include_path, and the
353  * file will be included. (If the file is not found, and E_USER_ERROR
354  * will be thrown.)
355  */
356 class PearFileFinder
357     extends FileFinder
358 {
359     /**
360      * Constructor.
361      *
362      * @param $path array Where to look for PEAR library code.
363      * A good set of defaults is provided, so you can probably leave
364      * this parameter blank.
365      */
366     function PearFileFinder ($path = array()) {
367         $this->FileFinder(array_merge(
368                           $path,
369                           array('/usr/share/php4',
370                                 '/usr/share/php',
371                                 '/usr/lib/php4',
372                                 '/usr/lib/php',
373                                 '/usr/local/share/php4',
374                                 '/usr/local/share/php',
375                                 '/usr/local/lib/php4',
376                                 '/usr/local/lib/php',
377                                 '/System/Library/PHP',
378                                 '/Apache/pear'        // Windows
379                                 )));
380     }
381 }
382
383 /**
384  * Find PhpWiki localized files.
385  *
386  * This is a subclass of FileFinder which searches PHP's include_path
387  * for files. It looks first for "locale/$LANG/$file", then for
388  * "$file".
389  *
390  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
391  * also search under various less specific variations like
392  * "de_DE.iso8859-1", "de_DE" and "de".
393  */
394 class LocalizedFileFinder
395 extends FileFinder
396 {
397     /**
398      * Constructor.
399      */
400     function LocalizedFileFinder () {
401         $this->_pathsep = $this->_get_syspath_separator();
402         $include_path = $this->_get_include_path();
403         $path = array();
404
405         $lang = $this->_get_lang();
406         assert(!empty($lang));
407
408         if ($locales = $this->locale_versions($lang)) {
409             foreach ($locales as $lang) {
410                 if ($lang == 'C') $lang = 'en';
411                 foreach ($include_path as $dir) {
412                     $path[] = $this->slashifyPath($dir . "/locale/$lang");
413                 }
414             }
415         }
416         $this->FileFinder(array_merge($path, $include_path));
417     }
418 }
419
420 /**
421  * Find PhpWiki localized theme buttons.
422  *
423  * This is a subclass of FileFinder which searches PHP's include_path
424  * for files. It looks first for "buttons/$LANG/$file", then for
425  * "$file".
426  *
427  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
428  * also search under various less specific variations like
429  * "de_DE.iso8859-1", "de_DE" and "de".
430  */
431 class LocalizedButtonFinder
432 extends FileFinder
433 {
434     /**
435      * Constructor.
436      */
437     function LocalizedButtonFinder () {
438         global $WikiTheme;
439         $this->_pathsep = $this->_get_syspath_separator();
440         $include_path = $this->_get_include_path();
441         $path = array();
442
443         $lang = $this->_get_lang();
444         assert(!empty($lang));
445         assert(!empty($WikiTheme));
446
447         if (is_object($WikiTheme)) {
448             $langs = $this->locale_versions($lang);
449             foreach ($langs as $lang) {
450                 if ($lang == 'C') $lang = 'en';
451                 foreach ($include_path as $dir) {
452                     $path[] = $this->slashifyPath($WikiTheme->file("buttons/$lang"));
453                 }
454             }
455         }
456
457         $this->FileFinder(array_merge($path, $include_path));
458     }
459 }
460
461 // Search PHP's include_path to find file or directory.
462 function FindFile ($file, $missing_okay = false, $slashify = false)
463 {
464     static $finder;
465     if (!isset($finder)) {
466         $finder = new FileFinder;
467         // remove "/lib" from dirname(__FILE__)
468         $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
469         // let the system favor its local pear?
470         $finder->_append_to_include_path(dirname(__FILE__)."/pear");
471         $finder->_prepend_to_include_path($wikidir);
472         // Don't override existing INCLUDE_PATH config.
473         if (!defined("INCLUDE_PATH"))
474             define("INCLUDE_PATH", implode($finder->_get_ini_separator(), $finder->_path));
475     }
476     $s = $finder->findFile($file, $missing_okay);
477     if ($slashify)
478         $s = $finder->slashifyPath($s);
479     return $s;
480 }
481
482 // Search PHP's include_path to find file or directory.
483 // Searches for "locale/$LANG/$file", then for "$file".
484 function FindLocalizedFile ($file, $missing_okay = false, $re_init = false)
485 {
486     static $finder;
487     if ($re_init or !isset($finder))
488         $finder = new LocalizedFileFinder;
489     return $finder->findFile($file, $missing_okay);
490 }
491
492 function FindLocalizedButtonFile ($file, $missing_okay = false, $re_init = false)
493 {
494     static $buttonfinder;
495     if ($re_init or !isset($buttonfinder))
496         $buttonfinder = new LocalizedButtonFinder;
497     return $buttonfinder->findFile($file, $missing_okay);
498 }
499
500 /** 
501  * Prefixes with PHPWIKI_DIR and slashify.
502  * For example to unify with 
503  *   require_once dirname(__FILE__).'/lib/file.php'
504  *   require_once 'lib/file.php' loading style.
505  * Doesn't expand "~" or symlinks yet. truename would be perfect.
506  *
507  * NormalizeLocalFileName("lib/config.php") => /home/user/phpwiki/lib/config.php
508  */
509 function NormalizeLocalFileName($file) {
510     static $finder;
511     if (!isset($finder)) {
512         $finder = new FileFinder;
513     }
514     // remove "/lib" from dirname(__FILE__)
515     if ($finder->_is_abs($file))
516         return $finder->slashifyPath($file);
517     else {
518         if (defined("PHPWIKI_DIR")) $wikidir = PHPWIKI_DIR;
519         else $wikidir = preg_replace('/.lib$/','',dirname(__FILE__));
520         $wikidir = $finder->_strip_last_pathchar($wikidir);
521         $pathsep = $finder->_use_path_separator($wikidir);
522         return $finder->slashifyPath($wikidir . $pathsep . $file);
523         // return PHPWIKI_DIR . "/" . $file;
524     }
525 }
526
527 /** 
528  * Prefixes with DATA_PATH and slashify
529  */
530 function NormalizeWebFileName($file) {
531     static $finder;
532     if (!isset($finder)) {
533         $finder = new FileFinder;
534     }
535     if (defined("DATA_PATH")) {
536         $wikipath = DATA_PATH;
537         $wikipath = $finder->_strip_last_pathchar($wikipath);
538         if (!$file)
539             return $finder->forcePathSlashes($wikipath);
540         else
541             return $finder->forcePathSlashes($wikipath . '/' . $file);
542     } else {
543         return $finder->forcePathSlashes($file);
544     }
545 }
546
547 function isWindows() {
548     static $win;
549     if (isset($win)) return $win;
550     //return preg_match('/^Windows/', php_uname());
551     $win = (substr(PHP_OS,0,3) == 'WIN');
552     return $win;
553 }
554
555 function isWindows95() {
556     static $win95;
557     if (isset($win95)) return $win95;
558     $win95 = isWindows() and !isWindowsNT();
559     return $win95;
560 }
561
562 function isWindowsNT() {
563     static $winnt;
564     if (isset($winnt)) return $winnt;
565     // FIXME: Do this using PHP_OS instead of php_uname().
566     // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
567     if (function_usable('php_uname'))
568         $winnt = preg_match('/^Windows NT/', php_uname());
569     else
570         $winnt = false;         // FIXME: punt.
571     return $winnt;
572 }
573
574 /** 
575  * This is for the OLD Macintosh OS, NOT MacOSX or Darwin!
576  * This has really ugly pathname semantics.
577  * ":path" is relative, "Desktop:path" (I think) is absolute. 
578  * FIXME: Please fix this someone. So far not supported.
579  */
580 function isMac() {
581     return (substr(PHP_OS,0,9) == 'Macintosh'); // not tested!
582 }
583
584 // probably not needed, same behaviour as on unix.
585 function isCygwin() {
586     return (substr(PHP_OS,0,6) == 'CYGWIN');
587 }
588
589 // $Log: not supported by cvs2svn $
590 // Revision 1.29  2004/11/09 17:11:03  rurban
591 // * revert to the wikidb ref passing. there's no memory abuse there.
592 // * use new wikidb->_cache->_id_cache[] instead of wikidb->_iwpcache, to effectively
593 //   store page ids with getPageLinks (GleanDescription) of all existing pages, which
594 //   are also needed at the rendering for linkExistingWikiWord().
595 //   pass options to pageiterator.
596 //   use this cache also for _get_pageid()
597 //   This saves about 8 SELECT count per page (num all pagelinks).
598 // * fix passing of all page fields to the pageiterator.
599 // * fix overlarge session data which got broken with the latest ACCESS_LOG_SQL changes
600 //
601 // Revision 1.28  2004/11/06 17:02:33  rurban
602 // Workaround some php-win \\ duplication bug
603 //
604 // Revision 1.27  2004/10/14 19:23:58  rurban
605 // remove debugging prints
606 //
607 // Revision 1.26  2004/10/14 19:19:33  rurban
608 // loadsave: check if the dumped file will be accessible from outside.
609 // and some other minor fixes. (cvsclient native not yet ready)
610 //
611 // Revision 1.25  2004/10/12 13:13:19  rurban
612 // php5 compatibility (5.0.1 ok)
613 //
614 // Revision 1.24  2004/08/05 17:33:51  rurban
615 // strange problem with WikiTheme
616 //
617 // Revision 1.23  2004/06/19 12:33:25  rurban
618 // prevent some warnings in corner cases
619 //
620 // Revision 1.22  2004/06/14 11:31:20  rurban
621 // renamed global $Theme to $WikiTheme (gforge nameclash)
622 // inherit PageList default options from PageList
623 //   default sortby=pagename
624 // use options in PageList_Selectable (limit, sortby, ...)
625 // added action revert, with button at action=diff
626 // added option regex to WikiAdminSearchReplace
627 //
628 // Revision 1.21  2004/06/02 18:01:45  rurban
629 // init global FileFinder to add proper include paths at startup
630 //   adds PHPWIKI_DIR if started from another dir, lib/pear also
631 // fix slashify for Windows
632 // fix USER_AUTH_POLICY=old, use only USER_AUTH_ORDER methods (besides HttpAuth)
633 //
634 // Revision 1.20  2004/05/27 17:49:05  rurban
635 // renamed DB_Session to DbSession (in CVS also)
636 // added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
637 // remove leading slash in error message
638 // added force_unlock parameter to File_Passwd (no return on stale locks)
639 // fixed adodb session AffectedRows
640 // added FileFinder helpers to unify local filenames and DATA_PATH names
641 // editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
642 //
643 //
644
645 // Local Variables:
646 // mode: php
647 // tab-width: 8
648 // c-basic-offset: 4
649 // c-hanging-comment-ender-p: nil
650 // indent-tabs-mode: nil
651 // End:
652 ?>