Subversion Repositories Applications.gtt

Rev

Rev 187 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
187 mathias 1
<?php
2
/**
3
 * File/Directory manipulation
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * @category   pear
8
 * @package    System
9
 * @author     Tomas V.V.Cox <cox@idecnet.com>
10
 * @copyright  1997-2009 The Authors
11
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
12
 * @link       http://pear.php.net/package/PEAR
13
 * @since      File available since Release 0.1
14
 */
15
 
16
/**
17
 * base class
18
 */
19
require_once 'PEAR.php';
20
require_once 'Console/Getopt.php';
21
 
22
$GLOBALS['_System_temp_files'] = array();
23
 
24
/**
25
* System offers cross plattform compatible system functions
26
*
27
* Static functions for different operations. Should work under
28
* Unix and Windows. The names and usage has been taken from its respectively
29
* GNU commands. The functions will return (bool) false on error and will
30
* trigger the error with the PHP trigger_error() function (you can silence
31
* the error by prefixing a '@' sign after the function call, but this
32
* is not recommended practice.  Instead use an error handler with
33
* {@link set_error_handler()}).
34
*
35
* Documentation on this class you can find in:
36
* http://pear.php.net/manual/
37
*
38
* Example usage:
39
* if (!@System::rm('-r file1 dir1')) {
40
*    print "could not delete file1 or dir1";
41
* }
42
*
43
* In case you need to to pass file names with spaces,
44
* pass the params as an array:
45
*
46
* System::rm(array('-r', $file1, $dir1));
47
*
48
* @category   pear
49
* @package    System
50
* @author     Tomas V.V. Cox <cox@idecnet.com>
51
* @copyright  1997-2006 The PHP Group
52
* @license    http://opensource.org/licenses/bsd-license.php New BSD License
53
* @version    Release: 1.10.1
54
* @link       http://pear.php.net/package/PEAR
55
* @since      Class available since Release 0.1
56
* @static
57
*/
58
class System
59
{
60
    /**
61
     * returns the commandline arguments of a function
62
     *
63
     * @param    string  $argv           the commandline
64
     * @param    string  $short_options  the allowed option short-tags
65
     * @param    string  $long_options   the allowed option long-tags
66
     * @return   array   the given options and there values
67
     */
68
    public static function _parseArgs($argv, $short_options, $long_options = null)
69
    {
70
        if (!is_array($argv) && $argv !== null) {
71
            /*
72
            // Quote all items that are a short option
73
            $av = preg_split('/(\A| )--?[a-z0-9]+[ =]?((?<!\\\\)((,\s*)|((?<!,)\s+))?)/i', $argv, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
74
            $offset = 0;
75
            foreach ($av as $a) {
76
                $b = trim($a[0]);
77
                if ($b{0} == '"' || $b{0} == "'") {
78
                    continue;
79
                }
80
 
81
                $escape = escapeshellarg($b);
82
                $pos = $a[1] + $offset;
83
                $argv = substr_replace($argv, $escape, $pos, strlen($b));
84
                $offset += 2;
85
            }
86
            */
87
 
88
            // Find all items, quoted or otherwise
89
            preg_match_all("/(?:[\"'])(.*?)(?:['\"])|([^\s]+)/", $argv, $av);
90
            $argv = $av[1];
91
            foreach ($av[2] as $k => $a) {
92
                if (empty($a)) {
93
                    continue;
94
                }
95
                $argv[$k] = trim($a) ;
96
            }
97
        }
98
 
99
        return Console_Getopt::getopt2($argv, $short_options, $long_options);
100
    }
101
 
102
    /**
103
     * Output errors with PHP trigger_error(). You can silence the errors
104
     * with prefixing a "@" sign to the function call: @System::mkdir(..);
105
     *
106
     * @param mixed $error a PEAR error or a string with the error message
107
     * @return bool false
108
     */
109
    protected static function raiseError($error)
110
    {
111
        if (PEAR::isError($error)) {
112
            $error = $error->getMessage();
113
        }
114
        trigger_error($error, E_USER_WARNING);
115
        return false;
116
    }
117
 
118
    /**
119
     * Creates a nested array representing the structure of a directory
120
     *
121
     * System::_dirToStruct('dir1', 0) =>
122
     *   Array
123
     *    (
124
     *    [dirs] => Array
125
     *        (
126
     *            [0] => dir1
127
     *        )
128
     *
129
     *    [files] => Array
130
     *        (
131
     *            [0] => dir1/file2
132
     *            [1] => dir1/file3
133
     *        )
134
     *    )
135
     * @param    string  $sPath      Name of the directory
136
     * @param    integer $maxinst    max. deep of the lookup
137
     * @param    integer $aktinst    starting deep of the lookup
138
     * @param    bool    $silent     if true, do not emit errors.
139
     * @return   array   the structure of the dir
140
     */
141
    protected static function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
142
    {
143
        $struct = array('dirs' => array(), 'files' => array());
144
        if (($dir = @opendir($sPath)) === false) {
145
            if (!$silent) {
146
                System::raiseError("Could not open dir $sPath");
147
            }
148
            return $struct; // XXX could not open error
149
        }
150
 
151
        $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
152
        $list = array();
153
        while (false !== ($file = readdir($dir))) {
154
            if ($file != '.' && $file != '..') {
155
                $list[] = $file;
156
            }
157
        }
158
 
159
        closedir($dir);
160
        natsort($list);
161
        if ($aktinst < $maxinst || $maxinst == 0) {
162
            foreach ($list as $val) {
163
                $path = $sPath . DIRECTORY_SEPARATOR . $val;
164
                if (is_dir($path) && !is_link($path)) {
165
                    $tmp    = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
166
                    $struct = array_merge_recursive($struct, $tmp);
167
                } else {
168
                    $struct['files'][] = $path;
169
                }
170
            }
171
        }
172
 
173
        return $struct;
174
    }
175
 
176
    /**
177
     * Creates a nested array representing the structure of a directory and files
178
     *
179
     * @param    array $files Array listing files and dirs
180
     * @return   array
181
     * @static
182
     * @see System::_dirToStruct()
183
     */
184
    protected static function _multipleToStruct($files)
185
    {
186
        $struct = array('dirs' => array(), 'files' => array());
187
        settype($files, 'array');
188
        foreach ($files as $file) {
189
            if (is_dir($file) && !is_link($file)) {
190
                $tmp    = System::_dirToStruct($file, 0);
191
                $struct = array_merge_recursive($tmp, $struct);
192
            } else {
193
                if (!in_array($file, $struct['files'])) {
194
                    $struct['files'][] = $file;
195
                }
196
            }
197
        }
198
        return $struct;
199
    }
200
 
201
    /**
202
     * The rm command for removing files.
203
     * Supports multiple files and dirs and also recursive deletes
204
     *
205
     * @param    string  $args   the arguments for rm
206
     * @return   mixed   PEAR_Error or true for success
207
     * @static
208
     * @access   public
209
     */
210
    public static function rm($args)
211
    {
212
        $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
213
        if (PEAR::isError($opts)) {
214
            return System::raiseError($opts);
215
        }
216
        foreach ($opts[0] as $opt) {
217
            if ($opt[0] == 'r') {
218
                $do_recursive = true;
219
            }
220
        }
221
        $ret = true;
222
        if (isset($do_recursive)) {
223
            $struct = System::_multipleToStruct($opts[1]);
224
            foreach ($struct['files'] as $file) {
225
                if (!@unlink($file)) {
226
                    $ret = false;
227
                }
228
            }
229
 
230
            rsort($struct['dirs']);
231
            foreach ($struct['dirs'] as $dir) {
232
                if (!@rmdir($dir)) {
233
                    $ret = false;
234
                }
235
            }
236
        } else {
237
            foreach ($opts[1] as $file) {
238
                $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
239
                if (!@$delete($file)) {
240
                    $ret = false;
241
                }
242
            }
243
        }
244
        return $ret;
245
    }
246
 
247
    /**
248
     * Make directories.
249
     *
250
     * The -p option will create parent directories
251
     * @param    string  $args    the name of the director(y|ies) to create
252
     * @return   bool    True for success
253
     */
254
    public static function mkDir($args)
255
    {
256
        $opts = System::_parseArgs($args, 'pm:');
257
        if (PEAR::isError($opts)) {
258
            return System::raiseError($opts);
259
        }
260
 
261
        $mode = 0777; // default mode
262
        foreach ($opts[0] as $opt) {
263
            if ($opt[0] == 'p') {
264
                $create_parents = true;
265
            } elseif ($opt[0] == 'm') {
266
                // if the mode is clearly an octal number (starts with 0)
267
                // convert it to decimal
268
                if (strlen($opt[1]) && $opt[1]{0} == '0') {
269
                    $opt[1] = octdec($opt[1]);
270
                } else {
271
                    // convert to int
272
                    $opt[1] += 0;
273
                }
274
                $mode = $opt[1];
275
            }
276
        }
277
 
278
        $ret = true;
279
        if (isset($create_parents)) {
280
            foreach ($opts[1] as $dir) {
281
                $dirstack = array();
282
                while ((!file_exists($dir) || !is_dir($dir)) &&
283
                        $dir != DIRECTORY_SEPARATOR) {
284
                    array_unshift($dirstack, $dir);
285
                    $dir = dirname($dir);
286
                }
287
 
288
                while ($newdir = array_shift($dirstack)) {
289
                    if (!is_writeable(dirname($newdir))) {
290
                        $ret = false;
291
                        break;
292
                    }
293
 
294
                    if (!mkdir($newdir, $mode)) {
295
                        $ret = false;
296
                    }
297
                }
298
            }
299
        } else {
300
            foreach($opts[1] as $dir) {
301
                if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
302
                    $ret = false;
303
                }
304
            }
305
        }
306
 
307
        return $ret;
308
    }
