From a56e04da7662baed230f3ae8ba46a66c553e8ca1 Mon Sep 17 00:00:00 2001 From: Portaflex Date: Sun, 11 Sep 2016 10:27:26 +0200 Subject: [PATCH 1/2] Filesystem_helper --- system/Helpers/filesystem_helper.php | 518 ++++++++++++++++++ .../source/helpers/filesystem_helper.rst | 287 ++++++++++ 2 files changed, 805 insertions(+) create mode 100644 system/Helpers/filesystem_helper.php create mode 100644 user_guide_src/source/helpers/filesystem_helper.rst diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php new file mode 100644 index 000000000000..51e9b1ca365b --- /dev/null +++ b/system/Helpers/filesystem_helper.php @@ -0,0 +1,518 @@ + 0) && is_dir($source_dir.$file)) + { + $filedata[$file] = directory_map($source_dir.$file, $new_depth, $hidden); + } + else + { + $filedata[] = $file; + } + } + + closedir($fp); + return $filedata; + } + + return false; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('write_file')) +{ + /** + * Write File + * + * Writes data to the file specified in the path. + * Creates a new file if non-existent. + * + * @param string $path File path + * @param string $data Data to write + * @param string $mode fopen() mode (default: 'wb') + * @return bool + */ + function write_file(string $path, string $data, string $mode = 'wb'): bool + { + if ( ! $fp = @fopen($path, $mode)) + { + return false; + } + + flock($fp, LOCK_EX); + + for ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result) + { + if (($result = fwrite($fp, substr($data, $written))) === false) + { + break; + } + } + + flock($fp, LOCK_UN); + fclose($fp); + + return is_int($result); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('delete_files')) +{ + /** + * Delete Files + * + * Deletes all files contained in the supplied directory path. + * Files must be writable or owned by the system in order to be deleted. + * If the second parameter is set to true, any directories contained + * within the supplied base directory will be nuked as well. + * + * @param string $path File path + * @param bool $del_dir Whether to delete any directories found in the path + * @param bool $htdocs Whether to skip deleting .htaccess and index page files + * @param int $_level Current directory depth level (default: 0; internal use only) + * @return bool + */ + function delete_files(string $path, bool $del_dir = false, bool $htdocs = false, int $_level = 0): bool + { + // Trim the trailing slash + $path = rtrim($path, '/\\'); + + if ( ! $current_dir = @opendir($path)) + { + return false; + } + + while (false !== ($filename = @readdir($current_dir))) + { + if ($filename !== '.' && $filename !== '..') + { + if (is_dir($path.DIRECTORY_SEPARATOR.$filename) && $filename[0] !== '.') + { + delete_files($path.DIRECTORY_SEPARATOR.$filename, $del_dir, $htdocs, $_level + 1); + } + elseif ($htdocs !== true OR ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) + { + @unlink($path.DIRECTORY_SEPARATOR.$filename); + } + } + } + + closedir($current_dir); + + return ($del_dir === true && $_level > 0) + ? @rmdir($path) + : true; + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('get_filenames')) +{ + /** + * Get Filenames + * + * Reads the specified directory and builds an array containing the filenames. + * Any sub-folders contained within the specified path are read as well. + * + * @param string path to source + * @param bool whether to include the path as part of the filename + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ + function get_filenames(string $source_dir, bool $include_path = false, bool $_recursion = false): array + { + static $_filedata = array(); + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === false) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + while (false !== ($file = readdir($fp))) + { + if (is_dir($source_dir.$file) && $file[0] !== '.') + { + get_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, true); + } + elseif ($file[0] !== '.') + { + $_filedata[] = ($include_path === true) ? $source_dir.$file : $file; + } + } + + closedir($fp); + return $_filedata; + } + + return false; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_dir_file_info')) +{ + /** + * Get Directory File Information + * + * Reads the specified directory and builds an array containing the filenames, + * filesize, dates, and permissions + * + * Any sub-folders contained within the specified path are read as well. + * + * @param string path to source + * @param bool Look only at the top level directory specified? + * @param bool internal variable to determine recursion status - do not use in calls + * @return array + */ + function get_dir_file_info(string $source_dir, bool $top_level_only = true, bool $_recursion = false): array + { + static $_filedata = array(); + $relative_path = $source_dir; + + if ($fp = @opendir($source_dir)) + { + // reset the array and make sure $source_dir has a trailing slash on the initial call + if ($_recursion === false) + { + $_filedata = array(); + $source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR; + } + + // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast + while (false !== ($file = readdir($fp))) + { + if (is_dir($source_dir.$file) && $file[0] !== '.' && $top_level_only === false) + { + get_dir_file_info($source_dir.$file.DIRECTORY_SEPARATOR, $top_level_only, true); + } + elseif ($file[0] !== '.') + { + $_filedata[$file] = get_file_info($source_dir.$file); + $_filedata[$file]['relative_path'] = $relative_path; + } + } + + closedir($fp); + return $_filedata; + } + + return false; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_file_info')) +{ + /** + * Get File Info + * + * Given a file and path, returns the name, path, size, date modified + * Second parameter allows you to explicitly declare what information you want returned + * Options are: name, server_path, size, date, readable, writable, executable, fileperms + * Returns false if the file cannot be found. + * + * @param string path to file + * @param mixed array or comma separated string of information returned + * @return array + */ + function get_file_info(strin $file, $returned_values = ['name', 'server_path', 'size', 'date']): array + { + if ( ! file_exists($file)) + { + return false; + } + + if (is_string($returned_values)) + { + $returned_values = explode(',', $returned_values); + } + + foreach ($returned_values as $key) + { + switch ($key) + { + case 'name': + $fileinfo['name'] = basename($file); + break; + case 'server_path': + $fileinfo['server_path'] = $file; + break; + case 'size': + $fileinfo['size'] = filesize($file); + break; + case 'date': + $fileinfo['date'] = filemtime($file); + break; + case 'readable': + $fileinfo['readable'] = is_readable($file); + break; + case 'writable': + $fileinfo['writable'] = is_really_writable($file); + break; + case 'executable': + $fileinfo['executable'] = is_executable($file); + break; + case 'fileperms': + $fileinfo['fileperms'] = fileperms($file); + break; + } + } + + return $fileinfo; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('get_mime_by_extension')) +{ + /** + * Get Mime by Extension + * + * Translates a file extension into a mime type based on config/mimes.php. + * Returns false if it can't determine the type, or open the mime config file + * + * Note: this is NOT an accurate way of determining file mime types, and is here strictly as a convenience + * It should NOT be trusted, and should certainly NOT be used for security + * + * @param string $filename File name + * @return string + */ + function get_mime_by_extension(string $filename): string + { + static $mimes; + + if ( ! is_array($mimes)) + { + $mimes = get_mimes(); + + if (empty($mimes)) + { + return false; + } + } + + $extension = strtolower(substr(strrchr($filename, '.'), 1)); + + if (isset($mimes[$extension])) + { + return is_array($mimes[$extension]) + ? current($mimes[$extension]) // Multiple mime types, just give the first one + : $mimes[$extension]; + } + + return false; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('symbolic_permissions')) +{ + /** + * Symbolic Permissions + * + * Takes a numeric value representing a file's permissions and returns + * standard symbolic notation representing that value + * + * @param int $perms Permissions + * @return string + */ + function symbolic_permissions($perms): string + { + if (($perms & 0xC000) === 0xC000) + { + $symbolic = 's'; // Socket + } + elseif (($perms & 0xA000) === 0xA000) + { + $symbolic = 'l'; // Symbolic Link + } + elseif (($perms & 0x8000) === 0x8000) + { + $symbolic = '-'; // Regular + } + elseif (($perms & 0x6000) === 0x6000) + { + $symbolic = 'b'; // Block special + } + elseif (($perms & 0x4000) === 0x4000) + { + $symbolic = 'd'; // Directory + } + elseif (($perms & 0x2000) === 0x2000) + { + $symbolic = 'c'; // Character special + } + elseif (($perms & 0x1000) === 0x1000) + { + $symbolic = 'p'; // FIFO pipe + } + else + { + $symbolic = 'u'; // Unknown + } + + // Owner + $symbolic .= (($perms & 0x0100) ? 'r' : '-') + .(($perms & 0x0080) ? 'w' : '-') + .(($perms & 0x0040) ? (($perms & 0x0800) ? 's' : 'x' ) : (($perms & 0x0800) ? 'S' : '-')); + + // Group + $symbolic .= (($perms & 0x0020) ? 'r' : '-') + .(($perms & 0x0010) ? 'w' : '-') + .(($perms & 0x0008) ? (($perms & 0x0400) ? 's' : 'x' ) : (($perms & 0x0400) ? 'S' : '-')); + + // World + $symbolic .= (($perms & 0x0004) ? 'r' : '-') + .(($perms & 0x0002) ? 'w' : '-') + .(($perms & 0x0001) ? (($perms & 0x0200) ? 't' : 'x' ) : (($perms & 0x0200) ? 'T' : '-')); + + return $symbolic; + } +} + +// -------------------------------------------------------------------- + +if ( ! function_exists('octal_permissions')) +{ + /** + * Octal Permissions + * + * Takes a numeric value representing a file's permissions and returns + * a three character string representing the file's octal permissions + * + * @param int $perms Permissions + * @return string + */ + function octal_permissions($perms) + { + return substr(sprintf('%o', $perms), -3); + } +} + +// ------------------------------------------------------------------------ + +if ( ! function_exists('set_realpath')) +{ + /** + * Set Realpath + * + * @param string + * @param bool checks to see if the path exists + * @return string + */ + function set_realpath(string $path, bool $check_existance = false): string + { + // Security check to make sure the path is NOT a URL. No remote file inclusion! + if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp)#i', $path) OR filter_var($path, FILTER_VALIDATE_IP) === $path ) + { + show_error('The path you submitted must be a local server path, not a URL'); + } + + // Resolve the path + if (realpath($path) !== false) + { + $path = realpath($path); + } + elseif ($check_existance && ! is_dir($path) && ! is_file($path)) + { + show_error('Not a valid path: '.$path); + } + + // Add a trailing slash, if this is a directory + return is_dir($path) ? rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR : $path; + } +} diff --git a/user_guide_src/source/helpers/filesystem_helper.rst b/user_guide_src/source/helpers/filesystem_helper.rst new file mode 100644 index 000000000000..f6f92ccbaf3c --- /dev/null +++ b/user_guide_src/source/helpers/filesystem_helper.rst @@ -0,0 +1,287 @@ +################ +Filesystem Helper +################ + +The Directory Helper file contains functions that assist in working with +directories. + +.. contents:: + :local: + +.. raw:: html + +
+ +Loading this Helper +=================== + +This helper is loaded using the following code: + +:: + + helper('filesystem'); + +Available Functions +=================== + +The following functions are available: + + +.. php:function:: directory_map($source_dir[, $directory_depth = 0[, $hidden = FALSE]]) + + :param string $source_dir: Path to the source directory + :param int $directory_depth: Depth of directories to traverse (0 = fully recursive, 1 = current dir, etc) + :param bool $hidden: Whether to include hidden directories + :returns: An array of files + :rtype: array + + Examples:: + + $map = directory_map('./mydirectory/'); + + .. note:: Paths are almost always relative to your main index.php file. + + + Sub-folders contained within the directory will be mapped as well. If + you wish to control the recursion depth, you can do so using the second + parameter (integer). A depth of 1 will only map the top level directory:: + + $map = directory_map('./mydirectory/', 1); + + By default, hidden files will not be included in the returned array. To + override this behavior, you may set a third parameter to true (boolean):: + + $map = directory_map('./mydirectory/', FALSE, TRUE); + + Each folder name will be an array index, while its contained files will + be numerically indexed. Here is an example of a typical array:: + + Array ( + [libraries] => Array + ( + [0] => benchmark.html + [1] => config.html + ["database/"] => Array + ( + [0] => query_builder.html + [1] => binds.html + [2] => configuration.html + [3] => connecting.html + [4] => examples.html + [5] => fields.html + [6] => index.html + [7] => queries.html + ) + [2] => email.html + [3] => file_uploading.html + [4] => image_lib.html + [5] => input.html + [6] => language.html + [7] => loader.html + [8] => pagination.html + [9] => uri.html + ) + +.. php:function:: read_file($file) + + :param string $file: File path + :returns: File contents or FALSE on failure + :rtype: string + + Returns the data contained in the file specified in the path. + + Example:: + + $string = read_file('./path/to/file.php'); + + The path can be a relative or full server path. Returns FALSE (boolean) on failure. + + .. note:: The path is relative to your main site index.php file, NOT your + controller or view files. CodeIgniter uses a front controller so paths + are always relative to the main site index. + + .. note:: This function is DEPRECATED. Use the native ``file_get_contents()`` + instead. + + .. important:: If your server is running an **open_basedir** restriction this + function might not work if you are trying to access a file above the + calling script. + +.. php:function:: write_file($path, $data[, $mode = 'wb']) + + :param string $path: File path + :param string $data: Data to write to file + :param string $mode: ``fopen()`` mode + :returns: TRUE if the write was successful, FALSE in case of an error + :rtype: bool + + Writes data to the file specified in the path. If the file does not exist then the + function will create it. + + Example:: + + $data = 'Some file data'; + if ( ! write_file('./path/to/file.php', $data)) + {      + echo 'Unable to write the file'; + } + else + {      + echo 'File written!'; + } + + You can optionally set the write mode via the third parameter:: + + write_file('./path/to/file.php', $data, 'r+'); + + The default mode is 'wb'. Please see the `PHP user guide `_ + for mode options. + + .. note: In order for this function to write data to a file, its permissions must + be set such that it is writable. If the file does not already exist, + then the directory containing it must be writable. + + .. note:: The path is relative to your main site index.php file, NOT your + controller or view files. CodeIgniter uses a front controller so paths + are always relative to the main site index. + + .. note:: This function acquires an exclusive lock on the file while writing to it. + +.. php:function:: delete_files($path[, $del_dir = FALSE[, $htdocs = FALSE]]) + + :param string $path: Directory path + :param bool $del_dir: Whether to also delete directories + :param bool $htdocs: Whether to skip deleting .htaccess and index page files + :returns: TRUE on success, FALSE in case of an error + :rtype: bool + + Deletes ALL files contained in the supplied path. + + Example:: + + delete_files('./path/to/directory/'); + + If the second parameter is set to TRUE, any directories contained within the supplied + root path will be deleted as well. + + Example:: + + delete_files('./path/to/directory/', TRUE); + + .. note:: The files must be writable or owned by the system in order to be deleted. + +.. php:function:: get_filenames($source_dir[, $include_path = FALSE]) + + :param string $source_dir: Directory path + :param bool $include_path: Whether to include the path as part of the filenames + :returns: An array of file names + :rtype: array + + Takes a server path as input and returns an array containing the names of all files + contained within it. The file path can optionally be added to the file names by setting + the second parameter to TRUE. + + Example:: + + $controllers = get_filenames(APPPATH.'controllers/'); + +.. php:function:: get_dir_file_info($source_dir, $top_level_only) + + :param string $source_dir: Directory path + :param bool $top_level_only: Whether to look only at the specified directory (excluding sub-directories) + :returns: An array containing info on the supplied directory's contents + :rtype: array + + Reads the specified directory and builds an array containing the filenames, filesize, + dates, and permissions. Sub-folders contained within the specified path are only read + if forced by sending the second parameter to FALSE, as this can be an intensive + operation. + + Example:: + + $models_info = get_dir_file_info(APPPATH.'models/'); + +.. php:function:: get_file_info($file[, $returned_values = array('name', 'server_path', 'size', 'date')]) + + :param string $file: File path + :param array $returned_values: What type of info to return + :returns: An array containing info on the specified file or FALSE on failure + :rtype: array + + Given a file and path, returns (optionally) the *name*, *path*, *size* and *date modified* + information attributes for a file. Second parameter allows you to explicitly declare what + information you want returned. + + Valid ``$returned_values`` options are: `name`, `size`, `date`, `readable`, `writeable`, + `executable` and `fileperms`. + +.. php:function:: get_mime_by_extension($filename) + + :param string $filename: File name + :returns: MIME type string or FALSE on failure + :rtype: string + + Translates a filename extension into a MIME type based on *config/mimes.php*. + Returns FALSE if it can't determine the type, or read the MIME config file. + + :: + + $file = 'somefile.png'; + echo $file.' is has a mime type of '.get_mime_by_extension($file); + + .. note:: This is not an accurate way of determining file MIME types, and + is here strictly for convenience. It should not be used for security + purposes. + +.. php:function:: symbolic_permissions($perms) + + :param int $perms: Permissions + :returns: Symbolic permissions string + :rtype: string + + Takes numeric permissions (such as is returned by ``fileperms()``) and returns + standard symbolic notation of file permissions. + + :: + + echo symbolic_permissions(fileperms('./index.php')); // -rw-r--r-- + +.. php:function:: octal_permissions($perms) + + :param int $perms: Permissions + :returns: Octal permissions string + :rtype: string + + Takes numeric permissions (such as is returned by ``fileperms()``) and returns + a three character octal notation of file permissions. + + :: + + echo octal_permissions(fileperms('./index.php')); // 644 + +.. php:function:: set_realpath($path[, $check_existance = FALSE]) + + :param string $path: Path + :param bool $check_existance: Whether to check if the path actually exists + :returns: An absolute path + :rtype: string + + This function will return a server path without symbolic links or + relative directory structures. An optional second argument will + cause an error to be triggered if the path cannot be resolved. + + Examples:: + + $file = '/etc/php5/apache2/php.ini'; + echo set_realpath($file); // Prints '/etc/php5/apache2/php.ini' + + $non_existent_file = '/path/to/non-exist-file.txt'; + echo set_realpath($non_existent_file, TRUE); // Shows an error, as the path cannot be resolved + echo set_realpath($non_existent_file, FALSE); // Prints '/path/to/non-exist-file.txt' + + $directory = '/etc/php5'; + echo set_realpath($directory); // Prints '/etc/php5/' + + $non_existent_directory = '/path/to/nowhere'; + echo set_realpath($non_existent_directory, TRUE); // Shows an error, as the path cannot be resolved + echo set_realpath($non_existent_directory, FALSE); // Prints '/path/to/nowhere' From 962c131a9ae27e83fe71f595359a2247815e7b57 Mon Sep 17 00:00:00 2001 From: Portaflex Date: Sun, 11 Sep 2016 10:35:52 +0200 Subject: [PATCH 2/2] Filesystem_helper --- system/Helpers/filesystem_helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/Helpers/filesystem_helper.php b/system/Helpers/filesystem_helper.php index 51e9b1ca365b..a57cf2506bd6 100644 --- a/system/Helpers/filesystem_helper.php +++ b/system/Helpers/filesystem_helper.php @@ -307,7 +307,7 @@ function get_dir_file_info(string $source_dir, bool $top_level_only = true, bool * @param mixed array or comma separated string of information returned * @return array */ - function get_file_info(strin $file, $returned_values = ['name', 'server_path', 'size', 'date']): array + function get_file_info(string $file, $returned_values = ['name', 'server_path', 'size', 'date']): array { if ( ! file_exists($file)) {