|
eZ Publish
[trunk]
|
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 ?>