309
 
310
    /**
311
     * Concatenate files
312
     *
313
     * Usage:
314
     * 1) $var = System::cat('sample.txt test.txt');
315
     * 2) System::cat('sample.txt test.txt > final.txt');
316
     * 3) System::cat('sample.txt test.txt >> final.txt');
317
     *
318
     * Note: as the class use fopen, urls should work also (test that)
319
     *
320
     * @param    string  $args   the arguments
321
     * @return   boolean true on success
322
     */
323
    public static function &cat($args)
324
    {
325
        $ret = null;
326
        $files = array();
327
        if (!is_array($args)) {
328
            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
329
        }
330
 
331
        $count_args = count($args);
332
        for ($i = 0; $i < $count_args; $i++) {
333
            if ($args[$i] == '>') {
334
                $mode = 'wb';
335
                $outputfile = $args[$i+1];
336
                break;
337
            } elseif ($args[$i] == '>>') {
338
                $mode = 'ab+';
339
                $outputfile = $args[$i+1];
340
                break;
341
            } else {
342
                $files[] = $args[$i];
343
            }
344
        }
345
        $outputfd = false;
346
        if (isset($mode)) {
347
            if (!$outputfd = fopen($outputfile, $mode)) {
348
                $err = System::raiseError("Could not open $outputfile");
349
                return $err;
350
            }
351
            $ret = true;
352
        }
353
        foreach ($files as $file) {
354
            if (!$fd = fopen($file, 'r')) {
355
                System::raiseError("Could not open $file");
356
                continue;
357
            }
358
            while ($cont = fread($fd, 2048)) {
359
                if (is_resource($outputfd)) {
360
                    fwrite($outputfd, $cont);
361
                } else {
362
                    $ret .= $cont;
363
                }
364
            }
365
            fclose($fd);
366
        }
367
        if (is_resource($outputfd)) {
368
            fclose($outputfd);
369
        }
370
        return $ret;
371
    }
