| [ Index ] |
PHP Cross Reference of MantisBT |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved. 4 * @license http://ez.no/licenses/new_bsd New BSD License 5 * @version 1.8 6 * @filesource 7 * @package Base 8 */ 9 10 /** 11 * Provides a selection of static independent methods to provide functionality 12 * for file and file system handling. 13 * 14 * This example shows how to use the findRecursive method: 15 * <code> 16 * <?php 17 * // lists all the files under /etc (including subdirectories) that end in 18 * // .conf 19 * $confFiles = ezcBaseFile::findRecursive( "/etc", array( '@\.conf$@' ) ); 20 * 21 * // lists all autoload files in the components source tree and excludes the 22 * // ones in the autoload subdirectory. Statistics are returned in the $stats 23 * // variable which is passed by reference. 24 * $files = ezcBaseFile::findRecursive( 25 * "/dat/dev/ezcomponents", 26 * array( '@src/.*_autoload.php$@' ), 27 * array( '@/autoload/@' ), 28 * $stats 29 * ); 30 * 31 * // lists all binaries in /bin except the ones starting with a "g" 32 * $data = ezcBaseFile::findRecursive( "/bin", array(), array( '@^/bin/g@' ) ); 33 * ?> 34 * </code> 35 * 36 * @package Base 37 * @version 1.8 38 * @mainclass 39 */ 40 class ezcBaseFile 41 { 42 /** 43 * This is the callback used by findRecursive to collect data. 44 * 45 * This callback method works together with walkRecursive() and is called 46 * for every file/and or directory. The $context is a callback specific 47 * container in which data can be stored and shared between the different 48 * calls to the callback function. The walkRecursive() function also passes 49 * in the full absolute directory in $sourceDir, the filename in $fileName 50 * and file information (such as size, modes, types) as an array as 51 * returned by PHP's stat() in the $fileInfo parameter. 52 * 53 * @param ezcBaseFileFindContext $context 54 * @param string $sourceDir 55 * @param string $fileName 56 * @param array(stat) $fileInfo 57 */ 58 static protected function findRecursiveCallback( ezcBaseFileFindContext $context, $sourceDir, $fileName, $fileInfo ) 59 { 60 // ignore if we have a directory 61 if ( $fileInfo['mode'] & 0x4000 ) 62 { 63 return; 64 } 65 66 // update the statistics 67 $context->elements[] = $sourceDir . DIRECTORY_SEPARATOR . $fileName; 68 $context->count++; 69 $context->size += $fileInfo['size']; 70 } 71 72 /** 73 * Walks files and directories recursively on a file system 74 * 75 * This method walks over a directory and calls a callback from every file 76 * and directory it finds. You can use $includeFilters to include only 77 * specific files, and $excludeFilters to exclude certain files from being 78 * returned. The function will always go into subdirectories even if the 79 * entry would not have passed the filters. 80 * 81 * The callback is passed in the $callback parameter, and the 82 * $callbackContext will be send to the callback function/method as 83 * parameter so that you can store data in there that persists with all the 84 * calls and recursive calls to this method. It's up to the callback method 85 * to do something useful with this. The callback function's parameters are 86 * in order: 87 * 88 * <ul> 89 * <li>ezcBaseFileFindContext $context</li> 90 * <li>string $sourceDir</li> 91 * <li>string $fileName</li> 92 * <li>array(stat) $fileInfo</li> 93 * </ul> 94 * 95 * See {@see findRecursiveCallback()} for an example of a callback function. 96 * 97 * Filters are regular expressions and are therefore required to have 98 * starting and ending delimiters. The Perl Compatible syntax is used as 99 * regular expression language. 100 * 101 * @param string $sourceDir 102 * @param array(string) $includeFilters 103 * @param array(string) $excludeFilters 104 * @param callback $callback 105 * @param mixed $callbackContext 106 * 107 * @throws ezcBaseFileNotFoundException if the $sourceDir directory is not 108 * a directory or does not exist. 109 * @throws ezcBaseFilePermissionException if the $sourceDir directory could 110 * not be opened for reading. 111 * @return array 112 */ 113 static public function walkRecursive( $sourceDir, array $includeFilters = array(), array $excludeFilters = array(), $callback, &$callbackContext ) 114 { 115 if ( !is_dir( $sourceDir ) ) 116 { 117 throw new ezcBaseFileNotFoundException( $sourceDir, 'directory' ); 118 } 119 $elements = array(); 120 $d = @dir( $sourceDir ); 121 if ( !$d ) 122 { 123 throw new ezcBaseFilePermissionException( $sourceDir, ezcBaseFileException::READ ); 124 } 125 126 while ( ( $entry = $d->read() ) !== false ) 127 { 128 if ( $entry == '.' || $entry == '..' ) 129 { 130 continue; 131 } 132 133 $fileInfo = @stat( $sourceDir . DIRECTORY_SEPARATOR . $entry ); 134 if ( !$fileInfo ) 135 { 136 $fileInfo = array( 'size' => 0, 'mode' => 0 ); 137 } 138 139 if ( $fileInfo['mode'] & 0x4000 ) 140 { 141 // We need to ignore the Permission exceptions here as it can 142 // be normal that a directory can not be accessed. We only need 143 // the exception if the top directory could not be read. 144 try 145 { 146 call_user_func_array( $callback, array( $callbackContext, $sourceDir, $entry, $fileInfo ) ); 147 $subList = self::walkRecursive( $sourceDir . DIRECTORY_SEPARATOR . $entry, $includeFilters, $excludeFilters, $callback, $callbackContext ); 148 $elements = array_merge( $elements, $subList ); 149 } 150 catch ( ezcBaseFilePermissionException $e ) 151 { 152 } 153 } 154 else 155 { 156 // By default a file is included in the return list 157 $ok = true; 158 // Iterate over the $includeFilters and prohibit the file from 159 // being returned when atleast one of them does not match 160 foreach ( $includeFilters as $filter ) 161 { 162 if ( !preg_match( $filter, $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) 163 { 164 $ok = false; 165 break; 166 } 167 } 168 // Iterate over the $excludeFilters and prohibit the file from 169 // being returns when atleast one of them matches 170 foreach ( $excludeFilters as $filter ) 171 { 172 if ( preg_match( $filter, $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) 173 { 174 $ok = false; 175 break; 176 } 177 } 178 179 // If everything's allright, call the callback and add the 180 // entry to the elements array 181 if ( $ok ) 182 { 183 call_user_func( $callback, $callbackContext, $sourceDir, $entry, $fileInfo ); 184 $elements[] = $sourceDir . DIRECTORY_SEPARATOR . $entry; 185 } 186 } 187 } 188 sort( $elements ); 189 return $elements; 190 } 191 192 /** 193 * Finds files recursively on a file system 194 * 195 * With this method you can scan the file system for files. You can use 196 * $includeFilters to include only specific files, and $excludeFilters to 197 * exclude certain files from being returned. The function will always go 198 * into subdirectories even if the entry would not have passed the filters. 199 * It uses the {@see walkRecursive()} method to do the actually recursion. 200 * 201 * Filters are regular expressions and are therefore required to have 202 * starting and ending delimiters. The Perl Compatible syntax is used as 203 * regular expression language. 204 * 205 * If you pass an empty array to the $statistics argument, the function 206 * will in details about the number of files found into the 'count' array 207 * element, and the total filesize in the 'size' array element. Because this 208 * argument is passed by reference, you *have* to pass a variable and you 209 * can not pass a constant value such as "array()". 210 * 211 * @param string $sourceDir 212 * @param array(string) $includeFilters 213 * @param array(string) $excludeFilters 214 * @param array() $statistics 215 * 216 * @throws ezcBaseFileNotFoundException if the $sourceDir directory is not 217 * a directory or does not exist. 218 * @throws ezcBaseFilePermissionException if the $sourceDir directory could 219 * not be opened for reading. 220 * @return array 221 */ 222 static public function findRecursive( $sourceDir, array $includeFilters = array(), array $excludeFilters = array(), &$statistics = null ) 223 { 224 // init statistics array 225 if ( !is_array( $statistics ) || !array_key_exists( 'size', $statistics ) || !array_key_exists( 'count', $statistics ) ) 226 { 227 $statistics['size'] = 0; 228 $statistics['count'] = 0; 229 } 230 231 // create the context, and then start walking over the array 232 $context = new ezcBaseFileFindContext; 233 self::walkRecursive( $sourceDir, $includeFilters, $excludeFilters, array( 'ezcBaseFile', 'findRecursiveCallback' ), $context ); 234 235 // collect the statistics 236 $statistics['size'] = $context->size; 237 $statistics['count'] = $context->count; 238 239 // return the found and pattern-matched files 240 sort( $context->elements ); 241 return $context->elements; 242 } 243 244 245 /** 246 * Removes files and directories recursively from a file system 247 * 248 * This method recursively removes the $directory and all its contents. 249 * You should be <b>extremely</b> careful with this method as it has the 250 * potential to erase everything that the current user has access to. 251 * 252 * @param string $directory 253 */ 254 static public function removeRecursive( $directory ) 255 { 256 $sourceDir = realpath( $directory ); 257 if ( !$sourceDir ) 258 { 259 throw new ezcBaseFileNotFoundException( $directory, 'directory' ); 260 } 261 $d = @dir( $sourceDir ); 262 if ( !$d ) 263 { 264 throw new ezcBaseFilePermissionException( $directory, ezcBaseFileException::READ ); 265 } 266 // check if we can remove the dir 267 $parentDir = realpath( $directory . DIRECTORY_SEPARATOR . '..' ); 268 if ( !is_writable( $parentDir ) ) 269 { 270 throw new ezcBaseFilePermissionException( $parentDir, ezcBaseFileException::WRITE ); 271 } 272 // loop over contents 273 while ( ( $entry = $d->read() ) !== false ) 274 { 275 if ( $entry == '.' || $entry == '..' ) 276 { 277 continue; 278 } 279 280 if ( is_dir( $sourceDir . DIRECTORY_SEPARATOR . $entry ) ) 281 { 282 self::removeRecursive( $sourceDir . DIRECTORY_SEPARATOR . $entry ); 283 } 284 else 285 { 286 if ( @unlink( $sourceDir . DIRECTORY_SEPARATOR . $entry ) === false ) 287 { 288 throw new ezcBaseFilePermissionException( $directory . DIRECTORY_SEPARATOR . $entry, ezcBaseFileException::REMOVE ); 289 } 290 } 291 } 292 $d->close(); 293 rmdir( $sourceDir ); 294 } 295 296 /** 297 * Recursively copy a file or directory. 298 * 299 * Recursively copy a file or directory in $source to the given 300 * destination. If a depth is given, the operation will stop, if the given 301 * recursion depth is reached. A depth of -1 means no limit, while a depth 302 * of 0 means, that only the current file or directory will be copied, 303 * without any recursion. 304 * 305 * You may optionally define modes used to create files and directories. 306 * 307 * @throws ezcBaseFileNotFoundException 308 * If the $sourceDir directory is not a directory or does not exist. 309 * @throws ezcBaseFilePermissionException 310 * If the $sourceDir directory could not be opened for reading, or the 311 * destination is not writeable. 312 * 313 * @param string $source 314 * @param string $destination 315 * @param int $depth 316 * @param int $dirMode 317 * @param int $fileMode 318 * @return void 319 */ 320 static public function copyRecursive( $source, $destination, $depth = -1, $dirMode = 0775, $fileMode = 0664 ) 321 { 322 // Check if source file exists at all. 323 if ( !is_file( $source ) && !is_dir( $source ) ) 324 { 325 throw new ezcBaseFileNotFoundException( $source ); 326 } 327 328 // Destination file should NOT exist 329 if ( is_file( $destination ) || is_dir( $destination ) ) 330 { 331 throw new ezcBaseFilePermissionException( $destination, ezcBaseFileException::WRITE ); 332 } 333 334 // Skip non readable files in source directory 335 if ( !is_readable( $source ) ) 336 { 337 return; 338 } 339 340 // Copy 341 if ( is_dir( $source ) ) 342 { 343 mkdir( $destination ); 344 // To ignore umask, umask() should not be changed with 345 // multithreaded servers... 346 chmod( $destination, $dirMode ); 347 } 348 elseif ( is_file( $source ) ) 349 { 350 copy( $source, $destination ); 351 chmod( $destination, $fileMode ); 352 } 353 354 if ( ( $depth === 0 ) || 355 ( !is_dir( $source ) ) ) 356 { 357 // Do not recurse (any more) 358 return; 359 } 360 361 // Recurse 362 $dh = opendir( $source ); 363 while ( ( $file = readdir( $dh ) ) !== false ) 364 { 365 if ( ( $file === '.' ) || 366 ( $file === '..' ) ) 367 { 368 continue; 369 } 370 371 self::copyRecursive( 372 $source . '/' . $file, 373 $destination . '/' . $file, 374 $depth - 1, $dirMode, $fileMode 375 ); 376 } 377 } 378 379 /** 380 * Calculates the relative path of the file/directory '$path' to a given 381 * $base path. 382 * 383 * $path and $base should be fully absolute paths. This function returns the 384 * answer of "How do I go from $base to $path". If the $path and $base are 385 * the same path, the function returns '.'. This method does not touch the 386 * filesystem. 387 * 388 * @param string $path 389 * @param string $base 390 * @return string 391 */ 392 static public function calculateRelativePath( $path, $base ) 393 { 394 // Sanitize the paths to use the correct directory separator for the platform 395 $path = strtr( $path, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ); 396 $base = strtr( $base, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ); 397 398 $base = explode( DIRECTORY_SEPARATOR, $base ); 399 $path = explode( DIRECTORY_SEPARATOR, $path ); 400 401 // If the paths are the same we return 402 if ( $base === $path ) 403 { 404 return '.'; 405 } 406 407 $result = ''; 408 409 $pathPart = array_shift( $path ); 410 $basePart = array_shift( $base ); 411 while ( $pathPart == $basePart ) 412 { 413 $pathPart = array_shift( $path ); 414 $basePart = array_shift( $base ); 415 } 416 417 if ( $pathPart != null ) 418 { 419 array_unshift( $path, $pathPart ); 420 } 421 if ( $basePart != null ) 422 { 423 array_unshift( $base, $basePart ); 424 } 425 426 $result = str_repeat( '..' . DIRECTORY_SEPARATOR, count( $base ) ); 427 // prevent a trailing DIRECTORY_SEPARATOR in case there is only a .. 428 if ( count( $path ) == 0 ) 429 { 430 $result = substr( $result, 0, -strlen( DIRECTORY_SEPARATOR ) ); 431 } 432 $result .= join( DIRECTORY_SEPARATOR, $path ); 433 434 return $result; 435 } 436 437 /** 438 * Returns whether the passed $path is an absolute path, giving the current $os. 439 * 440 * With the $os parameter you can tell this function to use the semantics 441 * for a different operating system to determine whether a path is 442 * absolute. The $os argument defaults to the OS that the script is running 443 * on. 444 * 445 * @param string $path 446 * @param string $os 447 * @return bool 448 */ 449 public static function isAbsolutePath( $path, $os = null ) 450 { 451 if ( $os === null ) 452 { 453 $os = ezcBaseFeatures::os(); 454 } 455 456 // Stream wrapper like phar can also be considered absolute paths 457 if ( preg_match( '(^[a-z]{3,}://)S', $path ) ) 458 { 459 return true; 460 } 461 462 switch ( $os ) 463 { 464 case 'Windows': 465 // Sanitize the paths to use the correct directory separator for the platform 466 $path = strtr( $path, '\\/', '\\\\' ); 467 468 // Absolute paths with drive letter: X:\ 469 if ( preg_match( '@^[A-Z]:\\\\@i', $path ) ) 470 { 471 return true; 472 } 473 474 // Absolute paths with network paths: \\server\share\ 475 if ( preg_match( '@^\\\\\\\\[A-Z]+\\\\[^\\\\]@i', $path ) ) 476 { 477 return true; 478 } 479 break; 480 case 'Mac': 481 case 'Linux': 482 case 'FreeBSD': 483 default: 484 // Sanitize the paths to use the correct directory separator for the platform 485 $path = strtr( $path, '\\/', '//' ); 486 487 if ( $path[0] == '/' ) 488 { 489 return true; 490 } 491 } 492 return false; 493 } 494 } 495 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Jul 28 15:48:31 2011 | Cross-referenced by PHPXref 0.7 |