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