PageRenderTime 170ms CodeModel.GetById 142ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/common/libraries/plugin/pear/System.php

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