|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZFile 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 eZFile ezfile.php 00013 \ingroup eZUtils 00014 \brief Tool class which has convencience functions for files and directories 00015 00016 */ 00017 class eZFile 00018 { 00019 /** 00020 * Number of bytes read per fread() operation. 00021 * 00022 * @see downloadContent() 00023 */ 00024 const READ_PACKET_SIZE = 16384; 00025 00026 /** 00027 * Flags for file manipulation 00028 * 00029 * @see rename() 00030 */ 00031 const CLEAN_ON_FAILURE = 1, 00032 APPEND_DEBUG_ON_FAILURE = 2; 00033 00034 /** 00035 * Reads the whole contents of the file \a $file and 00036 * splits it into lines which is collected into an array and returned. 00037 * It will handle Unix (\n), Windows (\r\n) and Mac (\r) style newlines. 00038 * \note The newline character(s) are not present in the line string. 00039 * 00040 * @deprecated Since 4.4, use file( $file, FILE_IGNORE_NEW_LINES ) instead. 00041 * @return array|false 00042 */ 00043 static function splitLines( $file ) 00044 { 00045 $contents = file_get_contents( $file ); 00046 if ( $contents === false ) 00047 return false; 00048 $lines = preg_split( "#\r\n|\r|\n#", $contents ); 00049 unset( $contents ); 00050 return $lines; 00051 } 00052 00053 /*! 00054 Creates a file called \a $filename. 00055 If \a $directory is specified the file is placed there, the directory will also be created if missing. 00056 if \a $data is specified the file will created with the content of this variable. 00057 00058 \param $atomic If true the file contents will be written to a temporary file and renamed to the correct file. 00059 */ 00060 static function create( $filename, $directory = false, $data = false, $atomic = false ) 00061 { 00062 $filepath = $filename; 00063 if ( $directory ) 00064 { 00065 if ( !file_exists( $directory ) ) 00066 { 00067 eZDir::mkdir( $directory, false, true ); 00068 // eZDebugSetting::writeNotice( 'ezfile-create', "Created directory $directory", 'eZFile::create' ); 00069 } 00070 $filepath = $directory . '/' . $filename; 00071 } 00072 // If atomic creation is needed we will use a temporary 00073 // file when writing the data, then rename it to the correct path. 00074 if ( $atomic ) 00075 { 00076 $realpath = $filepath; 00077 $dirname = dirname( $filepath ); 00078 if ( strlen( $dirname ) != 0 ) 00079 $dirname .= "/"; 00080 $filepath = $dirname . "ezfile-tmp." . md5( $filepath . getmypid() . mt_rand() ); 00081 } 00082 00083 $file = fopen( $filepath, 'wb' ); 00084 if ( $file ) 00085 { 00086 // eZDebugSetting::writeNotice( 'ezfile-create', "Created file $filepath", 'eZFile::create' ); 00087 if ( $data ) 00088 fwrite( $file, $data ); 00089 fclose( $file ); 00090 00091 if ( $atomic ) 00092 { 00093 // If the renaming process fails, delete the temporary file 00094 eZFile::rename( $filepath, $realpath, false, eZFile::CLEAN_ON_FAILURE ); 00095 } 00096 return true; 00097 } 00098 // eZDebugSetting::writeNotice( 'ezfile-create', "Failed creating file $filepath", 'eZFile::create' ); 00099 return false; 00100 } 00101 00102 /*! 00103 \static 00104 Read all content of file. 00105 00106 \param filename 00107 00108 \return file contents, false if error 00109 00110 \deprecated since eZ Publish 4.1, use file_get_contents() instead 00111 */ 00112 static function getContents( $filename ) 00113 { 00114 eZDebug::writeWarning( __METHOD__ . ' is deprecated, use file_get_contents() instead' ); 00115 00116 if ( function_exists( 'file_get_contents' ) ) 00117 { 00118 return file_get_contents( $filename ); 00119 } 00120 else 00121 { 00122 $fp = fopen( $filename, 'r' ); 00123 if ( !$fp ) 00124 { 00125 eZDebug::writeError( 'Could not read contents of ' . $filename, __METHOD__ ); 00126 return false; 00127 } 00128 00129 return fread( $fp, filesize( $filename ) ); 00130 } 00131 } 00132 00133 /*! 00134 \static 00135 Get suffix from filename 00136 00137 \param filename 00138 \return suffix, extends: file/to/readme.txt return txt 00139 */ 00140 static function suffix( $filename ) 00141 { 00142 $parts = explode( '.', $filename); 00143 return array_pop( $parts ); 00144 } 00145 00146 /*! 00147 \static 00148 Check if a given file is writeable 00149 00150 \return TRUE/FALSE 00151 */ 00152 static function isWriteable( $filename ) 00153 { 00154 if ( eZSys::osType() != 'win32' ) 00155 return is_writable( $filename ); 00156 00157 /* PHP function is_writable() doesn't work correctly on Windows NT descendants. 00158 * So we have to use the following hack on those OSes. 00159 */ 00160 if ( !( $fd = @fopen( $filename, 'a' ) ) ) 00161 return FALSE; 00162 00163 fclose( $fd ); 00164 00165 return TRUE; 00166 } 00167 00168 /** 00169 * Renames $srcFile to $destFile atomically on Unix, and provides a workaround for Windows. 00170 * 00171 * Usage example: 00172 * <code> 00173 * $srcFile = '/path/to/src/file'; 00174 * $destFile = '/path/to/dest/file'; 00175 * eZFile::rename( $srcFile, $destFile ); 00176 * 00177 * // Using flags 00178 * // In following example, if rename operation fails, $srcFile will be deleted and a message will be appended in eZDebug 00179 * eZFile::rename( $srcFile, $destFile, false, eZFile::APPEND_DEBUG_ON_FAILURE | eZFile::CLEAN_ON_FAILURE ); 00180 * </code> 00181 * 00182 * @param string $srcFile Source file path 00183 * @param string $destFile Destination file path 00184 * @param bool $mkdir Make directory for destination file if needed 00185 * @param int $flags Supported flags are : 00186 * - APPEND_DEBUG_ON_FAILURE (will append a message to the debug if operation fails 00187 * - CLEAN_ON_FAILURE (Will remove $srcFile if operation fails) 00188 * @return bool rename() status (true if successful, false if not) 00189 */ 00190 static function rename( $srcFile, $destFile, $mkdir = false, $flags = 0 ) 00191 { 00192 /* On windows we need to unlink the destination file first */ 00193 if ( strtolower( substr( PHP_OS, 0, 3 ) ) == 'win' ) 00194 { 00195 @unlink( $destFile ); 00196 } 00197 if( $mkdir ) 00198 { 00199 eZDir::mkdir( dirname( $destFile ), false, true ); 00200 } 00201 00202 $status = rename( $srcFile, $destFile ); 00203 // Rename operation failed, check $flags to know what to do then 00204 if ( $status === false ) 00205 { 00206 if ( $flags & self::APPEND_DEBUG_ON_FAILURE ) 00207 eZDebug::writeWarning( "$srcFile could not be renamed to $destFile", __METHOD__ ); 00208 00209 if ( $flags & self::CLEAN_ON_FAILURE ) 00210 unlink( $srcFile ); 00211 } 00212 00213 return $status; 00214 } 00215 00216 /** 00217 * Prepares a file for Download and terminates the execution. 00218 * This method will: 00219 * - empty the output buffer 00220 * - stop buffering 00221 * - stop the active session (in order to allow concurrent browsing while downloading) 00222 * 00223 * @param string $file Path to the local file 00224 * @param bool $isAttachedDownload Determines weather to download the file as an attachment ( download popup box ) or not. 00225 * @param string $overrideFilename 00226 * @param int $startOffset Offset to start transfer from, in bytes 00227 * @param int $length Data size to transfer 00228 * 00229 * @return bool false if error 00230 */ 00231 static function download( $file, $isAttachedDownload = true, $overrideFilename = false, $startOffset = 0, $length = false ) 00232 { 00233 if ( !file_exists( $file ) ) 00234 { 00235 return false; 00236 } 00237 00238 ob_end_clean(); 00239 eZSession::stop(); 00240 self::downloadHeaders( $file, $isAttachedDownload, $overrideFilename, $startOffset, $length ); 00241 self::downloadContent( $file, $startOffset, $length ); 00242 00243 eZExecution::cleanExit(); 00244 } 00245 00246 /** 00247 * Handles the header part of a file transfer to the client 00248 * 00249 * @see download() 00250 * 00251 * @param string $file Path to the local file 00252 * @param bool $isAttachedDownload Determines weather to download the file as an attachment ( download popup box ) or not. 00253 * @param string $overrideFilename Filename to send in headers instead of the actual file's name 00254 * @param int $startOffset Offset to start transfer from, in bytes 00255 * @param int $length Data size to transfer 00256 * @param string $fileSize The file's size. If not given, actual filesize will be queried. Required to work with clusterized files... 00257 */ 00258 public static function downloadHeaders( $file, $isAttachedDownload = true, $overrideFilename = false, $startOffset = 0, $length = false, $fileSize = false ) 00259 { 00260 if ( $fileSize === false ) 00261 { 00262 if ( !file_exists( $file ) ) 00263 { 00264 eZDebug::writeError( "\$fileSize not given, and file not found", __METHOD__ ); 00265 return false; 00266 } 00267 00268 $fileSize = filesize( $file ); 00269 } 00270 00271 header( 'X-Powered-By: eZ Publish' ); 00272 $mimeinfo = eZMimeType::findByURL( $file ); 00273 header( "Content-Type: {$mimeinfo['name']}" ); 00274 00275 // Fixes problems with IE when opening a file directly 00276 header( "Pragma: " ); 00277 header( "Cache-Control: " ); 00278 // Last-Modified header cannot be set, otherwise browser like FF will fail while resuming a paused download 00279 // because it compares the value of Last-Modified headers between requests. 00280 header( "Last-Modified: " ); 00281 /* Set cache time out to 10 minutes, this should be good enough to work 00282 around an IE bug */ 00283 header( "Expires: ". gmdate( 'D, d M Y H:i:s', time() + 600 ) . ' GMT' ); 00284 header( 00285 "Content-Disposition: " . 00286 ( $isAttachedDownload ? 'attachment' : 'inline' ) . 00287 ( $overrideFilename !== false ? "; filename={$overrideFilename}" : '' ) 00288 ); 00289 00290 // partial download (HTTP 'Range' header) 00291 if ( $startOffset !== 0 ) 00292 { 00293 $endOffset = ( $length !== false ) ? ( $length + $startOffset - 1 ) : $fileSize - 1; 00294 header( "Content-Length: " . ( $endOffset - $startOffset + 1 ) ); 00295 header( "Content-Range: bytes {$startOffset}-{$endOffset}/{$fileSize}" ); 00296 header( "HTTP/1.1 206 Partial Content" ); 00297 } 00298 else 00299 { 00300 header( "Content-Length: $fileSize" ); 00301 } 00302 header( 'Content-Transfer-Encoding: binary' ); 00303 header( 'Accept-Ranges: bytes' ); 00304 } 00305 00306 /** 00307 * Handles the data part of a file transfer to the client 00308 * 00309 * @see download() 00310 * 00311 * @param string $file Path to the local file 00312 * @param int $startOffset Offset to start transfer from, in bytes 00313 * @param int $length Data size to transfer 00314 */ 00315 public static function downloadContent( $file, $startOffset = 0, $length = false ) 00316 { 00317 if ( !file_exists( $file ) ) 00318 { 00319 eZDebug::writeError( "'$file' does not exist", __METHOD__ ); 00320 return false; 00321 } 00322 if ( ( $fp = fopen( $file, 'rb' ) ) === false ) 00323 { 00324 eZDebug::writeError( "An error occured opening '$file' for reading", __METHOD__ ); 00325 return false; 00326 } 00327 00328 $fileSize = filesize( $file ); 00329 00330 // an offset has been given: move the pointer to that offset if it seems valid 00331 if ( $startOffset !== false && $startOffset <= $fileSize && fseek( $fp, $startOffset ) === -1 ) 00332 { 00333 eZDebug::writeError( "Error while setting offset on '{$file}'", __METHOD__ ); 00334 return false; 00335 } 00336 00337 $transferred = $startOffset; 00338 $packetSize = self::READ_PACKET_SIZE; 00339 $endOffset = ( $length === false ) ? $fileSize - 1 : $length + $startOffset - 1; 00340 00341 while ( !feof( $fp ) && $transferred < $endOffset + 1 ) 00342 { 00343 if ( $transferred + $packetSize > $endOffset + 1 ) 00344 { 00345 $packetSize = $endOffset + 1 - $transferred; 00346 } 00347 echo fread( $fp, $packetSize ); 00348 $transferred += $packetSize; 00349 } 00350 fclose( $fp ); 00351 00352 return true; 00353 } 00354 } 00355 00356 ?>