eZ Publish  [trunk]
ezdir.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZDir class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package lib
00009  */
00010 
00011 /*!
00012   \class eZDir ezdir.php
00013   \brief The class eZDir does
00014 
00015 */
00016 class eZDir
00017 {
00018     const SEPARATOR_LOCAL = 1;
00019     const SEPARATOR_UNIX = 2;
00020     const SEPARATOR_DOS = 3;
00021 
00022     /*!
00023      \return a multi-level path from a specific key. For example:
00024      \code
00025      echo createMultiLevelPath( "42abce", 3 );
00026      \endcode
00027      returns "/4/2/abce"
00028 
00029      Parameters:
00030      $key:      the key to be used as path
00031      $maxDepth: the maximum number of path elements to be created (-1 is unlimited)
00032      \static
00033     */
00034     static function createMultiLevelPath( $key, $maxDepth = -1 )
00035     {
00036         $parts = preg_split("//", (string) $key, $maxDepth, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
00037         $sep = eZDir::separator( self::SEPARATOR_LOCAL );
00038         return $sep . join($sep, $parts);
00039     }
00040 
00041     static function getPathFromFilename( $filename )
00042     {
00043         $ini = eZINI::instance();
00044         $dirDepth = $ini->variable( "FileSettings" , "DirDepth" );
00045         $pathArray = array();
00046         for ( $i = 0; $i < $dirDepth and $i < strlen( $filename ); $i++ )
00047         {
00048             $pathArray[] = substr( $filename, $i, 1 );
00049         }
00050         $path = implode( '/', $pathArray );
00051 
00052         return $path;
00053     }
00054 
00055     static function filenamePath( $filename, $maxCharLen = 2 )
00056     {
00057         $path = '';
00058         for ( $i = 0; $i < strlen( $filename ) and ( strlen( $filename ) - $i ) > $maxCharLen;
00059               $i++ )
00060         {
00061             $path = $path . substr( $filename, $i, 1 ) . '/';
00062         }
00063 
00064         return $path;
00065     }
00066 
00067     /*!
00068      \static
00069      Creates the directory \a $dir with permissions \a $perm.
00070      If \a $recursive is true it will create any missing parent directories,
00071      just like 'mkdir -p'.
00072     */
00073     static function mkdir( $dir, $perm = false, $recursive = false )
00074     {
00075         if ( file_exists( $dir ) )
00076             return false;
00077 
00078         if ( $perm === false )
00079         {
00080             $perm = eZDir::directoryPermission();
00081         }
00082         $dir = eZDir::cleanPath( $dir, self::SEPARATOR_UNIX );
00083 
00084         $oldumask = umask( 0 );
00085         $success = @mkdir( $dir, $perm, $recursive );
00086         umask( $oldumask );
00087         return $success;
00088     }
00089 
00090     /*!
00091      \static
00092      Goes trough the directory path \a $dir and removes empty directories.
00093      \note This is just the opposite of mkdir() with \a $parents set to \c true.
00094     */
00095     static function cleanupEmptyDirectories( $dir )
00096     {
00097         $dir = self::cleanPath( $dir, self::SEPARATOR_UNIX );
00098         $dirElements = explode( '/', $dir );
00099         if ( count( $dirElements ) == 0 )
00100             return true;
00101         $currentDir = $dirElements[0];
00102         $result = true;
00103         if ( !file_exists( $currentDir ) and $currentDir != "" )
00104             $result = self::doMkdir( $currentDir, self::directoryPermission() );
00105         if ( !$result )
00106             return false;
00107 
00108         for ( $i = count( $dirElements ); $i > 0; --$i )
00109         {
00110             $dirpath = implode( '/', array_slice( $dirElements, 0, $i ) );
00111             if ( file_exists( $dirpath ) and
00112                  is_dir( $dirpath ) )
00113             {
00114                 $rmdirStatus = @rmdir( $dirpath );
00115                 if ( !$rmdirStatus )
00116                     return true;
00117             }
00118         }
00119         return true;
00120     }
00121 
00122     /*!
00123      \return the dirpath portion of the filepath \a $filepath.
00124      \code
00125      $dirpath = eZDir::dirpath( "path/to/some/file.txt" );
00126      print( $dirpath ); // prints out path/to/some
00127      \endcode
00128     */
00129     static function dirpath( $filepath )
00130     {
00131         $filepath = eZDir::cleanPath( $filepath, self::SEPARATOR_UNIX );
00132         $dirPosition = strrpos( $filepath, '/' );
00133         if ( $dirPosition !== false )
00134             return substr( $filepath, 0, $dirPosition );
00135         return $filepath;
00136     }
00137 
00138     /*!
00139      \return the default permissions to use for directories.
00140      \note The permission is converted from octal text to decimal value.
00141     */
00142     static function directoryPermission()
00143     {
00144         return octdec( eZINI::instance()->variable( 'FileSettings', 'StorageDirPermissions' ) );
00145     }
00146 
00147     /*!
00148      \static
00149      \private
00150      Creates the directory \a $dir with permission \a $perm.
00151     */
00152     static function doMkdir( $dir, $perm, $recursive = false )
00153     {
00154         $oldumask = umask( 0 );
00155         $success = @mkdir( $dir, $perm, $recursive );
00156         umask( $oldumask );
00157         return $success;
00158     }
00159 
00160     /*!
00161      \static
00162      \return the separator used between directories and files according to \a $type.
00163 
00164      Type can be one of the following:
00165      - self::SEPARATOR_LOCAL - Returns whatever is applicable for the current machine.
00166      - self::SEPARATOR_UNIX  - Returns a /
00167      - self::SEPARATOR_DOS   - Returns a \
00168     */
00169     static function separator( $type )
00170     {
00171         switch ( $type )
00172         {
00173             case self::SEPARATOR_LOCAL:
00174                 return eZSys::fileSeparator();
00175             case self::SEPARATOR_UNIX:
00176                 return '/';
00177             case self::SEPARATOR_DOS:
00178                 return "\\";
00179         }
00180         return null;
00181     }
00182 
00183     /*!
00184      \static
00185      Converts any directory separators found in \a $path, in both unix and dos style, into
00186      the separator type specified by \a $toType and returns it.
00187     */
00188     static function convertSeparators( $path, $toType = self::SEPARATOR_UNIX )
00189     {
00190         $separator = eZDir::separator( $toType );
00191         return str_replace( array( '/', '\\' ), $separator, $path );
00192     }
00193 
00194     /*!
00195      \static
00196      Removes all unneeded directory separators and resolves any "."s and ".."s found in \a $path.
00197 
00198      For instance: "var/../lib/ezdb" becomes "lib/ezdb", while "../site/var" will not be changed.
00199      \note Will also convert separators
00200      \sa convertSeparators.
00201     */
00202     static function cleanPath( $path, $toType = self::SEPARATOR_UNIX )
00203     {
00204         $path = eZDir::convertSeparators( $path, $toType );
00205         $separator = eZDir::separator( $toType );
00206         if ( strpos( $path, $separator . $separator ) !== false )
00207         {
00208             $path = preg_replace( "#$separator{2,}#", $separator, $path );
00209         }
00210         $pathElements = explode( $separator, $path );
00211         $newPathElements = array();
00212         foreach ( $pathElements as $pathElement )
00213         {
00214             if ( $pathElement === '.' )
00215                 continue;
00216             if ( $pathElement === '..' and
00217                  count( $newPathElements ) > 0 )
00218                 array_pop( $newPathElements );
00219             else
00220                 $newPathElements[] = $pathElement;
00221         }
00222         if ( !isset( $newPathElements[0] )  )
00223             $newPathElements[] = '.';
00224         $path = implode( $separator, $newPathElements );
00225         return $path;
00226     }
00227 
00228     /*!
00229      \static
00230      Creates a path out of all the dir and file items in the array \a $names
00231      with correct separators in between them.
00232      It will also remove unneeded separators.
00233      \a $type is used to determine the separator type, see eZDir::separator.
00234      If \a $includeEndSeparator is true then it will make sure that the path ends with a
00235      separator if false it make sure there are no end separator.
00236     */
00237     static function path( $names, $includeEndSeparator = false, $type = self::SEPARATOR_UNIX )
00238     {
00239         $separator = eZDir::separator( $type );
00240         $path = implode( $separator, $names );
00241         $path = eZDir::cleanPath( $path, $type );
00242         $pathLen = strlen( $path );
00243         $hasEndSeparator = ( $pathLen > 0 and
00244                          $path[$pathLen - 1] == $separator );
00245         if ( $includeEndSeparator and
00246              !$hasEndSeparator )
00247             $path .= $separator;
00248         else if ( !$includeEndSeparator &&
00249                   $hasEndSeparator &&
00250                   $pathLen > 1 )
00251             $path = substr( $path, 0, $pathLen - 1 );
00252         return $path;
00253     }
00254 
00255 
00256     /**
00257      * Removes a directory and all it's contents, recursively.
00258      *
00259      * @param string $dir Directory to remove
00260      * @param bool $rootCheck Check whether $dir is supposed to be contained in
00261      *                        eZ Publish root directory
00262      * @return bool True if the operation succeeded, false otherwise
00263      */
00264     static function recursiveDelete( $dir, $rootCheck = true )
00265     {
00266         // RecursiveDelete fails if ...
00267         // $dir is not a directory
00268         if ( !is_dir( $dir ) )
00269             return false;
00270 
00271         // rootCheck is enabled and $dir is not part of the root directory
00272         if ( $rootCheck && strpos( dirname( realpath( $dir ) ) . DIRECTORY_SEPARATOR, realpath( eZSys::rootDir() ) . DIRECTORY_SEPARATOR ) === false )
00273             return false;
00274 
00275         // the directory cannot be opened
00276         if ( ! ( $handle = @opendir( $dir ) ) )
00277             return false;
00278 
00279         while ( ( $file = readdir( $handle ) ) !== false )
00280         {
00281             if ( ( $file == "." ) || ( $file == ".." ) )
00282             {
00283                 continue;
00284             }
00285             if ( is_dir( $dir . '/' . $file ) )
00286             {
00287                 eZDir::recursiveDelete( $dir . '/' . $file, false );
00288             }
00289             else
00290             {
00291                 unlink( $dir . '/' . $file );
00292             }
00293         }
00294         @closedir( $handle );
00295         return rmdir( $dir );
00296     }
00297 
00298     /*!
00299      \static
00300      Creates a list of all files and dirs in the directory.
00301     */
00302     static function recursiveList( $dir, $path, &$fileList )
00303     {
00304         if ( $handle = @opendir( $dir ) )
00305         {
00306             while ( ( $file = readdir( $handle ) ) !== false )
00307             {
00308                 if ( ( $file == "." ) || ( $file == ".." ) )
00309                 {
00310                     continue;
00311                 }
00312                 if ( is_dir( $dir . '/' . $file ) )
00313                 {
00314                     $fileList[] = array( 'path' => $path, 'name' => $file, 'type' => 'dir' );
00315                     eZDir::recursiveList( $dir . '/' . $file, $path . '/' . $file, $fileList );
00316                 }
00317                 else
00318                 {
00319                     $fileList[] = array( 'path' => $path, 'name' => $file, 'type' => 'file'  );
00320                 }
00321             }
00322             @closedir( $handle );
00323         }
00324     }
00325 
00326     /*!
00327      \static
00328      Recurses through the directory and returns the files that matches the given suffix
00329      \note This function will not traverse . (hidden) folders
00330     */
00331     static function recursiveFind( $dir, $suffix )
00332     {
00333         $returnFiles = array();
00334         if ( $handle = @opendir( $dir ) )
00335         {
00336             while ( ( $file = readdir( $handle ) ) !== false )
00337             {
00338                 if ( ( $file == "." ) || ( $file == ".." ) )
00339                 {
00340                     continue;
00341                 }
00342                 if ( is_dir( $dir . '/' . $file ) )
00343                 {
00344                     if ( $file[0] != "." )
00345                     {
00346                         $files = eZDir::recursiveFind( $dir . '/' . $file, $suffix );
00347                         $returnFiles = array_merge( $files, $returnFiles );
00348                     }
00349                 }
00350                 else
00351                 {
00352                     if ( preg_match( "/$suffix$/", $file ) )
00353                         $returnFiles[] = $dir . '/' . $file;
00354                 }
00355             }
00356             @closedir( $handle );
00357         }
00358         return $returnFiles;
00359     }
00360 
00361     /*!
00362      \static
00363       Unlink files match the given pattern in the given directory.
00364     */
00365     static function unlinkWildcard( $dir, $pattern )
00366     {
00367         $availableFiles = array();
00368         if ( $handle = @opendir( $dir ) )
00369         {
00370             while ( ( $file = readdir( $handle ) ) !== false )
00371             {
00372                 if ( $file != "." && $file != ".." )
00373                 {
00374                     $availableFiles[] = $file;
00375                 }
00376             }
00377             @closedir( $handle );
00378 
00379             if( strpos( $pattern, "." ) )
00380             {
00381                 $baseexp = substr( $pattern, 0, strpos( $pattern, "." ) );
00382                 $typeexp = substr( $pattern, ( strpos( $pattern, "." ) + 1 ), strlen( $pattern ) );
00383             }
00384             else
00385             {
00386                 $baseexp = $pattern;
00387                 $typeexp = "";
00388             }
00389 
00390             $baseexp=preg_quote( $baseexp );
00391             $typeexp=preg_quote( $typeexp );
00392 
00393             $baseexp = str_replace( array( "\*", "\?" ), array( ".*", "." ), $baseexp );
00394             $typeexp = str_replace(array( "\*", "\?" ), array( ".*", "." ), $typeexp );
00395 
00396             $i=0;
00397             $matchedFileArray = array();
00398             foreach( $availableFiles as $file )
00399             {
00400                 $fileName = basename( $file );
00401 
00402                 if( strpos( $fileName, "." ) )
00403                 {
00404                     $base = substr( $fileName, 0, strpos( $fileName, "."));
00405                     $type = substr( $fileName, ( strpos( $fileName,"." ) + 1 ), strlen( $fileName ) );
00406                 }
00407                 else
00408                 {
00409                     $base = $fileName;
00410                     $type = "";
00411                 }
00412 
00413                 if( preg_match( "/^".$baseexp."$/i", $base ) && preg_match( "/^".$typeexp."$/i", $type ) )
00414                 {
00415                     $matchedFileArray[$i] = $file;
00416                     $i++;
00417                 }
00418             }
00419 
00420             foreach ( array_keys( $matchedFileArray ) as $key )
00421             {
00422                 $matchedFile =& $matchedFileArray[$key];
00423                 if ( substr( $dir,-1 ) == "/")
00424                 {
00425                     unlink( $dir.$matchedFile );
00426                 }
00427                 else
00428                 {
00429                     unlink( $dir."/".$matchedFile );
00430                 }
00431             }
00432         }
00433     }
00434 
00435     /*!
00436      \static
00437      Recurses through the directory and returns the files that matches the given suffix.
00438      This function will store the relative path from the given base only.
00439      Note: this function will not traverse . (hidden) folders
00440     */
00441     static function recursiveFindRelative( $baseDir, $subDir, $suffix )
00442     {
00443         $dir = $baseDir;
00444         if ( $subDir != "" )
00445         {
00446             if ( $dir != '' )
00447                 $dir .= "/" . $subDir;
00448             else
00449                 $dir .= $subDir;
00450         }
00451 
00452         if ( !is_dir( $dir ) )
00453         {
00454             return array();
00455         }
00456 
00457         $returnFiles = array();
00458         if ( $handle = @opendir( $dir ) )
00459         {
00460             while ( ( $file = readdir( $handle ) ) !== false )
00461             {
00462                 if ( ( $file == "." ) || ( $file == ".." ) )
00463                 {
00464                     continue;
00465                 }
00466                 if ( is_dir( $dir . '/' . $file ) )
00467                 {
00468                     if ( $file[0] != "." )
00469                     {
00470                         $files = eZDir::recursiveFindRelative( $baseDir, $subDir . '/' . $file, $suffix );
00471                         $returnFiles = array_merge( $files, $returnFiles );
00472                     }
00473                 }
00474                 else
00475                 {
00476                     if ( preg_match( "/$suffix$/", $file ) )
00477                         $returnFiles[] = $subDir . '/' . $file;
00478                 }
00479             }
00480             @closedir( $handle );
00481         }
00482         return $returnFiles;
00483     }
00484 
00485     /*!
00486      \static
00487      Returns all subdirectories in a folder
00488     */
00489     static function findSubdirs( $dir, $includeHidden = false, $excludeItems = false )
00490     {
00491         return eZDir::findSubitems( $dir, 'd', false, $includeHidden, $excludeItems );
00492     }
00493 
00494     /*!
00495      \static
00496      Returns all subdirectories in a folder
00497     */
00498     static function findSubitems( $dir, $types = false, $fullPath = false, $includeHidden = false, $excludeItems = false )
00499     {
00500         if ( !$types )
00501             $types = 'dfl';
00502         $dirArray = array();
00503         if ( $handle = @opendir( $dir ) )
00504         {
00505             while ( ( $element = readdir( $handle ) ) !== false )
00506             {
00507                 if ( $element === '.' || $element === '..' )
00508                     continue;
00509                 if ( !$includeHidden && $element[0] === '.' )
00510                     continue;
00511                 if ( $excludeItems && preg_match( $excludeItems, $element ) )
00512                     continue;
00513                 if ( strpos( $types, 'd' ) === false && is_dir( $dir . '/' . $element ) )
00514                     continue;
00515                 if ( strpos( $types, 'l' ) === false && is_link( $dir . '/' . $element ) )
00516                     continue;
00517                 if ( strpos( $types, 'f' ) === false && is_file( $dir . '/' . $element ))
00518                     continue;
00519                 if ( $fullPath )
00520                 {
00521                     if ( is_string( $fullPath ) )
00522                         $dirArray[] = $fullPath . '/' . $element;
00523                     else
00524                         $dirArray[] = $dir . '/' . $element;
00525                 }
00526                 else
00527                     $dirArray[] = $element;
00528             }
00529             @closedir( $handle );
00530         }
00531         return $dirArray;
00532     }
00533 
00534     /*!
00535      Copies a directory (and optionally all it's subitems) to another directory.
00536      \param $sourceDirectory The source directory which should be copied, this location must exist.
00537      \param $destinationDirectory The location for the copied directory structure, this location must exist.
00538             This parameter will be modified if \a $asChild is \c true.
00539      \param $asChild If \c true then it will use last part of the \a $sourceDirectory as a sub-folder to \a $destinationDirectory.
00540             e.g. copying /etc/httpd to /var/ will create /var/httpd and place all folders/files under it.
00541      \param $recursive If \c true then it will copy folders/files recursively from folders found in \a $sourceDirectory.
00542      \param $includeHidden If \c true it will include files or folders beginning with a dot (.).
00543      \param $excludeItems A regular expression used to exclude files or folders in the subtree, use \c false for no exclusion.
00544 
00545      \note The parameter \a $recursive is currently unused, it will always copy recursively.
00546     */
00547     static function copy( $sourceDirectory, &$destinationDirectory,
00548                    $asChild = true, $recursive = true, $includeHidden = false, $excludeItems = false )
00549     {
00550         if ( !is_dir( $sourceDirectory ) )
00551         {
00552             eZDebug::writeError( "Source $sourceDirectory is not a directory, cannot copy from it", __METHOD__ );
00553             return false;
00554         }
00555         if ( !is_dir( $destinationDirectory ) )
00556         {
00557             eZDebug::writeError( "Destination $destinationDirectory is not a directory, cannot copy to it", __METHOD__ );
00558             return false;
00559         }
00560         if ( $asChild )
00561         {
00562             if ( preg_match( "#^.+/([^/]+)$#", $sourceDirectory, $matches ) )
00563             {
00564                 eZDir::mkdir( $destinationDirectory . '/' . $matches[1], eZDir::directoryPermission(), false );
00565                 $destinationDirectory .= '/' . $matches[1];
00566             }
00567         }
00568         $items = eZDir::findSubitems( $sourceDirectory, 'df', false, $includeHidden, $excludeItems );
00569         $totalItems = $items;
00570         while ( count( $items ) > 0 )
00571         {
00572             $currentItems = $items;
00573             $items = array();
00574             foreach ( $currentItems as $item )
00575             {
00576                 $fullPath = $sourceDirectory . '/' . $item;
00577                 if ( is_file( $fullPath ) )
00578                     eZFileHandler::copy( $fullPath, $destinationDirectory . '/' . $item );
00579                 else if ( is_dir( $fullPath ) )
00580                 {
00581                     eZDir::mkdir( $destinationDirectory . '/' . $item, eZDir::directoryPermission(), false );
00582                     $newItems = eZDir::findSubitems( $fullPath, 'df', $item, $includeHidden, $excludeItems );
00583                     $items = array_merge( $items, $newItems );
00584                     $totalItems = array_merge( $totalItems, $newItems );
00585                     unset( $newItems );
00586                 }
00587             }
00588         }
00589 //         eZDebugSetting::writeNotice( 'lib-ezfile-copy',
00590 //                                      "Copied directory $sourceDirectory to destination $destinationDirectory",
00591 //                                      'eZDir::copy' );
00592         return $totalItems;
00593     }
00594 
00595     /*!
00596      \return a regexp which will match certain temporary files.
00597     */
00598     static function temporaryFileRegexp( $standalone = true )
00599     {
00600         $preg = '';
00601         if ( $standalone )
00602             $preg .= "/^";
00603         $preg .= "(.*~|#.+#|.*\.bak|.svn|CVS|.revive.el|.cvsignore)";
00604         if ( $standalone )
00605             $preg .= "$/";
00606         return $preg;
00607     }
00608 
00609     /*!
00610     \static
00611     Check if a given directory is writeable
00612 
00613     \return TRUE/FALSE
00614     */
00615     static function isWriteable( $dirname )
00616     {
00617         if ( eZSys::osType() != 'win32' )
00618             return is_writable( $dirname );
00619 
00620         /* PHP function is_writable() doesn't work correctly on Windows NT descendants.
00621          * So we have to use the following hack on those OSes.
00622          */
00623         $tmpfname = $dirname . eZSys::fileSeparator() . "ezsetup_" . md5( microtime() ) . ".tmp";
00624 
00625         // try to create temporary file
00626         if ( !( $fp = @fopen( $tmpfname, "w" ) ) )
00627             return FALSE;
00628 
00629         fclose( $fp );
00630         unlink( $tmpfname );
00631 
00632         return TRUE;
00633     }
00634 }
00635 
00636 ?>