Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
//
3
// +----------------------------------------------------------------------+
4
// | PHP Version 4                                                        |
5
// +----------------------------------------------------------------------+
6
// | Copyright (c) 1997-2005 The PHP Group                                |
7
// +----------------------------------------------------------------------+
8
// | This source file is subject to version 2.02 of the PHP license,      |
9
// | that is bundled with this package in the file LICENSE, and is        |
10
// | available at through the world-wide-web at                           |
11
// | http://www.php.net/license/2_02.txt.                                 |
12
// | If you did not receive a copy of the PHP license and are unable to   |
13
// | obtain it through the world-wide-web, please send a note to          |
14
// | license@php.net so we can mail you a copy immediately.               |
15
// +----------------------------------------------------------------------+
16
// | Author: Sterling Hughes <sterling@php.net>                           |
17
// +----------------------------------------------------------------------+
18
//
19
// $Id: Find.php 304090 2010-10-05 14:07:34Z clockwerx $
20
//
21
 
22
require_once 'PEAR.php';
23
 
24
define('FILE_FIND_VERSION', '@package_version@');
25
 
26
// to debug uncomment this string
27
// define('FILE_FIND_DEBUG', '');
28
 
29
/**
30
*  Commonly needed functions searching directory trees
31
*
32
* @access public
33
* @version $Id: Find.php 304090 2010-10-05 14:07:34Z clockwerx $
34
* @package File
35
* @author Sterling Hughes <sterling@php.net>
36
*/
37
class File_Find
38
{
39
    /**
40
     * internal dir-list
41
     * @var array
42
     */
43
    var $_dirs = array();
44
 
45
    /**
46
     * directory separator
47
     * @var string
48
     */
49
    var $dirsep = "/";
50
 
51
    /**
52
     * found files
53
     * @var array
54
     */
55
    var $files = array();
56
 
57
    /**
58
     * found dirs
59
     * @var array
60
     */
61
    var $directories = array();
62
 
63
    /**
64
     * Search specified directory to find matches for specified pattern
65
     *
66
     * @param string $pattern a string containing the pattern to search
67
     * the directory for.
68
     *
69
     * @param string $dirpath a string containing the directory path
70
     * to search.
71
     *
72
     * @param string $pattern_type a string containing the type of
73
     * pattern matching functions to use (can either be 'php',
74
     * 'perl' or 'shell').
75
     *
76
     * @return array containing all of the files and directories
77
     * matching the pattern or null if no matches
78
     *
79
     * @author Sterling Hughes <sterling@php.net>
80
     * @access public
81
     * @static
82
     */
83
    function &glob($pattern, $dirpath, $pattern_type = 'php')
84
    {
85
        $dh = @opendir($dirpath);
86
 
87
        if (!$dh) {
88
            $pe = PEAR::raiseError("Cannot open directory $dirpath");
89
            return $pe;
90
        }
91
 
92
        $match_function = File_Find::_determineRegex($pattern, $pattern_type);
93
        $matches = array();
94
 
95
        // empty string cannot be specified for 'php' and 'perl' pattern
96
        if ($pattern || ($pattern_type != 'php' && $pattern_type != 'perl')) {
97
            while (false !== ($entry = @readdir($dh))) {
98
                if ($match_function($pattern, $entry) &&
99
                    $entry != '.' && $entry != '..') {
100
                    $matches[] = $entry;
101
                }
102
            }
103
        }
104
 
105
        @closedir($dh);
106
 
107
        if (0 == count($matches)) {
108
            $matches = null;
109
        } else {
110
            sort($matches);
111
        }
112
 
113
 
114
        return $matches ;
115
    }
116
 
117
    /**
118
     * Map the directory tree given by the directory_path parameter.
119
     *
120
     * @param string $directory contains the directory path that you
121
     * want to map.
122
     *
123
     * @return array a two element array, the first element containing a list
124
     * of all the directories, the second element containing a list of all the
125
     * files.
126
     *
127
     * @author Sterling Hughes <sterling@php.net>
128
     * @access public
129
     */
130
    function &maptree($directory)
131
    {
132
 
133
        /* if called statically */
134
        if (!isset($this)  || !is_a($this, "File_Find")) {
135
            $obj = &new File_Find();
136
            return $obj->maptree($directory);
137
        }
138
 
139
        /* clear the results just in case */
140
        $this->files       = array();
141
        $this->directories = array();
142
 
143
        /* strip out trailing slashes */
144
        $directory = preg_replace('![\\\\/]+$!', '', $directory);
145
 
146
        $this->_dirs = array($directory);
147
 
148
        while (count($this->_dirs)) {
149
            $dir = array_pop($this->_dirs);
150
            File_Find::_build($dir, $this->dirsep);
151
            array_push($this->directories, $dir);
152
        }
153
 
154
        sort($this->directories);
155
        sort($this->files);
156
 
157
        $retval = array($this->directories, $this->files);
158
        return $retval;
159
 
160
    }
161
 
162
    /**
163
     * Map the directory tree given by the directory parameter.
164
     *
165
     * @param string $directory contains the directory path that you
166
     * want to map.
167
     * @param integer $maxrecursion maximun number of folders to recursive
168
     * map
169
     *
170
     * @return array a multidimensional array containing all subdirectories
171
     * and their files. For example:
172
     *
173
     * Array
174
     * (
175
     *    [0] => file_1.php
176
     *    [1] => file_2.php
177
     *    [subdirname] => Array
178
     *       (
179
     *          [0] => file_1.php
180
     *       )
181
     * )
182
     *
183
     * @author Mika Tuupola <tuupola@appelsiini.net>
184
     * @access public
185
     * @static
186
     */
187
    function &mapTreeMultiple($directory, $maxrecursion = 0, $count = 0)
188
    {
189
        $retval = array();
190
 
191
        $count++;
192
 
193
        /* strip trailing slashes */
194
        $directory = preg_replace('![\\\\/]+$!', '', $directory);
195
 
196
        if (is_readable($directory)) {
197
            $dh = opendir($directory);
198
            while (false !== ($entry = @readdir($dh))) {
199
                if ($entry != '.' && $entry != '..') {
200
                     array_push($retval, $entry);
201
                }
202
            }
203
            closedir($dh);
204
        }
205
 
206
        sort($retval);
207
 
208
        while (list($key, $val) = each($retval)) {
209
            $path = $directory . "/" . $val;
210
 
211
            if (!is_array($val) && is_dir($path)) {
212
                unset($retval[$key]);
213
                if ($maxrecursion == 0 || $count < $maxrecursion) {
214
                    $retval[$val] = &File_Find::mapTreeMultiple($path,
215
                                    $maxrecursion, $count);
216
                }
217
            }
218
        }
219
 
220
        return $retval;
221
    }
222
 
223
    /**
224
     * Search the specified directory tree with the specified pattern.  Return
225
     * an array containing all matching files (no directories included).
226
     *
227
     * @param string $pattern the pattern to match every file with.
228
     *
229
     * @param string $directory the directory tree to search in.
230
     *
231
     * @param string $type the type of regular expression support to use, either
232
     * 'php', 'perl' or 'shell'.
233
     *
234
     * @param bool $fullpath whether the regex should be matched against the
235
     * full path or only against the filename
236
     *
237
     * @param string $match can be either 'files', 'dirs' or 'both' to specify
238
     * the kind of list to return
239
     *
240
     * @return array a list of files matching the pattern parameter in the the
241
     * directory path specified by the directory parameter
242
     *
243
     * @author Sterling Hughes <sterling@php.net>
244
     * @access public
245
     * @static
246
     */
247
    function &search($pattern, $directory, $type = 'php', $fullpath = true, $match = 'files')
248
    {
249
 
250
        $matches = array();
251
        list ($directories,$files)  = File_Find::maptree($directory);
252
        switch($match) {
253
            case 'directories':
254
                $data = $directories;
255
                break;
256
            case 'both':
257
                $data = array_merge($directories, $files);
258
                break;
259
            case 'files':
260
            default:
261
                $data = $files;
262
        }
263
        unset($files, $directories);
264
 
265
        $match_function = File_Find::_determineRegex($pattern, $type);
266
 
267
        reset($data);
268
        // check if empty string given (ok for 'shell' method, but bad for others)
269
        if ($pattern || ($type != 'php' && $type != 'perl')) {
270
            while (list(,$entry) = each($data)) {
271
                if ($match_function($pattern,
272
                                    $fullpath ? $entry : basename($entry))) {
273
                    $matches[] = $entry;
274
                }
275
            }
276
        }
277
 
278
        sort($matches);
279
 
280
        return $matches;
281
    }
282
 
283
    /**
284
     * Determine whether or not a variable is a PEAR error
285
     *
286
     * @param object PEAR_Error $var the variable to test.
287
     *
288
     * @return boolean returns true if the variable is a PEAR error, otherwise
289
     * it returns false.
290
     * @access public
291
     */
292
    function isError(&$var)
293
    {
294
        return PEAR::isError($var);
295
    }
296
 
297
    /**
298
     * internal function to build singular directory trees, used by
299
     * File_Find::maptree()
300
     *
301
     * @param string $directory name of the directory to read
302
     * @param string $separator directory separator
303
     * @return void
304
     */
305
    function _build($directory, $separator = "/")
306
    {
307
 
308
        $dh = @opendir($directory);
309
 
310
        if (!$dh) {
311
            $pe = PEAR::raiseError("Cannot open directory");
312
            return $pe;
313
        }
314
 
315
        while (false !== ($entry = @readdir($dh))) {
316
            if ($entry != '.' && $entry != '..') {
317
 
318
                $entry = $directory.$separator.$entry;
319
 
320
                if (is_dir($entry)) {
321
                    array_push($this->_dirs, $entry);
322
                } else {
323
                    array_push($this->files, $entry);
324
                }
325
            }
326
        }
327
 
328
        @closedir($dh);
329
    }
330
 
331
    /**
332
     * internal function to determine the type of regular expression to
333
     * use, implemented by File_Find::glob() and File_Find::search()
334
     *
335
     * @param string $type given RegExp type
336
     * @return string kind of function ( "eregi", "ereg" or "preg_match") ;
337
     *
338
     */
339
    function _determineRegex($pattern, $type)
340
    {
341
        if (!strcasecmp($type, 'shell')) {
342
            $match_function = 'File_Find_match_shell';
343
        } else if (!strcasecmp($type, 'perl')) {
344
            $match_function = 'preg_match';
345
        } else if (!strcasecmp(substr($pattern, -2), '/i')) {
346
            $match_function = 'eregi';
347
        } else {
348
            $match_function = 'ereg';
349
        }
350
        return $match_function;
351
    }
352
 
353
}
354
 
