PHP only first level of subdirectories into an array - php

I'm trying to get only the first level of subdirectories into an array.
Does someone know a slimmer and faster way to do this?
$dirs = new RecursiveDirectoryIterator('myroot', RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($dirs, RecursiveIteratorIterator::SELF_FIRST);
$dir_array = array();
foreach( $files AS $file)
{
if($files->isDot()) continue;
if($files->getDepth() > 0) continue;
if( $files->isDir() )
{
$dir_array[] = $file->getFilename();
}
}

Simple as this:
$array = glob('myroot/*', GLOB_ONLYDIR);
To get only the base directory name and not the full path:
$array = array_map('basename', glob('myroot/*', GLOB_ONLYDIR));
See http://php.net/glob

Related

PHP array as variable for diffent array?

How can I load multiple vars each containing a single path into an array variable?
$dir1 = "/path/1/";
$dir2 = "/path/2/";
$dir3 = array($dir1, $dir2);
$directories = array($dir3);
$count = 0;
foreach ($directories as $dir) {
$files = glob("{$dir}*.gz") ?: array();
$count += count($files);
}
When I print $dir3 I get both paths as an array but I cannot get the $dir3 var for $directories to work. I have tried looking for answers but I am unable to find similar use cases. The documentation on php.net is also unclear to me. I'm still new to PHP and programming in general.
I want to figure out how I can have multiple paths in a single var but still have foreach execute on each path in the var.
$dir3 already is an array containing your directories.
By doing $directories = array($dir3);, you're therefore creating an array of arrays.
So you could simply replace your code with:
$dir1 = "/path/1/";
$dir2 = "/path/2/";
$directories = array($dir1, $dir2); // or $directories = [$dir1, $dir2];
$count = 0;
foreach ($directories as $dir) {
$files = glob("{$dir}*.gz");
$count += count($files);
}
However, as #MarkusZeller has pointed out, you can also pass a comma separated list of directories directly to glob, using the GLOB_BRACE flag:
$dir1 = "/path/1/";
$dir2 = "/path/2/";
$commaSeparatedDirectories = implode(',', [$dir1, $dir2]);
$count = count(glob("{{$commaSeparatedDirectories}}*.gz", GLOB_BRACE));

Dir-Content to array - exclude files with specific pattern

I would like to collect all files in a specific directory (at the moment I'm using scandir) - but only those, who do not have a special pattern.
Example:
someimage.png
someimage-150x150.png
someimage-233x333.png
someotherimage.png
someotherimage-760x543.png
someotherimage-150x50.png
In this case I would like to get someimage.png and someotherimage.png as result in my array.
How can I solve this?
To get array of filenames consisting of letters only, you can use this:
$array = array();
$handle = opendir($directory);
while ($file = readdir($handle)) {
if(preg_match('/^[A-Za-z]+\.png$/',$file)){
$array[] = $file;
}
}
The OOP way could be to use a DirectoryIterator in combination with a FilterIterator:
class FilenameFilter extends FilterIterator {
protected $filePattern;
public function __construct(Iterator $iterator , $pattern) {
parent::__construct($iterator);
$this->filePattern = $pattern;
}
public function accept() {
$currentFile = $this->current();
return (1 === preg_match($this->filePattern, $currentFile));
}
}
Usage:
$myFilter = new FilenameFilter(new DirectoryIterator('path/to/your/files'), '/^[a-z-_]*\.(png|PNG|jpg|JPG)$/i');
foreach ($myFilter as $filteredFile) {
// Only files which match your specified pattern should appear here
var_dump($filteredFile);
}
It's just an idea and the code is not tested but. Hope that helps;
$files = array(
"someimage.png",
"someimage-150x150.png",
"someimage-233x333.png",
"someotherimage.png",
"someotherimage-760x543.png",
"someotherimage-150x50.png",
);
foreach ( $files as $key => $value ) {
if ( preg_match( '#\-[0-9]+x[0-9]+\.(png|jpe?g|gif)$#', $value ) ) {
unset( $files[$key] );
}
}
echo '<xmp>' . print_r( $files, 1 ) . '</xmp>';
This regex will fill $correctFiles with all png images that don't contain dimensions (42x42 for example) in their names.
<?php
// here you get the files with scandir, or any method you want
$files = array(
'someimage.png',
'someimage-150x150.png',
'someimage-233x333.png',
'someotherimage.png',
'someotherimage-760x543.png',
'someotherimage-150x50.png'
);
$correctFiles = array(); // This will contain the correct file names
foreach ($files as $file)
if (!preg_match('/^.*-\d+x\d+\.png$/', $file)) // If the file doesn't have "NUMBERxNUMBER" in their name
$correctFiles[] = $file;
print_r($correctFiles); // Here you can do what you want with those files
If you don't want to store the names in an array (faster, less memory consumption), you can use the code below.
<?php
// here you get the files with scandir, or any method you want
$files = array(
'someimage.png',
'someimage-150x150.png',
'someimage-233x333.png',
'someotherimage.png',
'someotherimage-760x543.png',
'someotherimage-150x50.png'
);
foreach ($files as $file)
if (!preg_match('/^.*-\d+x\d+\.png$/', $file)) // If the file doesn't have "NUMBERxNUMBER" in their name
{
print_r($file); // Here you can do what you want with this file
}

php DirectoryIterator sort files by date

I'm using php's DirectoryIterator class to list files in a directory. I can't however figure out an easy way to sort files by date. How is this done with DirectoryIterator
<?php
$dir = new DirectoryIterator('.');
foreach ($dir as $fileinfo) {
echo $fileinfo->getFilename() . '<br>';
}
?>
What if i name my files like whatever_2342345345.ext where the numbers represents time in milliseconds so each file has a unique number. How can we sort by looking at the numbers after underscore
If you need to sort, build an array and sort that.
$files = array();
$dir = new DirectoryIterator('.');
foreach ($dir as $fileinfo) {
$files[$fileinfo->getMTime()][] = $fileinfo->getFilename();
}
ksort($files);
This will build an array with the modified time as the key and an array of filenames as the value. It then sorts via ksort(), which will give you the filenames in order of time modified.
If you then want to re-flatten the structure to a standard array, you can use...
$files = call_user_func_array('array_merge', $files);
If you still want to access all the data available at DirectoryIterator (e.g. isDot() getSize() etc) a possible way is to store the Iterator key on the array you are going to sort, and seek the DirectoryIterator later.
$sorted_keys = array();
$dir_iterator = new DirectoryIterator('.');
foreach ( $dir_iterator as $fileinfo )
{
$sorted_keys[$fileinfo->getMTime()] = $fileinfo->key();
}
ksort($sorted_keys);
/* Iterate `DirectoryIterator` as a sorted array */
foreach ( $sorted_keys as $key )
{
$dir_iterator->seek($key);
$fileinfo = $dir_iterator->current();
/* Use $fileinfo here as a normal DirectoryIterator */
echo $fileinfo->getFilename() . ' ' . $fileinfo->getSize() . '<br>';
}
In case multiple files have the same modified time (updated):
$files = array();
$mtimes = array();
$dir = new DirectoryIterator('.');
foreach($dir as $file){
if(!$file->isFile())
continue;
$mtime = $file->getMTime();
if(!$mtimes[$mtime]){
$files[$mtime.'.0'] = $file->getFilename();
$mtimes[$mtime] = 1;
}else{
$files[$mtime.'.'.$mtimes[$mtime]++] = $file->getFilename();
}
}
ksort($files);

Get folders with PHP glob - unlimited levels deep

I have this working function that finds folders and creates an array.
function dua_get_files($path)
{
foreach (glob($path . "/*", GLOB_ONLYDIR) as $filename)
{
$dir_paths[] = $filename;
}
return $dir_paths;
}
This function can only find the directories on the current location. I want to find the directory paths in the child folders and their children and so on.
The array should still be a flat list of directory paths.
An example of how the output array should look like
$dir_path[0] = 'path/folder1';
$dir_path[1] = 'path/folder1/child_folder1';
$dir_path[2] = 'path/folder1/child_folder2';
$dir_path[3] = 'path/folder2';
$dir_path[4] = 'path/folder2/child_folder1';
$dir_path[5] = 'path/folder2/child_folder2';
$dir_path[6] = 'path/folder2/child_folder3';
If you want to recursively work on directories, you should take a look at the RecursiveDirectoryIterator.
$path = realpath('/etc');
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
echo "$name\n";
}
Very strange - everybody advice recursion, but better just cycle:
$dir ='/dir';
while($dirs = glob($dir . '/*', GLOB_ONLYDIR)) {
$dir .= '/*';
if(!$result) {
$result = $dirs;
} else {
$result = array_merge($result, $dirs);
}
}
Try this instead:
function dua_get_files($path)
{
$data = array();
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach ($files as $file)
{
if (is_dir($file) === true)
{
$data[] = strval($file);
}
}
return $data;
}
Use this function :
function dua_get_files($path)
{
$dir_paths = array();
foreach (glob($path . "/*", GLOB_ONLYDIR) as $filename)
{
$dir_paths[] = $filename;
$a = glob("$filename/*", GLOB_ONLYDIR);
if( is_array( $a ) )
{
$b = dua_get_files( "$filename/*" );
foreach( $b as $c )
{
$dir_paths[] = $c;
}
}
}
return $dir_paths;
}
You can use php GLOB function, but you must create a recursive function to scan directories at infinite level depth. Then store results in a global variable.
function dua_get_files($path) {
global $dir_paths; //global variable where to store the result
foreach ($path as $dir) { //loop the input
$dir_paths[] = $dir; //can use also "basename($dir)" or "realpath($dir)"
$subdir = glob($dir . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR); //use DIRECTORY_SEPARATOR to be OS independent
if (!empty($subdir)) { //if subdir is not empty make function recursive
dua_get_files($subdir); //execute the function again with current subdir
}
}
}
//usage:
$path = array('galleries'); //suport absolute or relative path. support one or multiple path
dua_get_files($path);
print('<pre>'.print_r($dir_paths,true).'</pre>'); //debug
For PHP, if you are on a linux/unix, you can also use backticks (shell execution) with the unix find command. Directory searching on the filesystem can take a long time and hit a loop -- the system find command is already built for speed and to handle filesystem loops. In other words, the system exec call is likely to cost far less cpu-time than using PHP itself to search the filesystem tree.
$dirs = `find $path -type d`;
Remember to sanitize the $path input, so other users don't pass in security compromising path names (like from the url or something).
To put it into an array
$dirs = preg_split("/\s*\n+\s*/",`find $path -type d`,-1,PREG_SPLIT_NO_EMPTY);

List content of directory (php)

I have a folder. I want to put every file in this folder into an array and afterwards I want to echo them all in an foreach loop.
What's the best way to do this?
Thanks!
Scandir is what you're looking for
http://php.net/manual/en/function.scandir.php
<?php
$dir = '/tmp';
$files1 = scandir($dir);
print_r($files1);
?>
Or use combination of opendir and readdir
http://php.net/manual/en/function.readdir.php
Doesn't get much easier than this
http://ca3.php.net/manual/en/function.scandir.php
Don't forget to filter out hidden and parent directories (they start with a dot) on linux.
An Alternative:
define('PATH', 'files/');
$filesArray = array();
$filesArray = glob(PATH . '*', GLOB_ONLYDIR);
This method allow you to specify/filter a by file type. E.G.,
define('PATH', 'files/');
define('FILE_TYPE', '.jpg');
$filesArray = array();
$filesArray = glob(PATH . '*' . FILE_TYPE, GLOB_ONLYDIR);
You can also get the FULL path name to the file by removing the parameter 'GLOB_ONLYDIR'
This works for files and folders in subfolders too. Return list of folders and list of files with their path.
$dir = __DIR__; //work only for this current dir
function listFolderContent($dir,$path=''){
$r = array();
$list = scandir($dir);
foreach ($list as $item) {
if($item!='.' && $item!='..'){
if(is_file($path.$item)){
$r['files'][] = $path.$item;
}elseif(is_dir($path.$item)){
$r['folders'][] = $path.$item;
$sub = listFolderContent($path.$item,$path.$item.'/');
if(isset($sub['files']) && count($sub['files'])>0)
$r['files'] = isset ($r['files'])?array_merge ($r['files'], $sub['files']):$sub['files'];
if(isset($sub['folders']) && count($sub['folders'])>0)
$r['folders'] = array_merge ($r['folders'], $sub['folders']);
}
}
}
return $r;
}
$list = listFolderContent($dir);
var_dump($list['files']);
var_dump($list['folders']);
Edit: dwich answer is better. I will leave this just for information.
readdir.
<?php
if ($handle = opendir('/path/to/dir')) {
echo "Directory handle: $handle\n";
echo "Files:\n";
while (false !== ($file = readdir($handle))) {
echo "$file\n";
}
closedir($handle);
}
?>
Hope this helps.
—Alberto

Categories