3 * File/Directory manipulation
\r
5 * PHP versions 4 and 5
\r
7 * LICENSE: This source file is subject to version 3.0 of the PHP license
\r
8 * that is available through the world-wide-web at the following URI:
\r
9 * http://www.php.net/license/3_0.txt. If you did not receive a copy of
\r
10 * the PHP License and are unable to obtain it through the web, please
\r
11 * send a note to license@php.net so we can mail you a copy immediately.
\r
15 * @author Tomas V.V.Cox <cox@idecnet.com>
\r
16 * @copyright 1997-2008 The PHP Group
\r
17 * @license http://www.php.net/license/3_0.txt PHP License 3.0
\r
18 * @version CVS: $Id$
\r
19 * @link http://pear.php.net/package/PEAR
\r
20 * @since File available since Release 0.1
\r
26 require_once 'PEAR.php';
\r
27 require_once 'Console/Getopt.php';
\r
29 $GLOBALS['_System_temp_files'] = array();
\r
32 * System offers cross plattform compatible system functions
\r
34 * Static functions for different operations. Should work under
\r
35 * Unix and Windows. The names and usage has been taken from its respectively
\r
36 * GNU commands. The functions will return (bool) false on error and will
\r
37 * trigger the error with the PHP trigger_error() function (you can silence
\r
38 * the error by prefixing a '@' sign after the function call).
\r
40 * Documentation on this class you can find in:
\r
41 * http://pear.php.net/manual/
\r
44 * if (!@System::rm('-r file1 dir1')) {
\r
45 * print "could not delete file1 or dir1";
\r
48 * In case you need to to pass file names with spaces,
\r
49 * pass the params as an array:
\r
51 * System::rm(array('-r', $file1, $dir1));
\r
55 * @author Tomas V.V. Cox <cox@idecnet.com>
\r
56 * @copyright 1997-2008 The PHP Group
\r
57 * @license http://www.php.net/license/3_0.txt PHP License 3.0
\r
58 * @version Release: 1.4.4
\r
59 * @link http://pear.php.net/package/PEAR
\r
60 * @since Class available since Release 0.1
\r
65 * returns the commandline arguments of a function
\r
67 * @param string $argv the commandline
\r
68 * @param string $short_options the allowed option short-tags
\r
69 * @param string $long_options the allowed option long-tags
\r
70 * @return array the given options and there values
\r
73 function _parseArgs($argv, $short_options, $long_options = null)
\r
75 if (!is_array($argv) && $argv !== null) {
\r
76 $argv = preg_split('/\s+/', $argv, -1, PREG_SPLIT_NO_EMPTY);
\r
78 return Console_Getopt::getopt2($argv, $short_options);
\r
82 * Output errors with PHP trigger_error(). You can silence the errors
\r
83 * with prefixing a "@" sign to the function call: @System::mkdir(..);
\r
85 * @param mixed $error a PEAR error or a string with the error message
\r
86 * @return bool false
\r
89 function raiseError($error)
\r
91 if (PEAR::isError($error)) {
\r
92 $error = $error->getMessage();
\r
94 trigger_error($error, E_USER_WARNING);
\r
99 * Creates a nested array representing the structure of a directory
\r
101 * System::_dirToStruct('dir1', 0) =>
\r
111 * [0] => dir1/file2
\r
112 * [1] => dir1/file3
\r
115 * @param string $sPath Name of the directory
\r
116 * @param integer $maxinst max. deep of the lookup
\r
117 * @param integer $aktinst starting deep of the lookup
\r
118 * @return array the structure of the dir
\r
122 function _dirToStruct($sPath, $maxinst, $aktinst = 0)
\r
124 $struct = array('dirs' => array(), 'files' => array());
\r
125 if (($dir = @opendir($sPath)) === false) {
\r
126 System::raiseError("Could not open dir $sPath");
\r
127 return $struct; // XXX could not open error
\r
129 $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
\r
131 while (false !== ($file = readdir($dir))) {
\r
132 if ($file != '.' && $file != '..') {
\r
138 if ($aktinst < $maxinst || $maxinst == 0) {
\r
139 foreach($list as $val) {
\r
140 $path = $sPath . DIRECTORY_SEPARATOR . $val;
\r
141 if (is_dir($path) && !is_link($path)) {
\r
142 $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1);
\r
143 $struct = array_merge_recursive($tmp, $struct);
\r
145 $struct['files'][] = $path;
\r
153 * Creates a nested array representing the structure of a directory and files
\r
155 * @param array $files Array listing files and dirs
\r
157 * @see System::_dirToStruct()
\r
159 function _multipleToStruct($files)
\r
161 $struct = array('dirs' => array(), 'files' => array());
\r
162 settype($files, 'array');
\r
163 foreach ($files as $file) {
\r
164 if (is_dir($file) && !is_link($file)) {
\r
165 $tmp = System::_dirToStruct($file, 0);
\r
166 $struct = array_merge_recursive($tmp, $struct);
\r
168 $struct['files'][] = $file;
\r
175 * The rm command for removing files.
\r
176 * Supports multiple files and dirs and also recursive deletes
\r
178 * @param string $args the arguments for rm
\r
179 * @return mixed PEAR_Error or true for success
\r
184 $opts = System::_parseArgs($args, 'rf'); // "f" do nothing but like it :-)
\r
185 if (PEAR::isError($opts)) {
\r
186 return System::raiseError($opts);
\r
188 foreach($opts[0] as $opt) {
\r
189 if ($opt[0] == 'r') {
\r
190 $do_recursive = true;
\r
194 if (isset($do_recursive)) {
\r
195 $struct = System::_multipleToStruct($opts[1]);
\r
196 foreach($struct['files'] as $file) {
\r
197 if (!@unlink($file)) {
\r
201 foreach($struct['dirs'] as $dir) {
\r
202 if (!@rmdir($dir)) {
\r
207 foreach ($opts[1] as $file) {
\r
208 $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
\r
209 if (!@$delete($file)) {
\r
218 * Make directories.
\r
220 * The -p option will create parent directories
\r
221 * @param string $args the name of the director(y|ies) to create
\r
222 * @return bool True for success
\r
225 function mkDir($args)
\r
227 $opts = System::_parseArgs($args, 'pm:');
\r
228 if (PEAR::isError($opts)) {
\r
229 return System::raiseError($opts);
\r
231 $mode = 0777; // default mode
\r
232 foreach($opts[0] as $opt) {
\r
233 if ($opt[0] == 'p') {
\r
234 $create_parents = true;
\r
235 } elseif($opt[0] == 'm') {
\r
236 // if the mode is clearly an octal number (starts with 0)
\r
237 // convert it to decimal
\r
238 if (strlen($opt[1]) && $opt[1]{0} == '0') {
\r
239 $opt[1] = octdec($opt[1]);
\r
248 if (isset($create_parents)) {
\r
249 foreach($opts[1] as $dir) {
\r
250 $dirstack = array();
\r
251 while (!@is_dir($dir) && $dir != DIRECTORY_SEPARATOR) {
\r
252 array_unshift($dirstack, $dir);
\r
253 $dir = dirname($dir);
\r
255 while ($newdir = array_shift($dirstack)) {
\r
256 if (!is_writeable(dirname($newdir))) {
\r
260 if (!mkdir($newdir, $mode)) {
\r
266 foreach($opts[1] as $dir) {
\r
267 if (!@is_dir($dir) && !mkdir($dir, $mode)) {
\r
276 * Concatenate files
\r
279 * 1) $var = System::cat('sample.txt test.txt');
\r
280 * 2) System::cat('sample.txt test.txt > final.txt');
\r
281 * 3) System::cat('sample.txt test.txt >> final.txt');
\r
283 * Note: as the class use fopen, urls should work also (test that)
\r
285 * @param string $args the arguments
\r
286 * @return boolean true on success
\r
289 function &cat($args)
\r
293 if (!is_array($args)) {
\r
294 $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
\r
296 for($i=0; $i < count($args); $i++) {
\r
297 if ($args[$i] == '>') {
\r
299 $outputfile = $args[$i+1];
\r
301 } elseif ($args[$i] == '>>') {
\r
303 $outputfile = $args[$i+1];
\r
306 $files[] = $args[$i];
\r
309 if (isset($mode)) {
\r
310 if (!$outputfd = fopen($outputfile, $mode)) {
\r
311 $err = System::raiseError("Could not open $outputfile");
\r
316 foreach ($files as $file) {
\r
317 if (!$fd = fopen($file, 'r')) {
\r
318 System::raiseError("Could not open $file");
\r
321 while ($cont = fread($fd, 2048)) {
\r
322 if (isset($outputfd)) {
\r
323 fwrite($outputfd, $cont);
\r
330 if (@is_resource($outputfd)) {
\r
337 * Creates temporary files or directories. This function will remove
\r
338 * the created files when the scripts finish its execution.
\r
341 * 1) $tempfile = System::mktemp("prefix");
\r
342 * 2) $tempdir = System::mktemp("-d prefix");
\r
343 * 3) $tempfile = System::mktemp();
\r
344 * 4) $tempfile = System::mktemp("-t /var/tmp prefix");
\r
346 * prefix -> The string that will be prepended to the temp name
\r
347 * (defaults to "tmp").
\r
348 * -d -> A temporary dir will be created instead of a file.
\r
349 * -t -> The target dir where the temporary (file|dir) will be created. If
\r
350 * this param is missing by default the env vars TMP on Windows or
\r
351 * TMPDIR in Unix will be used. If these vars are also missing
\r
352 * c:\windows\temp or /tmp will be used.
\r
354 * @param string $args The arguments
\r
355 * @return mixed the full path of the created (file|dir) or false
\r
356 * @see System::tmpdir()
\r
359 function mktemp($args = null)
\r
361 static $first_time = true;
\r
362 $opts = System::_parseArgs($args, 't:d');
\r
363 if (PEAR::isError($opts)) {
\r
364 return System::raiseError($opts);
\r
366 foreach($opts[0] as $opt) {
\r
367 if($opt[0] == 'd') {
\r
368 $tmp_is_dir = true;
\r
369 } elseif($opt[0] == 't') {
\r
373 $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
\r
374 if (!isset($tmpdir)) {
\r
375 $tmpdir = System::tmpdir();
\r
377 if (!System::mkDir(array('-p', $tmpdir))) {
\r
380 $tmp = tempnam($tmpdir, $prefix);
\r
381 if (isset($tmp_is_dir)) {
\r
382 unlink($tmp); // be careful possible race condition here
\r
383 if (!mkdir($tmp, 0700)) {
\r
384 return System::raiseError("Unable to create temporary directory $tmpdir");
\r
387 $GLOBALS['_System_temp_files'][] = $tmp;
\r
389 PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
\r
390 $first_time = false;
\r
396 * Remove temporary files created my mkTemp. This function is executed
\r
397 * at script shutdown time
\r
401 function _removeTmpFiles()
\r
403 if (count($GLOBALS['_System_temp_files'])) {
\r
404 $delete = $GLOBALS['_System_temp_files'];
\r
405 array_unshift($delete, '-r');
\r
406 System::rm($delete);
\r
407 $GLOBALS['_System_temp_files'] = array();
\r
412 * Get the path of the temporal directory set in the system
\r
413 * by looking in its environments variables.
\r
414 * Note: php.ini-recommended removes the "E" from the variables_order setting,
\r
415 * making unavaible the $_ENV array, that s why we do tests with _ENV
\r
417 * @return string The temporal directory on the system
\r
422 if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
\r
425 if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
\r
428 if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
\r
431 return getenv('SystemRoot') . '\temp';
\r
433 if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
\r
440 * The "which" command (show the full path of a command)
\r
442 * @param string $program The command to search for
\r
443 * @param mixed $fallback Value to return if $program is not found
\r
445 * @return mixed A string with the full path or false if not found
\r
446 * @author Stig Bakken <ssb@php.net>
\r
448 function which($program, $fallback = false)
\r
450 // avaible since 4.3.0RC2
\r
451 if (defined('PATH_SEPARATOR')) {
\r
452 $path_delim = PATH_SEPARATOR;
\r
454 $path_delim = OS_WINDOWS ? ';' : ':';
\r
457 if (basename($program) != $program) {
\r
458 $path_elements[] = dirname($program);
\r
459 $program = basename($program);
\r
462 if (!ini_get('safe_mode') || !$path = ini_get('safe_mode_exec_dir')) {
\r
463 $path = getenv('PATH');
\r
465 $path = getenv('Path'); // some OSes are just stupid enough to do this
\r
468 $path_elements = explode($path_delim, $path);
\r
472 $exe_suffixes = getenv('PATHEXT')
\r
473 ? explode($path_delim, getenv('PATHEXT'))
\r
474 : array('.exe','.bat','.cmd','.com');
\r
475 // allow passing a command.exe param
\r
476 if (strpos($program, '.') !== false) {
\r
477 array_unshift($exe_suffixes, '');
\r
479 // is_executable() is not available on windows for PHP4
\r
480 $pear_is_executable = (function_exists('is_executable')) ? 'is_executable' : 'is_file';
\r
482 $exe_suffixes = array('');
\r
483 $pear_is_executable = 'is_executable';
\r
486 foreach ($exe_suffixes as $suff) {
\r
487 foreach ($path_elements as $dir) {
\r
488 $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
\r
489 if ($pear_is_executable($file)) {
\r
498 * The "find" command
\r
502 * System::find($dir);
\r
503 * System::find("$dir -type d");
\r
504 * System::find("$dir -type f");
\r
505 * System::find("$dir -name *.php");
\r
506 * System::find("$dir -name *.php -name *.htm*");
\r
507 * System::find("$dir -maxdepth 1");
\r
509 * Params implmented:
\r
510 * $dir -> Start the search at this directory
\r
511 * -type d -> return only directories
\r
512 * -type f -> return only files
\r
513 * -maxdepth <n> -> max depth of recursion
\r
514 * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
\r
516 * @param mixed Either array or string with the command line
\r
517 * @return array Array of found files
\r
520 function find($args)
\r
522 if (!is_array($args)) {
\r
523 $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
\r
525 $dir = array_shift($args);
\r
526 $patterns = array();
\r
528 $do_files = $do_dirs = true;
\r
529 for ($i = 0; $i < count($args); $i++) {
\r
530 switch ($args[$i]) {
\r
532 if (in_array($args[$i+1], array('d', 'f'))) {
\r
533 if ($args[$i+1] == 'd') {
\r
543 if ($args[$i+1]{0} == '\\') {
\r
545 $args[$i+1] = addslashes(substr(getcwd(), 0, 2) . $args[$i + 1]);
\r
548 $patterns[] = "(" . preg_replace(array('/\./', '/\*/'),
\r
549 array('\.', '.*', ),
\r
555 $depth = $args[$i+1];
\r
559 $path = System::_dirToStruct($dir, $depth);
\r
560 if ($do_files && $do_dirs) {
\r
561 $files = array_merge($path['files'], $path['dirs']);
\r
562 } elseif ($do_dirs) {
\r
563 $files = $path['dirs'];
\r
565 $files = $path['files'];
\r
567 if (count($patterns)) {
\r
568 $patterns = implode('|', $patterns);
\r
570 for ($i = 0; $i < count($files); $i++) {
\r
571 if (preg_match("#^$patterns\$#", $files[$i])) {
\r
572 $ret[] = $files[$i];
\r