355
/**
356
* Package method to match via 'shell' pattern. Provided in global
357
* scope, because it should be called like 'preg_match' and 'eregi'
358
* and can be easily copied into other packages
359
*
360
* @author techtonik <techtonik@php.net>
361
* @return mixed bool on success and PEAR_Error on failure
362
*/
363
function File_Find_match_shell($pattern, $filename)
364
{
365
    // {{{ convert pattern to positive and negative regexps
366
        $positive = $pattern;
367
        $negation = substr_count($pattern, "|");
368
 
369
        if ($negation > 1) {
370
            PEAR::raiseError("Mask string contains errors!");
371
            return FALSE;
372
        } elseif ($negation) {
373
            list($positive, $negative) = explode("|", $pattern);
374
            if (strlen($negative) == 0) {
375
                PEAR::raiseError("File-mask string contains errors!");
376
                return FALSE;
377
            }
378
        }
379
 
380
       $positive = _File_Find_match_shell_get_pattern($positive);
381
       if ($negation) {
382
           $negative = _File_Find_match_shell_get_pattern($negative);
383
       }
384
    // }}} convert end
385
 
386
 
387
    if (defined("FILE_FIND_DEBUG")) {
388
        print("Method: $type\nPattern: $pattern\n Converted pattern:");
389
        print_r($positive);
390
        if (isset($negative)) print_r($negative);
391
    }
392
 
393
    if (!preg_match($positive, $filename)) {
394
        return FALSE;
395
    } else {
396
        if (isset($negative)
397
              && preg_match($negative, $filename)) {
398
            return FALSE;
399
        } else {
400
            return TRUE;
401
        }
402
    }
403
}
404
 
