]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/FileFinder.php
Use public when needed
[SourceForge/phpwiki.git] / lib / FileFinder.php
1 <?php
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, ...)
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     public $_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 = array())
24     {
25         $this->_pathsep = $this->_get_syspath_separator();
26         if (!isset($this->_path) and $path === false)
27             $path = $this->_get_include_path();
28         $this->_path = $path;
29     }
30
31     /**
32      * Find file.
33      *
34      * @param $file string File to search for.
35      * @param bool $missing_okay
36      * @return string The filename (including path), if found, otherwise false.
37      */
38     function findFile($file, $missing_okay = false)
39     {
40         if ($this->_is_abs($file)) {
41             if (file_exists($file))
42                 return $file;
43         } elseif (($dir = $this->_search_path($file))) {
44             return $dir . $this->_use_path_separator($dir) . $file;
45         }
46         return $missing_okay ? false : $this->_not_found($file);
47     }
48
49     /**
50      * Unify used pathsep character.
51      * Accepts array of paths also.
52      * This might not work on Windows95 or FAT volumes. (not tested)
53      */
54     function slashifyPath($path)
55     {
56         return $this->forcePathSlashes($path, $this->_pathsep);
57     }
58
59     /**
60      * Force using '/' as path seperator.
61      */
62     function forcePathSlashes($path, $sep = '/')
63     {
64         if (is_array($path)) {
65             $result = array();
66             foreach ($path as $dir) {
67                 $result[] = $this->forcePathSlashes($dir, $sep);
68             }
69             return $result;
70         } else {
71             if (isWindows() or $this->_isOtherPathsep()) {
72                 if (isWindows()) $from = "\\";
73                 else $from = "\\";
74                 // PHP is stupid enough to use \\ instead of \
75                 if (isWindows()) {
76                     if (substr($path, 0, 2) != '\\\\')
77                         $path = str_replace('\\\\', '\\', $path);
78                     else // UNC paths
79                         $path = '\\\\' . str_replace('\\\\', '\\', substr($path, 2));
80                 }
81                 return strtr($path, $from, $sep);
82             } else
83                 return $path;
84         }
85     }
86
87     /**
88      * Try to include file.
89      *
90      * If file is found in the path, then the files directory is added
91      * to PHP's include_path (if it's not already there.) Then the
92      * file is include_once()'d.
93      *
94      * @param $file string File to include.
95      * @return bool True if file was successfully included.
96      */
97     function includeOnce($file)
98     {
99         if (($ret = @include_once($file)))
100             return $ret;
101
102         if (!$this->_is_abs($file)) {
103             if (($dir = $this->_search_path($file)) && is_file($dir . $this->_pathsep . $file)) {
104                 $this->_append_to_include_path($dir);
105                 return include_once($file);
106             }
107         }
108         return $this->_not_found($file);
109     }
110
111     function _isOtherPathsep()
112     {
113         return $this->_pathsep != '/';
114     }
115
116     /**
117      * The system-dependent path-separator character.
118      * UNIX,WindowsNT,MacOSX: /
119      * Windows95: \
120      * Mac:       :
121      *
122      * @return string path_separator.
123      */
124     private function _get_syspath_separator()
125     {
126         if (!empty($this->_pathsep)) return $this->_pathsep;
127         elseif (isWindowsNT()) return "/"; // we can safely use '/'
128         elseif (isWindows()) return "\\"; // FAT might use '\'
129         // VMS or LispM is really weird, we ignore it.
130         else return '/';
131     }
132
133     /**
134      * The path-separator character of the given path.
135      * Windows accepts "/" also, but gets confused with mixed path_separators,
136      * e.g "C:\Apache\phpwiki/locale/button"
137      * > dir "C:\Apache\phpwiki/locale/button" =>
138      *       Parameterformat nicht korrekt - "locale"
139      * So if there's any '\' in the path, either fix them to '/' (not in Win95 or FAT?)
140      * or use '\' for ours.
141      *
142      * @param string $path
143      * @return string path_separator.
144      */
145     public function _use_path_separator($path)
146     {
147         if (isWindows95()) {
148             if (empty($path)) return "\\";
149             else return (strchr($path, "\\")) ? "\\" : '/';
150         } else {
151             return $this->_get_syspath_separator();
152         }
153     }
154
155     /**
156      * Determine if path is absolute.
157      *
158      * @param $path string Path.
159      * @return bool True if path is absolute.
160      */
161     public function _is_abs($path)
162     {
163         if (substr($path, 0, 1) == '/') {
164             return true;
165         } elseif (isWindows() and preg_match("/^[a-z]:/i", $path)
166             and (substr($path, 2, 1) == "/" or substr($path, 2, 1) == "\\")
167         ) {
168             return true;
169         } else {
170             return false;
171         }
172     }
173
174     /**
175      * Strip ending '/' or '\' from path.
176      *
177      * @param $path string Path.
178      * @return bool New path (destructive)
179      */
180     public function _strip_last_pathchar(&$path)
181     {
182         if (substr($path, -1) == '/' or substr($path, -1) == "\\")
183             $path = substr($path, 0, -1);
184         return $path;
185     }
186
187     /**
188      * Report a "file not found" error.
189      *
190      * @param $file string Name of missing file.
191      * @return bool false.
192      */
193     private function _not_found($file)
194     {
195         trigger_error(sprintf(_("%s: file not found"), $file), E_USER_ERROR);
196         return false;
197     }
198
199     /**
200      * Search our path for a file.
201      *
202      * @param $file string File to find.
203      * @return string Directory which contains $file, or false.
204      * [5x,44ms]
205      */
206     private function _search_path($file)
207     {
208         foreach ($this->_path as $dir) {
209             // ensure we use the same pathsep
210             if ($this->_isOtherPathsep()) {
211                 $dir = $this->slashifyPath($dir);
212                 $file = $this->slashifyPath($file);
213                 if (file_exists($dir . $this->_pathsep . $file))
214                     return $dir;
215             } elseif (@file_exists($dir . $this->_pathsep . $file))
216                 return $dir;
217         }
218         return false;
219     }
220
221     /**
222      * The system-dependent path-separator character. On UNIX systems,
223      * this character is ':'; on Win32 systems it is ';'.
224      * Fixme:
225      * On Mac it cannot be : because this is the seperator there!
226      *
227      * @return string path_separator.
228      */
229     public function _get_ini_separator()
230     {
231         return isWindows() ? ';' : ':';
232         // return preg_match('/^Windows/', php_uname())
233     }
234
235     /**
236      * Get the value of PHP's include_path.
237      *
238      * @return array Include path.
239      */
240     private function _get_include_path()
241     {
242         if (defined("INCLUDE_PATH"))
243             $path = INCLUDE_PATH;
244         else {
245             $path = @get_cfg_var('include_path'); // FIXME: report warning
246             if (empty($path)) $path = @ini_get('include_path');
247         }
248         if (empty($path))
249             $path = '.';
250         return explode($this->_get_ini_separator(), $this->slashifyPath($path));
251     }
252
253     /**
254      * Add a directory to the end of PHP's include_path.
255      *
256      * The directory is appended only if it is not already listed in
257      * the include_path.
258      *
259      * @param $dir string Directory to add.
260      */
261     public function _append_to_include_path($dir)
262     {
263         $dir = $this->slashifyPath($dir);
264         if (!in_array($dir, $this->_path)) {
265             $this->_path[] = $dir;
266         }
267         /*
268          * Some (buggy) PHP's (notable SourceForge's PHP 4.0.6)
269          * sometimes don't seem to heed their include_path.
270          * I.e. sometimes a file is not found even though it seems to
271          * be in the current include_path. A simple
272          * ini_set('include_path', ini_get('include_path')) seems to
273          * be enough to fix the problem
274          *
275          * This following line should be in the above if-block, but we
276          * put it here, as it seems to work-around the bug.
277          */
278         $GLOBALS['INCLUDE_PATH'] = implode($this->_get_ini_separator(), $this->_path);
279         @ini_set('include_path', $GLOBALS['INCLUDE_PATH']);
280     }
281
282     /**
283      * Add a directory to the front of PHP's include_path.
284      *
285      * The directory is prepended, and removed from the tail if already existing.
286      *
287      * @param $dir string Directory to add.
288      */
289     public function _prepend_to_include_path($dir)
290     {
291         $dir = $this->slashifyPath($dir);
292         // remove duplicates
293         if ($i = array_search($dir, $this->_path) !== false) {
294             array_splice($this->_path, $i, 1);
295         }
296         array_unshift($this->_path, $dir);
297         $GLOBALS['INCLUDE_PATH'] = implode($this->_path, $this->_get_ini_separator());
298         @ini_set('include_path', $GLOBALS['INCLUDE_PATH']);
299     }
300
301     // Return all the possible shortened locale specifiers for the given locale.
302     // Most specific first.
303     // de_DE.iso8859-1@euro => de_DE.iso8859-1, de_DE, de
304     // This code might needed somewhere else also.
305     function locale_versions($lang)
306     {
307         // Try less specific versions of the locale
308         $langs[] = $lang;
309         foreach (array('@', '.', '_') as $sep) {
310             if (($tail = strchr($lang, $sep)))
311                 $langs[] = substr($lang, 0, -strlen($tail));
312         }
313         return $langs;
314     }
315
316     /**
317      * Try to figure out the appropriate value for $LANG.
318      *
319      * @return string The value of $LANG.
320      */
321     private function _get_lang()
322     {
323         if (!empty($GLOBALS['LANG']))
324             return $GLOBALS['LANG'];
325
326         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES') as $var) {
327             $lang = setlocale(constant($var), 0);
328             if (!empty($lang))
329                 return $lang;
330         }
331
332         foreach (array('LC_ALL', 'LC_MESSAGES', 'LC_RESPONSES', 'LANG') as $var) {
333             $lang = getenv($var);
334             if (!empty($lang))
335                 return $lang;
336         }
337
338         return "C";
339     }
340 }
341
342 /**
343  * A class for finding PEAR code.
344  *
345  * This is a subclass of FileFinder which searches a standard list of
346  * directories where PEAR code is likely to be installed.
347  *
348  * Example usage:
349  *
350  * <pre>
351  *   $pearFinder = new PearFileFinder;
352  *   $pearFinder->includeOnce('DB.php');
353  * </pre>
354  *
355  * The above code will look for 'DB.php', if found, the directory in
356  * which it was found will be added to PHP's include_path, and the
357  * file will be included. (If the file is not found, and E_USER_ERROR
358  * will be thrown.)
359  */
360 class PearFileFinder
361     extends FileFinder
362 {
363     /**
364      * Constructor.
365      *
366      * @param $path array Where to look for PEAR library code.
367      * A good set of defaults is provided, so you can probably leave
368      * this parameter blank.
369      */
370     function PearFileFinder($path = array())
371     {
372         $this->FileFinder(array_merge(
373             $path,
374             array('/usr/share/php',
375                 '/usr/lib/php',
376                 '/usr/local/share/php',
377                 '/usr/local/lib/php',
378                 '/System/Library/PHP',
379                 '/Apache/pear' // Windows
380             )));
381     }
382 }
383
384 /**
385  * Find PhpWiki localized files.
386  *
387  * This is a subclass of FileFinder which searches PHP's include_path
388  * for files. It looks first for "locale/$LANG/$file", then for
389  * "$file".
390  *
391  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
392  * also search under various less specific variations like
393  * "de_DE.iso8859-1", "de_DE" and "de".
394  */
395 class LocalizedFileFinder
396     extends FileFinder
397 {
398     /**
399      * Constructor.
400      */
401     function LocalizedFileFinder()
402     {
403         $this->_pathsep = $this->_get_syspath_separator();
404         $include_path = $this->_get_include_path();
405         $path = array();
406
407         $lang = $this->_get_lang();
408         assert(!empty($lang));
409
410         if ($locales = $this->locale_versions($lang)) {
411             foreach ($locales as $lang) {
412                 if ($lang == 'C') $lang = 'en';
413                 foreach ($include_path as $dir) {
414                     $path[] = $this->slashifyPath($dir . "/locale/$lang");
415                 }
416             }
417         }
418         $this->FileFinder(array_merge($path, $include_path));
419     }
420 }
421
422 /**
423  * Find PhpWiki localized theme buttons.
424  *
425  * This is a subclass of FileFinder which searches PHP's include_path
426  * for files. It looks first for "buttons/$LANG/$file", then for
427  * "$file".
428  *
429  * If $LANG is something like "de_DE.iso8859-1@euro", this class will
430  * also search under various less specific variations like
431  * "de_DE.iso8859-1", "de_DE" and "de".
432  */
433 class LocalizedButtonFinder
434     extends FileFinder
435 {
436     /**
437      * Constructor.
438      */
439     function LocalizedButtonFinder()
440     {
441         global $WikiTheme;
442         $this->_pathsep = $this->_get_syspath_separator();
443         $include_path = $this->_get_include_path();
444         $path = array();
445
446         $lang = $this->_get_lang();
447         assert(!empty($lang));
448         assert(!empty($WikiTheme));
449
450         if (is_object($WikiTheme)) {
451             $langs = $this->locale_versions($lang);
452             foreach ($langs as $lang) {
453                 if ($lang == 'C') $lang = 'en';
454                 foreach ($include_path as $dir) {
455                     $path[] = $this->slashifyPath($WikiTheme->file("buttons/$lang"));
456                 }
457             }
458         }
459
460         $this->FileFinder(array_merge($path, $include_path));
461     }
462 }
463
464 // Search PHP's include_path to find file or directory.
465 function FindFile($file, $missing_okay = false, $slashify = false)
466 {
467     static $finder;
468     if (!isset($finder)) {
469         $finder = new FileFinder;
470         // remove "/lib" from dirname(__FILE__)
471         $wikidir = preg_replace('/.lib$/', '', dirname(__FILE__));
472         // let the system favor its local pear?
473         $finder->_append_to_include_path(dirname(__FILE__) . "/pear");
474         $finder->_prepend_to_include_path($wikidir);
475         // Don't override existing INCLUDE_PATH config.
476         if (!defined("INCLUDE_PATH"))
477             define("INCLUDE_PATH", implode($finder->_get_ini_separator(), $finder->_path));
478     }
479     $s = $finder->findFile($file, $missing_okay);
480     if ($slashify)
481         $s = $finder->slashifyPath($s);
482     return $s;
483 }
484
485 // Search PHP's include_path to find file or directory.
486 // Searches for "locale/$LANG/$file", then for "$file".
487 function FindLocalizedFile($file, $missing_okay = false, $re_init = false)
488 {
489     static $finder;
490     if ($re_init or !isset($finder))
491         $finder = new LocalizedFileFinder;
492     return $finder->findFile($file, $missing_okay);
493 }
494
495 function FindLocalizedButtonFile($file, $missing_okay = false, $re_init = false)
496 {
497     static $buttonfinder;
498     if ($re_init or !isset($buttonfinder))
499         $buttonfinder = new LocalizedButtonFinder;
500     return $buttonfinder->findFile($file, $missing_okay);
501 }
502
503 /**
504  * Prefixes with PHPWIKI_DIR and slashify.
505  * For example to unify with
506  *   require_once dirname(__FILE__).'/lib/file.php'
507  *   require_once 'lib/file.php' loading style.
508  * Doesn't expand "~" or symlinks yet. truename would be perfect.
509  *
510  * NormalizeLocalFileName("lib/config.php") => /home/user/phpwiki/lib/config.php
511  */
512 function NormalizeLocalFileName($file)
513 {
514     static $finder;
515     if (!isset($finder)) {
516         $finder = new FileFinder;
517     }
518     // remove "/lib" from dirname(__FILE__)
519     if ($finder->_is_abs($file))
520         return $finder->slashifyPath($file);
521     else {
522         if (defined("PHPWIKI_DIR")) $wikidir = PHPWIKI_DIR;
523         else $wikidir = preg_replace('/.lib$/', '', dirname(__FILE__));
524         $wikidir = $finder->_strip_last_pathchar($wikidir);
525         $pathsep = $finder->_use_path_separator($wikidir);
526         return $finder->slashifyPath($wikidir . $pathsep . $file);
527         // return PHPWIKI_DIR . "/" . $file;
528     }
529 }
530
531 /**
532  * Prefixes with DATA_PATH and slashify
533  */
534 function NormalizeWebFileName($file)
535 {
536     static $finder;
537     if (!isset($finder)) {
538         $finder = new FileFinder;
539     }
540     if (defined("DATA_PATH")) {
541         $wikipath = DATA_PATH;
542         $wikipath = $finder->_strip_last_pathchar($wikipath);
543         if (!$file)
544             return $finder->forcePathSlashes($wikipath);
545         else
546             return $finder->forcePathSlashes($wikipath . '/' . $file);
547     } else {
548         return $finder->forcePathSlashes($file);
549     }
550 }
551
552 function isWindows()
553 {
554     static $win;
555     if (isset($win)) return $win;
556     //return preg_match('/^Windows/', php_uname());
557     $win = (substr(PHP_OS, 0, 3) == 'WIN');
558     return $win;
559 }
560
561 function isWindows95()
562 {
563     static $win95;
564     if (isset($win95)) return $win95;
565     $win95 = isWindows() and !isWindowsNT();
566     return $win95;
567 }
568
569 function isWindowsNT()
570 {
571     static $winnt;
572     if (isset($winnt)) return $winnt;
573     // FIXME: Do this using PHP_OS instead of php_uname().
574     // $winnt = (PHP_OS == "WINNT"); // example from http://www.php.net/manual/en/ref.readline.php
575     if (function_usable('php_uname'))
576         $winnt = preg_match('/^Windows NT/', php_uname());
577     else
578         $winnt = false; // FIXME: punt.
579     return $winnt;
580 }
581
582 // Local Variables:
583 // mode: php
584 // tab-width: 8
585 // c-basic-offset: 4
586 // c-hanging-comment-ender-p: nil
587 // indent-tabs-mode: nil
588 // End: