[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/library/ezc/Base/src/ -> file.php (source)

   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  ?>


Generated: Thu Jul 28 15:48:31 2011 Cross-referenced by PHPXref 0.7