372
 
373
    /**
374
     * Creates temporary files or directories. This function will remove
375
     * the created files when the scripts finish its execution.
376
     *
377
     * Usage:
378
     *   1) $tempfile = System::mktemp("prefix");
379
     *   2) $tempdir  = System::mktemp("-d prefix");
380
     *   3) $tempfile = System::mktemp();
381
     *   4) $tempfile = System::mktemp("-t /var/tmp prefix");
382
     *
383
     * prefix -> The string that will be prepended to the temp name
384
     *           (defaults to "tmp").
385
     * -d     -> A temporary dir will be created instead of a file.
386
     * -t     -> The target dir where the temporary (file|dir) will be created. If
387
     *           this param is missing by default the env vars TMP on Windows or
388
     *           TMPDIR in Unix will be used. If these vars are also missing
389
     *           c:\windows\temp or /tmp will be used.
390
     *
391
     * @param   string  $args  The arguments
392
     * @return  mixed   the full path of the created (file|dir) or false
393
     * @see System::tmpdir()
394
     */
395
    public static function mktemp($args = null)
396
    {
397
        static $first_time = true;
398
        $opts = System::_parseArgs($args, 't:d');
399
        if (PEAR::isError($opts)) {
400
            return System::raiseError($opts);
401
        }
402
 
403
        foreach ($opts[0] as $opt) {
404
            if ($opt[0] == 'd') {
405
                $tmp_is_dir = true;
406
            } elseif ($opt[0] == 't') {
407
                $tmpdir = $opt[1];
408
            }
409
        }
410
 
411
        $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
412
        if (!isset($tmpdir)) {
413
            $tmpdir = System::tmpdir();
414
        }
415
 
416
        if (!System::mkDir(array('-p', $tmpdir))) {
417
            return false;
418
        }
419
 
420
        $tmp = tempnam($tmpdir, $prefix);
421
        if (isset($tmp_is_dir)) {
422
            unlink($tmp); // be careful possible race condition here
423
            if (!mkdir($tmp, 0700)) {
424
                return System::raiseError("Unable to create temporary directory $tmpdir");
425
            }
426
        }
427
 
428
        $GLOBALS['_System_temp_files'][] = $tmp;
429
        if (isset($tmp_is_dir)) {
430
            //$GLOBALS['_System_temp_files'][] = dirname($tmp);
431
        }
432
 
433
        if ($first_time) {
434
            PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
435
            $first_time = false;
436
        }
437
 
438
        return $tmp;
439
    }
