php DirectoryIterator sort files by date - php

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);

Related

List all json files from folder then sort by date and paginate it

I need help to make it work.
I'm using this code for list all json files from folder and paginate it and this works well.
<?php
$all_files = [];
$dir = new DirectoryIterator(dirname(__FILE__) . DIRECTORY_SEPARATOR . constant('POSTS_DIR'));
foreach ($dir as $fileinfo) {
if ($fileinfo->isFile() && in_array($fileinfo->getExtension(), array('json'))) {
array_push($all_files, realpath(constant('POSTS_DIR')) . '/' . $fileinfo->getBasename());
}
}
?>
But how can I implement sort by getMTime() and krsort()?
I want the last modified files at first.
Store the mtime values in a separate array, then sort by them with usort.
Before the loop, add:
$mtimes = [];
In the loop:
$all_files[] = $file = realpath(constant('POSTS_DIR')) . '/' . $fileinfo->getBasename());
$mtimes[$file] = $fileinfo->getMTime();
Then, after the loop:
usort($all_files, function ($a, $b) use ($mtimes) {
return $mtimes[$b] <=> $mtimes[$a];
});

PHP only first level of subdirectories into an array

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

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
}

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);

PHP scandir results: sort by folder-file, then alphabetically

PHP manual for scandir: By default, the sorted order is alphabetical in ascending order.
I'm building a file browser (in Windows), so I want the addresses to be returned sorted by folder/file, then alphabetically in those subsets.
Example: Right now, I scan and output
Aardvark.txt
BarDir
BazDir
Dante.pdf
FooDir
and I want
BarDir
BazDir
FooDir
Aardvark.txt
Dante.pdf
Other than a usort and is_dir() solution (which I can figure out myself), is there a quick and efficient way to do this?
The ninja who wrote this comment is on the right track - is that the best way?
Does this give you what you want?
function readDir($path) {
// Make sure we have a trailing slash and asterix
$path = rtrim($path, '/') . '/*';
$dirs = glob($path, GLOB_ONLYDIR);
$files = glob($path);
return array_unique(array_merge($dirs, $files));
}
$path = '/path/to/dir/';
readDir($path);
Note that you can't glob('*.*') for files because it picks up folders named like.this.
Please try this. A simple function to sort the PHP scandir results by files and folders (directories):
function sort_dir_files($dir)
{
$sortedData = array();
foreach(scandir($dir) as $file)
{
if(is_file($dir.'/'.$file))
array_push($sortedData, $file);
else
array_unshift($sortedData, $file);
}
return $sortedData;
}
I'm late to the party but I like to offer my solution with readdir() rather than with glob(). What I was missing from the solution is a recursive version of your solution. But with readdir it's faster than with glob.
So with glob it would look like this:
function myglobdir($path, $level = 0) {
$dirs = glob($path.'/*', GLOB_ONLYDIR);
$files = glob($path.'/*');
$all2 = array_unique(array_merge($dirs, $files));
$filter = array($path.'/Thumbs.db');
$all = array_diff($all2,$filter);
foreach ($all as $target){
echo "$target<br />";
if(is_dir("$target")){
myglobdir($target, ($level+1));
}
}
}
And this one is with readdir but has basically the same output:
function myreaddir($target, $level = 0){
$ignore = array("cgi-bin", ".", "..", "Thumbs.db");
$dirs = array();
$files = array();
if(is_dir($target)){
if($dir = opendir($target)){
while (($file = readdir($dir)) !== false){
if(!in_array($file, $ignore)){
if(is_dir("$target/$file")){
array_push($dirs, "$target/$file");
}
else{
array_push($files, "$target/$file");
}
}
}
//Sort
sort($dirs);
sort($files);
$all = array_unique(array_merge($dirs, $files));
foreach ($all as $value){
echo "$value<br />";
if(is_dir($value)){
myreaddir($value, ($level+1));
}
}
}
closedir($dir);
}
}
I hope someone might find this useful.

Categories