405
/**
406
* function used by File_Find_match_shell to convert 'shell' mask
407
* into pcre regexp. Some of the rules (see testcases for more):
408
*  escaping all special chars and replacing
409
*    . with \.
410
*    * with .*
411
*    ? with .{1}
412
*    also adding ^ and $ as the pattern matches whole filename
413
*
414
* @author techtonik <techtonik@php.net>
415
* @return string pcre regexp for preg_match
416
*/
417
function _File_Find_match_shell_get_pattern($mask) {
418
    // get array of several masks (if any) delimited by comma
419
    // do not touch commas in char class
420
    $premasks = preg_split("|(\[[^\]]+\])|", $mask, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
421
    if (defined("FILE_FIND_DEBUG")) {
422
        print("\nPremask: ");
423
        print_r($premasks);
424
    }
425
    $pi = 0;
426
    foreach($premasks as $pm) {
427
        if (!isset($masks[$pi])) $masks[$pi] = "";
428
        if ($pm{0} == '[' && $pm{strlen($pm)-1} == ']') {
429
            // strip commas from character class
430
            $masks[$pi] .= str_replace(",", "", $pm);
431
        } else {
432
            $tarr = explode(",", $pm);
433
            if (sizeof($tarr) == 1) {
434
                $masks[$pi] .= $pm;
435
            } else {
436
                foreach ($tarr as $te) {
437
                    $masks[$pi++] .= $te;
438
                    $masks[$pi] = "";
439
                }
440
                unset($masks[$pi--]);
441
            }
442
        }
443
    }
444
 
445
    // if empty string given return *.* pattern
446
    if (strlen($mask) == 0) return "!^.*$!";
447
 
448
    // convert to preg regexp
449
    $regexmask = implode("|", $masks);
450
    if (defined("FILE_FIND_DEBUG")) {
451
        print("regexMask step one(implode): $regexmask");
452
    }
453
    $regexmask = addcslashes($regexmask, '^$}!{)(\/.+');
454
    if (defined("FILE_FIND_DEBUG")) {
455
        print("\nregexMask step two(addcslashes): $regexmask");
456
    }
457
    $regexmask = preg_replace("!(\*|\?)!", ".$1", $regexmask);
458
    if (defined("FILE_FIND_DEBUG")) {
459
        print("\nregexMask step three(* ? -> .* .?): $regexmask");
460
    }
461
    // a special case '*.' at the end means that there is no extension
462
    $regexmask = preg_replace("!\.\*\\\.(\||$)!", "[^\.]*$1", $regexmask);
463
    // it is impossible to have dot at the end of filename
464
    $regexmask = preg_replace("!\\\.(\||$)!", "$1", $regexmask);
465
    // and .* at the end also means that there could be nothing at all
466
    //   (i.e. no dot at the end also)
467
    $regexmask = preg_replace("!\\\.\.\*(\||$)!", "(\\\\..*)?$1", $regexmask);
468
    if (defined("FILE_FIND_DEBUG")) {
469
        print("\nregexMask step two and half(*.$ \\..*$ .$ -> [^.]*$ .?.* $): $regexmask");
470
    }
471
    // if no extension supplied - add .* to match partially from filename start
472
    if (strpos($regexmask, "\\.") === FALSE) $regexmask .= ".*";
473
 
474
    // file mask match whole name - adding restrictions
475
    $regexmask = preg_replace("!(\|)!", '^'."$1".'$', $regexmask);
476
    $regexmask = '^'.$regexmask.'$';
477
    if (defined("FILE_FIND_DEBUG")) {
478
        print("\nregexMask step three(^ and $ to match whole name): $regexmask");
479
    }
480
    // wrap regex into ! since all ! are already escaped
481
    $regexmask = "!$regexmask!i";
482
    if (defined("FILE_FIND_DEBUG")) {
483
        print("\nWrapped regex: $regexmask\n");
484
    }
485
    return $regexmask;
486
}
487
 
488
/*
489
 * Local variables:
490
 * tab-width: 4
491
 * c-basic-offset: 4
492
 * End:
493
 */
494
 
495
?>