Loop through paths / files in array and remove path - php

i'm working on a Joomla 3.x component, I'm trying to build an array with folders, files and filenames, I can get the Folders and Files , including paths, which I want, but have been unable to get it to return the names using "basename()" to get just the name.ext . I get an error regarding passing an array to "basename()" in lieu of a string, I've tried "foreach" but it only returns the last item in the array.
Below is the code
function getPathContents() {
$this->directory = 'some/directory';
$recursive = '1';
$this->recursive = ($recursive == 'true' || $recursive == 'recursive' || $recursive == '1');
$this->exclude_folders = '';
$this->exclude_files = '';
$options = array();
$filenames = array();
$filename = basename($filenames);
$path = $this->directory;
if (!is_dir($path))
{
if (is_dir(JPATH_ROOT . '/' . $path))
{
$path = JPATH_ROOT . '/' . $path;
}
else
{
return;
}
}
$path = JPath::clean($path);
$folders = JFolder::folders($path, $this->filter, $this->recursive, true);
// Build the options list from the list of folders.
if (is_array($folders))
{
foreach ($folders as $folder)
{
$options['folders'] = $folders;
$files = JFolder::files($folder, $this->filter, $this->recursive, true);
if (is_array($files))
{
foreach ($files as $file)
{
$options['files'] = $files;
}
$filenames = JFolder::files($folder, $this->filter, $this->recursive, true);
if (is_array($filenames))
{
foreach ($files as $filename)
{
$options['filenames'] = $filename;
}
}
}
}
return $options;
}
any help would be great
UPDATE: Below Code will provide list of Folders and additional arrays of files located in each folder. Each file array is labeled with the Folders name.
Code
function getFolersfiles() {
$this->directory = 'yourpath';
$recursive = '1';
$this->recursive = ($recursive == 'true' || $recursive == 'recursive' || $recursive == '1');
$this->exclude_folders = '';
$this->exclude_files = '';
$path = $this->directory;
if (!is_dir($path))
{
if (is_dir(JPATH_ROOT . '/' . $path))
{
$path = JPATH_ROOT . '/' . $path;
}
else
{
return;
}
}
$path = JPath::clean($path);
$options2['folders'] = JFolder::folders($path, $this->filter, $this->recursive, true);
// Build the options list from the list of folders.
if (is_array($options2))
{
foreach ($options2['folders'] as $option2)
{
$optionname = basename($option2);
$options2[$optionname] = JFolder::files($option2, $this->filter, $this->recursive, true);
//$options2['filepath'] = JFolder::files($option2, $this->filter, $this->recursive, true);
}
return $options2;
}
Thank you for the help

Related

How can I prevent double checks in this function?

Here is a code to search and return the existing files from the given directories:
<?php
function getDirContents($directories, &$results = array()) {
$length = count($directories);
for ($i = 0; $i < $length; $i++) {
if(is_file($directories[$i])) {
if(file_exists($directories[$i])) {
$path = $directories[$i];
$directory_path = basename($_SERVER['REQUEST_URI']);
$results[] = 'https://' . $_SERVER['SERVER_NAME'] . str_replace($directory_path, "", $_SERVER['REQUEST_URI']) .$path;
}
} else {
$files = array_diff(scandir($directories[$i]), array('..', '.'));
foreach($files as $key => $value) {
$path = $directories[$i].DIRECTORY_SEPARATOR.$value;
if(is_dir($path)) {
getDirContents([$path], $results);
} else {
$directory_path = basename($_SERVER['REQUEST_URI']);
$results[] = 'https://' . $_SERVER['SERVER_NAME'] . str_replace($directory_path, "", $_SERVER['REQUEST_URI']) .$path;
}
}
}
}
return $results;
}
echo json_encode(getDirContents($_POST['directories']));
So you can pass an array of file addresses and directories and get what ever files inside those directories, Note if you pass a file address instead of a directory address the function checks if there is such a file and if there is it returns its address in the result .
The issue is for the directories it works fine but the files repeat twice in the result and for each file the function double checks this if statement in the code:
if(is_file($directories[$i]))
Here is a result of the function note that contemporary.mp3 and Japanese.mp3
has been re checked and added to the result.
How can I solve this?
If $directories contains both a directory and a file within that directory, you'll add the file to the result for the filename and also when scanning the directory.
A simple fix is to check whether the filename is already in the result before adding it.
<?php
function getDirContents($directories, &$results = array()) {
foreach ($directories as $name) {
if(is_file($name)) {
$path = $name;
$directory_path = basename($_SERVER['REQUEST_URI']);
$new_path = 'https://' . $_SERVER['SERVER_NAME'] . str_replace($directory_path, "", $_SERVER['REQUEST_URI']) .$path;
if (!in_array($new_path, $results)) {
$results[] = $new_path;
}
} elseif (is_dir($name)) {
$files = array_diff(scandir($name), array('..', '.'));
foreach($files as $key => $value) {
$path = $name.DIRECTORY_SEPARATOR.$value;
if(is_dir($path)) {
getDirContents([$path], $results);
} else {
$directory_path = basename($_SERVER['REQUEST_URI']);
$new_path = 'https://' . $_SERVER['SERVER_NAME'] . str_replace($directory_path, "", $_SERVER['REQUEST_URI']) .$path;
if (!in_array($new_path, $results)) {
$results[] = $new_path;
}
}
}
}
}
return $results;
}

PHP flatten a directory

I'm using flysystem to work with my files.
I don't see an easy way to flatten a directory so I used this
public function flattenDir($dir, $destination = null) {
$files = find($dir . '/*');
foreach ($files as $file) {
$localdir = dirname($file);
if (!isDirectory($file)) {
$destination = $dir . '/' . basename($file);
move($file, $destination);
}
if (isDirectory($localdir) && isEmptyDirectory($localdir) && ($localdir != $dir)) {
remove($localdir);
}
}
}
Is there an easier way by using flysystem?
I finally used that. It also manage a few things that I needed
I'm using flysystem mount manager but this script could be easily adapted to work with the default flysystem instance.
$elements should be the result of manager->listContents('local://my_dir_to_flatten');
And $root is a string my_dir_to_flatten in my case.
public function flatten($elements , $root) {
$files = array();
$directories = array();
//Make difference between files and directories
foreach ($elements as $element) {
if( $element['type'] == 'file' ) {
array_push($files, $element);
} else {
array_push($directories, $element);
}
}
//Manage files
foreach ($files as $file) {
//Dont move file already in root
if($file['dirname'] != $root) {
//Check if filename already used in root directory
if ( $this->manager->has('local://'.$root . '/' . $file['basename']) ) {
//Manage if file don't have extension
if( isset( $file['extension']) ) {
$file['basename'] = $file['filename'] . uniqid() . '.' . $file['extension'];
} else {
$file['basename'] = $file['filename'] . uniqid();
}
}
//Move the file
$this->manager->move('local://' . $file['path'] , 'local_process://' . $root . '/' . $file['basename']);
}
}
//Delete folders
foreach ($not_files as $file) {
$this->manager->deleteDir( 'local://' . $file['path'] );
}
}

Check multiple levels of folders is dir

When user types in a new folder name and the name all ready exists in sub folders and main folder then will return error.
Currently it returns error even if folder not exist in sub folders etc.
Question How can I make it so that it can check main directory and sub folders and return error if exits else lets me create it.
The post for foldername is $this->input->post('folder')
Checks if any subfolders exists with same name.
if (!$json) {
$results = scandir($DIR_IMAGE);
foreach ($results as $result) {
if ($result === '.' or $result === '..') continue;
if (is_dir($DIR_IMAGE . '/' . $result)) {
$json['error'] = 'This folder name all ready';
}
}
}
Full Function
public function folder() {
$json = array();
$DIR_IMAGE = FCPATH . 'images/';
if ($this->input->get('directory')) {
$directory = $this->input->get('directory') . '/';
} else {
$directory = 'catalog/';
}
if (!$json) {
$arrPathParts = explode('/', $directory);
if (count($arrPathParts) > 3) {
$json['error'] = 'You can not create a new folder here try back one level.';
}
}
if (!$json) {
$re = '/\W/'; // \W matches any non-word character (equal to [^a-zA-Z0-9_])
$str = $this->input->post('folder');
$is_correct_foldername = !preg_match($re, $str) ? true : false;
if (!$is_correct_foldername) {
$json['error'] = 'You have some symbols or no spaces that are not allowed';
}
}
// Checks if any subfolders exists with same name.
if (!$json) {
$results = scandir($DIR_IMAGE);
foreach ($results as $result) {
if ($result === '.' or $result === '..') continue;
if (is_dir($DIR_IMAGE . '/' . $result)) {
$json['error'] = 'This folder name all ready';
}
}
}
// Every thing passes now will create folder
if (!$json) {
mkdir($DIR_IMAGE . $directory . $this->input->post('folder'), 0777, true);
$json['success'] = 'Your folder is now created!';
}
$this->output->set_content_type('Content-Type: application/json');
$this->output->set_output(json_encode($json));
}
In your code:
if (!$json) {
$results = scandir($DIR_IMAGE);
foreach ($results as $result) {
if ($result === '.' or $result === '..') continue;
if (is_dir($DIR_IMAGE . '/' . $result)) {
$json['error'] = 'This folder name all ready';
}
}
}
You are only checking whethe each directory name returned from scandir() is a directory - which would always be true.
Assuming the name of the directory you want to check for is $this->input->post('foldername'), you can do:
if (!$json) {
$results = scandir($DIR_IMAGE);
foreach ($results as $result) {
if ($result === '.' or $result === '..') continue;
if (is_dir($DIR_IMAGE . '/' . $result) && ($result == $this->input->post('foldername'))) {
$json['error'] = 'This folder name all ready';
}
}
}
As a side note, you should try to use the DIRECTORY_SEPARATOR constant instead of '/' directory.
Finally, also be careful that directory name capitalisation could be a factor on different operating systems.
I have now got a solution using RecursiveIteratorIterator and in array
public function folder() {
$json = array();
$DIR_IMAGE = FCPATH . 'images/';
if ($this->input->get('directory')) {
$directory = $this->input->get('directory') . '/';
} else {
$directory = 'catalog/';
}
if (!$json) {
$arrPathParts = explode('/', $directory);
if (count($arrPathParts) > 3) {
$json['error'] = 'You can not create a new folder here try back one level.';
}
}
if (!$json) {
$re = '/\W/'; // \W matches any non-word character (equal to [^a-zA-Z0-9_])
$str = $this->input->post('folder');
$is_correct_foldername = !preg_match($re, $str) ? true : false;
if (!$is_correct_foldername) {
$json['error'] = 'You have some symbols or no spaces that are not allowed';
}
}
// Checks if any subfolders exists with same name. #todo clean paths
if (!$json) {
$root = $DIR_IMAGE . 'catalog/';
$iter = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore "Permission denied"
);
$paths = array($root);
foreach ($iter as $path => $dir) {
if ($dir->isDir()) {
$paths[] = basename($path);
}
}
if (in_array($this->input->post('folder'), $paths)) {
$json['error'] = 'You have all ready created a folder called ' . $this->input->post('folder') . ' in one of your directories!';
}
}
if (!$json) {
mkdir($DIR_IMAGE . $directory . $this->input->post('folder'), 0777, true);
$json['success'] = 'Your folder is now created!';
}
$this->output->set_content_type('Content-Type: application/json');
$this->output->set_output(json_encode($json));
}

building a simple directory browser using php RecursiveDirectoryIterator

Hi
i am trying to build simple directory browser to browse folders and sub-folders uing php RecursiveDirectoryIterator .. i need help of how to create this. i have started with the following code.
$dir = dirname(__FILE__); //path of the directory to read
$iterator = new RecursiveDirectoryIterator($dir);
foreach (new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST) as $file) {
if (!$file->isFile()) { //create hyperlink if this is a folder
echo "" . $file->getFilename() . "\";
}else{ //do not link if this is a file
$file->getFilename()
}
}
Allow me to code that for you....
<?php
$root = __DIR__;
function is_in_dir($file, $directory, $recursive = true, $limit = 1000) {
$directory = realpath($directory);
$parent = realpath($file);
$i = 0;
while ($parent) {
if ($directory == $parent) return true;
if ($parent == dirname($parent) || !$recursive) break;
$parent = dirname($parent);
}
return false;
}
$path = null;
if (isset($_GET['file'])) {
$path = $_GET['file'];
if (!is_in_dir($_GET['file'], $root)) {
$path = null;
} else {
$path = '/'.$path;
}
}
if (is_file($root.$path)) {
readfile($root.$path);
return;
}
if ($path) echo '..<br />';
foreach (glob($root.$path.'/*') as $file) {
$file = realpath($file);
$link = substr($file, strlen($root) + 1);
echo ''.basename($file).'<br />';
}