440
 
441
    /**
442
     * Remove temporary files created my mkTemp. This function is executed
443
     * at script shutdown time
444
     */
445
    public static function _removeTmpFiles()
446
    {
447
        if (count($GLOBALS['_System_temp_files'])) {
448
            $delete = $GLOBALS['_System_temp_files'];
449
            array_unshift($delete, '-r');
450
            System::rm($delete);
451
            $GLOBALS['_System_temp_files'] = array();
452
        }
453
    }
454
 
455
    /**
456
     * Get the path of the temporal directory set in the system
457
     * by looking in its environments variables.
458
     * Note: php.ini-recommended removes the "E" from the variables_order setting,
459
     * making unavaible the $_ENV array, that s why we do tests with _ENV
460
     *
461
     * @return string The temporary directory on the system
462
     */
463
    public static function tmpdir()
464
    {
465
        if (OS_WINDOWS) {
466
            if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
467
                return $var;
468
            }
469
            if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
470
                return $var;
471
            }
472
            if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
473
                return $var;
474
            }
475
            if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
476
                return $var;
477
            }
478
            return getenv('SystemRoot') . '\temp';
479
        }
480
        if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
481
            return $var;
482
        }
483
        return realpath('/tmp');
484
    }
485
 
486
    /**
487
     * The "which" command (show the full path of a command)
488
     *
489
     * @param string $program The command to search for
490
     * @param mixed  $fallback Value to return if $program is not found
491
     *
492
     * @return mixed A string with the full path or false if not found
493
     * @author Stig Bakken <ssb@php.net>
494
     */
