8 * @file classes/file/FileManager.inc.php
10 * Copyright (c) 2000-2009 John Willinsky
11 * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
16 * @brief Class defining basic operations for file management.
19 // $Id: FileManager.inc.php,v 1.10 2009/09/29 23:39:13 asmecher Exp $
22 define('FILE_MODE_MASK', 0666);
23 define('DIRECTORY_MODE_MASK', 0777);
29 function FileManager() {
33 * Return true if an uploaded file exists.
34 * @param $fileName string the name of the file used in the POST form
37 function uploadedFileExists($fileName) {
38 if (isset($_FILES[$fileName]['tmp_name']) && is_uploaded_file($_FILES[$fileName]['tmp_name'])) {
45 * Return true iff an error occurred when trying to upload a file.
46 * @param $fileName string the name of the file used in the POST form
49 function uploadError($fileName) {
50 return (isset($_FILES[$fileName]) && $_FILES[$fileName]['error'] != 0);
54 * Return the (temporary) path to an uploaded file.
55 * @param $fileName string the name of the file used in the POST form
56 * @return string (boolean false if no such file)
58 function getUploadedFilePath($fileName) {
59 if (isset($_FILES[$fileName]['tmp_name']) && is_uploaded_file($_FILES[$fileName]['tmp_name'])) {
60 return $_FILES[$fileName]['tmp_name'];
66 * Return the user-specific (not temporary) filename of an uploaded file.
67 * @param $fileName string the name of the file used in the POST form
68 * @return string (boolean false if no such file)
70 function getUploadedFileName($fileName) {
71 if (isset($_FILES[$fileName]['name'])) {
72 return $_FILES[$fileName]['name'];
78 * Return the type of an uploaded file.
79 * @param $fileName string the name of the file used in the POST form
82 function getUploadedFileType($fileName) {
83 if (isset($_FILES[$fileName])) {
84 $type = String::mime_content_type($_FILES[$fileName]['tmp_name']);
85 if (!empty($type)) return $type;
86 return $_FILES[$fileName]['type'];
93 * @param $fileName string the name of the file used in the POST form
94 * @param $dest string the path where the file is to be saved
95 * @return boolean returns true if successful
97 function uploadFile($fileName, $destFileName) {
98 $destDir = dirname($destFileName);
99 if (!FileManager
::fileExists($destDir, 'dir')) {
100 // Try to create the destination directory
101 FileManager
::mkdirtree($destDir);
103 if (!isset($_FILES[$fileName])) return false;
104 if (move_uploaded_file($_FILES[$fileName]['tmp_name'], $destFileName))
105 return FileManager
::setMode($destFileName, FILE_MODE_MASK
);
111 * @param $dest string the path where the file is to be saved
112 * @param $contents string the contents to write to the file
113 * @return boolean returns true if successful
115 function writeFile($dest, &$contents) {
117 $destDir = dirname($dest);
118 if (!FileManager
::fileExists($destDir, 'dir')) {
119 // Try to create the destination directory
120 FileManager
::mkdirtree($destDir);
122 if (($f = fopen($dest, 'wb'))===false) $success = false;
123 if ($success && fwrite($f, $contents)===false) $success = false;
127 return FileManager
::setMode($dest, FILE_MODE_MASK
);
133 * @param $source string the source URL for the file
134 * @param $dest string the path where the file is to be saved
135 * @return boolean returns true if successful
137 function copyFile($source, $dest) {
139 $destDir = dirname($dest);
140 if (!FileManager
::fileExists($destDir, 'dir')) {
141 // Try to create the destination directory
142 FileManager
::mkdirtree($destDir);
144 if (copy($source, $dest))
145 return FileManager
::setMode($dest, FILE_MODE_MASK
);
150 * Read a file's contents.
151 * @param $filePath string the location of the file to be read
152 * @param $output boolean output the file's contents instead of returning a string
155 function &readFile($filePath, $output = false) {
156 if (is_readable($filePath)) {
157 $f = fopen($filePath, 'rb');
160 $data .= fread($f, 4096);
183 * Outputs HTTP headers and file content for download
184 * @param $filePath string the location of the file to be sent
185 * @param $type string the MIME type of the file, optional
186 * @param $inline print file as inline instead of attachment, optional
189 function downloadFile($filePath, $type = null, $inline = false) {
191 if (HookRegistry
::call('FileManager::downloadFile', array(&$filePath, &$type, &$inline, &$result))) return $result;
192 if (is_readable($filePath)) {
194 $type = String::mime_content_type($filePath);
195 if (empty($type)) $type = 'application/octet-stream';
198 Registry
::clear(); // Free some memory
200 header("Content-Type: $type");
201 header("Content-Length: ".filesize($filePath));
202 header("Content-Disposition: " . ($inline ?
'inline' : 'attachment') . "; filename=\"" .basename($filePath)."\"");
203 header("Cache-Control: private"); // Workarounds for IE weirdness
204 header("Pragma: public");
206 import('file.FileManager');
207 FileManager
::readFile($filePath, true);
217 * View a file inline (variant of downloadFile).
218 * @see FileManager::downloadFile
220 function viewFile($filePath, $type = null) {
221 FileManager
::downloadFile($filePath, $type, true);
226 * @param $filePath string the location of the file to be deleted
227 * @return boolean returns true if successful
229 function deleteFile($filePath) {
230 if (FileManager
::fileExists($filePath)) {
231 return unlink($filePath);
238 * Create a new directory.
239 * @param $dirPath string the full path of the directory to be created
240 * @param $perms string the permissions level of the directory (optional)
241 * @return boolean returns true if successful
243 function mkdir($dirPath, $perms = null) {
244 if ($perms !== null) {
245 return mkdir($dirPath, $perms);
248 return FileManager
::setMode($dirPath, DIRECTORY_MODE_MASK
);
254 * Remove a directory.
255 * @param $dirPath string the full path of the directory to be delete
256 * @return boolean returns true if successful
258 function rmdir($dirPath) {
259 return rmdir($dirPath);
263 * Delete all contents including directory (equivalent to "rm -r")
264 * @param $file string the full path of the directory to be removed
266 function rmtree($file) {
267 if (file_exists($file)) {
269 $handle = opendir($file);
270 import('file.FileManager');
271 while (($filename = readdir($handle)) !== false) {
272 if ($filename != '.' && $filename != '..') {
273 FileManager
::rmtree($file . '/' . $filename);
286 * Create a new directory, including all intermediate directories if required (equivalent to "mkdir -p")
287 * @param $dirPath string the full path of the directory to be created
288 * @param $perms string the permissions level of the directory (optional)
289 * @return boolean returns true if successful
291 function mkdirtree($dirPath, $perms = null) {
292 if (!file_exists($dirPath)) {
293 if (FileManager
::mkdirtree(dirname($dirPath), $perms)) {
294 return FileManager
::mkdir($dirPath, $perms);
303 * Check if a file path is valid;
304 * @param $filePath string the file/directory to check
305 * @param $type string (file|dir) the type of path
307 function fileExists($filePath, $type = 'file') {
310 return file_exists($filePath);
312 return file_exists($filePath) && is_dir($filePath);
319 * Returns file extension associated with the given document type,
320 * or false if the type does not belong to a recognized document type.
321 * @param $type string
323 function getDocumentExtension($type) {
325 case 'application/pdf':
327 case 'application/word':
337 * Returns file extension associated with the given image type,
338 * or false if the type does not belong to a recognized image type.
339 * @param $type string
341 function getImageExtension($type) {
351 case 'image/vnd.microsoft.icon':
355 case 'application/x-shockwave-flash':
358 case 'application/x-flash-video':
359 case 'flv-application/octet-stream':
360 case 'application/octet-stream':
370 case 'video/quicktime':
374 case 'text/javascript':
382 * Parse file extension from file name.
383 * @param string a valid file name
384 * @return string extension
386 function getExtension($fileName) {
388 $fileParts = explode('.', $fileName);
389 if (is_array($fileParts)) {
390 $extension = $fileParts[count($fileParts) - 1];
396 * Truncate a filename to fit in the specified length.
398 function truncateFileName($fileName, $length = 127) {
399 if (String::strlen($fileName) <= $length) return $fileName;
400 $ext = FileManager
::getExtension($fileName);
401 $truncated = String::substr($fileName, 0, $length - 1 - String::strlen($ext)) . '.' . $ext;
402 return String::substr($truncated, 0, $length);
406 * Return pretty file size string (in B, KB, MB, or GB units).
407 * @param $size int file size in bytes
410 function getNiceFileSize($size) {
411 $niceFileSizeUnits = array('B', 'KB', 'MB', 'GB');
412 for($i = 0; $i < 4 && $size > 1024; $i++
) {
415 return $size . $niceFileSizeUnits[$i];
419 * Set file/directory mode based on the 'umask' config setting.
420 * @param $path string
424 function setMode($path, $mask) {
425 $umask = Config
::getVar('files', 'umask');
428 return chmod($path, $mask & ~
$umask);
432 * Parse the file extension from a filename/path.
433 * @param $fileName string
436 function parseFileExtension($fileName) {
437 $fileParts = explode('.', $fileName);
438 if (is_array($fileParts)) {
439 $fileExtension = $fileParts[count($fileParts) - 1];
442 // FIXME Check for evil
443 if (!isset($fileExtension) ||
strstr($fileExtension, 'php') ||
strlen($fileExtension) > 6 ||
!preg_match('/^\w+$/', $fileExtension)) {
444 $fileExtension = 'txt';
447 return $fileExtension;