PHP Get all subdirectories of a given directory

How can I get all sub-directories of a given directory without files, .(current directory) or ..(parent directory)
and then use each directory in a function?
Option 1:
You can use glob() with the GLOB_ONLYDIR option.
Option 2:
Another option is to use array_filter to filter the list of directories. However, note that the code below will skip valid directories with periods in their name like .config.
$dirs = array_filter(glob('*'), 'is_dir');
print_r($dirs);
Here is how you can retrieve only directories with GLOB:
$directories = glob($somePath . '/*' , GLOB_ONLYDIR);
The Spl DirectoryIterator class provides a simple interface for viewing the contents of filesystem directories.
$dir = new DirectoryIterator($path);
foreach ($dir as $fileinfo) {
if ($fileinfo->isDir() && !$fileinfo->isDot()) {
echo $fileinfo->getFilename().'<br>';
}
}
Almost the same as in your previous question:
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($yourStartingPath),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
echo strtoupper($file->getRealpath()), PHP_EOL;
}
}
Replace strtoupper with your desired function.
Try this code:
<?php
$path = '/var/www/html/project/somefolder';
$dirs = array();
// directory handle
$dir = dir($path);
while (false !== ($entry = $dir->read())) {
if ($entry != '.' && $entry != '..') {
if (is_dir($path . '/' .$entry)) {
$dirs[] = $entry;
}
}
}
echo "<pre>"; print_r($dirs); exit;
In Array:
function expandDirectoriesMatrix($base_dir, $level = 0) {
$directories = array();
foreach(scandir($base_dir) as $file) {
if($file == '.' || $file == '..') continue;
$dir = $base_dir.DIRECTORY_SEPARATOR.$file;
if(is_dir($dir)) {
$directories[]= array(
'level' => $level
'name' => $file,
'path' => $dir,
'children' => expandDirectoriesMatrix($dir, $level +1)
);
}
}
return $directories;
}
//access:
$dir = '/var/www/';
$directories = expandDirectoriesMatrix($dir);
echo $directories[0]['level'] // 0
echo $directories[0]['name'] // pathA
echo $directories[0]['path'] // /var/www/pathA
echo $directories[0]['children'][0]['name'] // subPathA1
echo $directories[0]['children'][0]['level'] // 1
echo $directories[0]['children'][1]['name'] // subPathA2
echo $directories[0]['children'][1]['level'] // 1
Example to show all:
function showDirectories($list, $parent = array())
{
foreach ($list as $directory){
$parent_name = count($parent) ? " parent: ({$parent['name']}" : '';
$prefix = str_repeat('-', $directory['level']);
echo "$prefix {$directory['name']} $parent_name <br/>"; // <-----------
if(count($directory['children'])){
// list the children directories
showDirectories($directory['children'], $directory);
}
}
}
showDirectories($directories);
// pathA
// - subPathA1 (parent: pathA)
// -- subsubPathA11 (parent: subPathA1)
// - subPathA2
// pathB
// pathC
You can try this function (PHP 7 required)
function getDirectories(string $path) : array
{
$directories = [];
$items = scandir($path);
foreach ($items as $item) {
if($item == '..' || $item == '.')
continue;
if(is_dir($path.'/'.$item))
$directories[] = $item;
}
return $directories;
}
Non-recursively List Only Directories
The only question that direct asked this has been erroneously closed, so I have to put it here.
It also gives the ability to filter directories.
/**
* Copyright © 2020 Theodore R. Smith <https://www.phpexperts.pro/>
* License: MIT
*
* #see https://stackoverflow.com/a/61168906/430062
*
* #param string $path
* #param bool $recursive Default: false
* #param array $filtered Default: [., ..]
* #return array
*/
function getDirs($path, $recursive = false, array $filtered = [])
{
if (!is_dir($path)) {
throw new RuntimeException("$path does not exist.");
}
$filtered += ['.', '..'];
$dirs = [];
$d = dir($path);
while (($entry = $d->read()) !== false) {
if (is_dir("$path/$entry") && !in_array($entry, $filtered)) {
$dirs[] = $entry;
if ($recursive) {
$newDirs = getDirs("$path/$entry");
foreach ($newDirs as $newDir) {
$dirs[] = "$entry/$newDir";
}
}
}
}
return $dirs;
}
<?php
/*this will do what you asked for, it only returns the subdirectory names in a given
path, and you can make hyperlinks and use them:
*/
$yourStartingPath = "photos\\";
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($yourStartingPath),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
$path = strtoupper($file->getRealpath()) ;
$path2 = PHP_EOL;
$path3 = $path.$path2;
$result = end(explode('/', $path3));
echo "<br />". basename($result );
}
}
/* best regards,
Sanaan Barzinji
Erbil
*/
?>
This is the one liner code:
$sub_directories = array_map('basename', glob($directory_path . '/*', GLOB_ONLYDIR));
Proper way
/**
* Get all of the directories within a given directory.
*
* #param string $directory
* #return array
*/
function directories($directory)
{
$glob = glob($directory . '/*');
if($glob === false)
{
return array();
}
return array_filter($glob, function($dir) {
return is_dir($dir);
});
}
Inspired by Laravel
The following recursive function returns an array with the full list of sub directories
function getSubDirectories($dir)
{
$subDir = array();
$directories = array_filter(glob($dir), 'is_dir');
$subDir = array_merge($subDir, $directories);
foreach ($directories as $directory) $subDir = array_merge($subDir, getSubDirectories($directory.'/*'));
return $subDir;
}
Source: https://www.lucidar.me/en/web-dev/how-to-get-subdirectories-in-php/
You can use the glob() function to do this.
Here is some documentation on it:
http://php.net/manual/en/function.glob.php
Find all PHP files recursively. The logic should be simple enough to tweak and it aims to be fast(er) by avoiding function calls.
function get_all_php_files($directory) {
$directory_stack = array($directory);
$ignored_filename = array(
'.git' => true,
'.svn' => true,
'.hg' => true,
'index.php' => true,
);
$file_list = array();
while ($directory_stack) {
$current_directory = array_shift($directory_stack);
$files = scandir($current_directory);
foreach ($files as $filename) {
// Skip all files/directories with:
// - A starting '.'
// - A starting '_'
// - Ignore 'index.php' files
$pathname = $current_directory . DIRECTORY_SEPARATOR . $filename;
if (isset($filename[0]) && (
$filename[0] === '.' ||
$filename[0] === '_' ||
isset($ignored_filename[$filename])
))
{
continue;
}
else if (is_dir($pathname) === TRUE) {
$directory_stack[] = $pathname;
} else if (pathinfo($pathname, PATHINFO_EXTENSION) === 'php') {
$file_list[] = $pathname;
}
}
}
return $file_list;
}
If you're looking for a recursive directory listing solutions. Use below code I hope it should help you.
<?php
/**
* Function for recursive directory file list search as an array.
*
* #param mixed $dir Main Directory Path.
*
* #return array
*/
function listFolderFiles($dir)
{
$fileInfo = scandir($dir);
$allFileLists = [];
foreach ($fileInfo as $folder) {
if ($folder !== '.' && $folder !== '..') {
if (is_dir($dir . DIRECTORY_SEPARATOR . $folder) === true) {
$allFileLists[$folder . '/'] = listFolderFiles($dir . DIRECTORY_SEPARATOR . $folder);
} else {
$allFileLists[$folder] = $folder;
}
}
}
return $allFileLists;
}//end listFolderFiles()
$dir = listFolderFiles('your searching directory path ex:-F:\xampp\htdocs\abc');
echo '<pre>';
print_r($dir);
echo '</pre>'
?>
Find all subfolders under a specified directory.
<?php
function scanDirAndSubdir($dir, &$fullDir = array()){
$currentDir = scandir($dir);
foreach ($currentDir as $key => $filename) {
$realpath = realpath($dir . DIRECTORY_SEPARATOR . $filename);
if (!is_dir($realpath) && $filename != "." && $filename != "..") {
scanDirAndSubdir($realpath, $fullDir);
} else {
$fullDir[] = $realpath;
}
}
return $fullDir;
}
var_dump(scanDirAndSubdir('C:/web2.0/'));
Sample :
array (size=4)
0 => string 'C:/web2.0/config/' (length=17)
1 => string 'C:/web2.0/js/' (length=13)
2 => string 'C:/web2.0/mydir/' (length=16)
3 => string 'C:/web2.0/myfile/' (length=17)

Categories