495
    public static function which($program, $fallback = false)
496
    {
497
        // enforce API
498
        if (!is_string($program) || '' == $program) {
499
            return $fallback;
500
        }
501
 
502
        // full path given
503
        if (basename($program) != $program) {
504
            $path_elements[] = dirname($program);
505
            $program = basename($program);
506
        } else {
507
            $path = getenv('PATH');
508
            if (!$path) {
509
                $path = getenv('Path'); // some OSes are just stupid enough to do this
510
            }
511
 
512
            $path_elements = explode(PATH_SEPARATOR, $path);
513
        }
514
 
515
        if (OS_WINDOWS) {
516
            $exe_suffixes = getenv('PATHEXT')
517
                                ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
518
                                : array('.exe','.bat','.cmd','.com');
519
            // allow passing a command.exe param
520
            if (strpos($program, '.') !== false) {
521
                array_unshift($exe_suffixes, '');
522
            }
523
        } else {
524
            $exe_suffixes = array('');
525
        }
526
 
527
        foreach ($exe_suffixes as $suff) {
528
            foreach ($path_elements as $dir) {
529
                $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
530
                if (is_executable($file)) {
531
                    return $file;
532
                }
533
            }
534
        }
535
        return $fallback;
536
    }
537
 
538
    /**
539
     * The "find" command
540
     *
541
     * Usage:
542
     *
543
     * System::find($dir);
544
     * System::find("$dir -type d");
545
     * System::find("$dir -type f");
546
     * System::find("$dir -name *.php");
547
     * System::find("$dir -name *.php -name *.htm*");
548
     * System::find("$dir -maxdepth 1");
549
     *
550
     * Params implmented:
551
     * $dir            -> Start the search at this directory
552
     * -type d         -> return only directories
553
     * -type f         -> return only files
554
     * -maxdepth <n>   -> max depth of recursion
555
     * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
556
     *
557
     * @param  mixed Either array or string with the command line
558
     * @return array Array of found files
559
     */
560
    public static function find($args)
561
    {
562
        if (!is_array($args)) {
563
            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
564
        }
565
        $dir = realpath(array_shift($args));
566
        if (!$dir) {
567
            return array();
568
        }
569
        $patterns = array();
570
        $depth = 0;
571
        $do_files = $do_dirs = true;
572
        $args_count = count($args);
573
        for ($i = 0; $i < $args_count; $i++) {
574
            switch ($args[$i]) {
575
                case '-type':
576
                    if (in_array($args[$i+1], array('d', 'f'))) {
577
                        if ($args[$i+1] == 'd') {
578
                            $do_files = false;
579
                        } else {
580
                            $do_dirs = false;
581
                        }
582
                    }
583
                    $i++;
584
                    break;
585
                case '-name':
586
                    $name = preg_quote($args[$i+1], '#');
587
                    // our magic characters ? and * have just been escaped,
588
                    // so now we change the escaped versions to PCRE operators
589
                    $name = strtr($name, array('\?' => '.', '\*' => '.*'));
590
                    $patterns[] = '('.$name.')';
591
                    $i++;
592
                    break;
593
                case '-maxdepth':
594
                    $depth = $args[$i+1];
595
                    break;
596
            }
597
        }
598
        $path = System::_dirToStruct($dir, $depth, 0, true);
599
        if ($do_files && $do_dirs) {
600
            $files = array_merge($path['files'], $path['dirs']);
601
        } elseif ($do_dirs) {
602
            $files = $path['dirs'];
603
        } else {
604
            $files = $path['files'];
605
        }
606
        if (count($patterns)) {
607
            $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
608
            $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
609
            $ret = array();
610
            $files_count = count($files);
611
            for ($i = 0; $i < $files_count; $i++) {
612
                // only search in the part of the file below the current directory
613
                $filepart = basename($files[$i]);
614
                if (preg_match($pattern, $filepart)) {
615
                    $ret[] = $files[$i];
616
                }
617
            }
618
            return $ret;
619
        }
620
        return $files;
621
    }
622
}