Viewed   77 times

Yes, I know this is ugly code :) And it doesn't work.

I'm globbing all the .php files in a directory and then sorting them by file timestamp, and then I want to echo the file names into each month. But how do I fix this loop so the echoed output of each month is correct? The files by $path are sorted by date, but the $link_title is the same for all files in all months, so I don't see that the $paths are getting arranged by month.

foreach (glob("*.php") as $path) {

        $files[$path] = filemtime($path);

} arsort($files);

$currentdate = strtotime(date("Y-m-d H:i:s"));

$julybegin = strtotime("2018-07-01 00:00:00");
$julyend = strtotime("2018-07-31 23:23:59");

$junebegin = strtotime("2018-06-01 00:00:00");
$juneend = strtotime("2018-06-30 23:23:59");

$maybegin = strtotime("2018-05-01 00:00:00");
$mayend = strtotime("2018-05-31 23:23:59");

$aprilbegin = strtotime("2018-04-01 00:00:00");
$aprilend = strtotime("2018-04-30 23:23:59");

$timestamp = filemtime ($path);
$link_title = $path;

    foreach ($files as $path => $timestamp) {

if($timestamp > $julybegin && $timestamp < $julyend) {
echo '<li>';
echo $path . ' ' . $link_title;
echo '</li>';

} else {

if($timestamp > $junebegin && $timestamp < $juneend) {
echo '<li>';
echo $path . ' ' . $link_title;
echo '</li>';

} else {

if (($timestamp >= $maybegin) && ($timestamp <= $mayend)){

echo '<li>';
echo $path . ' ' . $link_title;
echo '</li>';

} else {

if (($timestamp >= $aprilbegin) && ($timestamp <= $aprilend)){

echo '<li>';
echo $path . ' ' . $link_title;
echo '</li>';

}
}
}
}
}

Edit 6/21/18

This works and outputs the correct markup for a jQuery accordian:

foreach (glob("*.php") as $path) {

    $timestamp = filemtime($path);

    $files[date('F', $timestamp)][$path] = $timestamp;

}

arsort($files); 

        echo '<div class="accordion">';

        foreach ($files as $month => $paths) {

            krsort($paths);

        echo '<h6>' . $month . '</h6><div><ul>';

        foreach ($paths as $path => $timestamp) {

        echo '<li>' . $path . '</li>';
    }
        echo '</ul></div>';

    }
        echo '</div>';

 Answers

3

Since your end-goal is to group the filenames by month, you need a loop to show you only the entries from the $files array that match a particular month:

$months = array(
    '2018-07',
    '2018-06',
    '2018-05',
    '2018-04'
);

foreach ($months as $month) {
    // just some example markup
    echo '<p>' . $month . '</p>';

    echo '<ul>';
    foreach ($files as $path => $timestamp) {
        if (date('Y-m', $timestamp) == $month) {
            echo '<li>' . $path . '</li>';
        }
    }
    echo '</ul>';
}

If you already know that you're not going to use your $files array for anything else, another option might be to simply group the files while processing the glob results:

foreach (glob("*.php") as $path) {
    $timestamp = filemtime($path);

    $files[date('Y-m', $timestamp)][$path] = $timestamp;
}

Since it's now a nested array, you can't simply arsort() the whole thing. But you can delay that until you want to loop over the file list to generate the desired HTML output:

// first sort the whole thing by keys (year + month), so that the months are sorted:
krsort($files);

foreach ($files as $month => $paths) {
    // now sort the paths by their timestamps
    arsort($paths);

    // and output whatever markup you want
    echo '<p>' . $month . '</p>';

    echo '<ul>';
    foreach ($paths as $path => $timestamp) {
        echo '<li>' . $path . '</li>';
    }
    echo '</ul>';
}

EDIT AFTER THE UPDATED QUESTION

You seem to be misunderstanding a basic concept of defining variables in loops.

You have two separate loops in your code: one that processes the result of glob() and one that turns the result of that processing into markup for your jQuery accordion.

By defining $monthname in the first loop, it will maintain its last value when processing the glob() result is finished.

You want to use $monthname in the second loop, but you're not setting it there. Therefore, it will always be the same value: the last value it was when processing the glob() result. Instead, you should define $monthname where you want to use it.

Sidenote: I used date('Y-m') in my answer as an example, since I didn't know what you were going to display. If you always want to display the date as date('F'), you can simply use that for the array key instead. Then there's no need to keep track of a separate $monthname variable and you can simply output the $month value in the second loop. However, this might not be what you want, if you have files in there that have been modified in the same month but in different years.

As for the specific warnings you're seeing, they are caused by the fact that you're storing each file into the $files array twice now:

foreach (glob("*.php") as $path) {
    if($path == 'index.php') {
    continue;
    }  // removes all files names index.php

/**** HERE: ****/
    $files[$path] = filemtime($path);

    $timestamp = filemtime ($path);
    // needed to define $timestamp

/**** AND HERE: ****/
    $files[date('Y-m', $timestamp)][$path] = $timestamp;

    $monthname = (date('F', $timestamp));
    // display month full name instead of month numeral
}

This means that the $files array looks something like this:

Array
(
    [/var/www/file.php] => 1529239095
    [2018-06] => Array
        (
            [/var/www/file.php] => 1529239095
        )

)

Because you still have that first assignment in there, you now occasionally try to sort (which you copy/pasted incorrectly, it should be an arsort() instead of krsort()) a timestamp (integer) instead of an array of path => timestamp entries. You should remove that first assignment.

Monday, October 17, 2022
4

Try this:

glob("$dir/{,[1-9]}[0-9].jpg", GLOB_BRACE);

The GLOB_BRACE option tells it to recognize alternatives in braces, so {,[1-9]} matches either an empty string or a non-zero digit. Then this is followed by any digit. So with the empty string it matches 0 through 9, and with the non-zero digit it matches 10 through 99.

Sunday, November 13, 2022
 
1

This seems to be covered as an issue in this bug report on php.net: https://bugs.php.net/bug.php?id=33047

The last post on that thread is about it not being a bug, but an issue from how glob treats brackets, as part of the regular expression. I'm not sure I agree. It seems you can work around this, unless you cannot move up into the parent folder.

If you remove the first requirement of being in the inside [test] folder, you can get the file listing by using a syntax like below:

chdir('..');
$glob = glob("[[]test[]]/*");

Given these complications, I would recommend not using the glob function if you are running into issues on windows machines, and look at other file listing functions like readdir.

Saturday, December 24, 2022
3

tablefunc module

I would use crosstab() for this. Install the additional module tablefunc if you don't have already:

CREATE EXTENSION tablefunc

Basics here:
PostgreSQL Crosstab Query

How to deal with extra columns:
Pivot on Multiple Columns using Tablefunc

Advanced usage:
Dynamic alternative to pivot with CASE and GROUP BY

Setup

CREATE TEMP TABLE tbl
   (id int, extra_info varchar(3), month date, value int);
   
INSERT INTO tbl (id, extra_info, month, value)
VALUES
   (1, 'abc', '2012-01-01', 10),
   (1, 'abc', '2012-02-01', 20),
   (2, 'def', '2012-01-01', 10),
   (2, 'def', '2012-02-01', 5),
   (1, 'abc', '2012-01-01', 15),
   (3, 'ghi', '2012-03-01', 15);

I am using an actual date in the base table, since I am assuming are just hiding that in a effort to simplify your question. But with just month names, there would be nothing to ORDER BY.

Query

SELECT * FROM crosstab(
     $$SELECT id, extra_info, to_char(month, 'mon'), sum(value) AS value
       FROM   tbl
       GROUP  BY 1,2,month
       ORDER  BY 1,2,month$$

    ,$$VALUES
      ('jan'::text), ('feb'), ('mar'), ('apr'), ('may'), ('jun')
    , ('jul'),       ('aug'), ('sep'), ('oct'), ('nov'), ('dec')$$
   )
AS ct (id  int, extra text
   , jan int, feb int, mar int, apr int, may int, jun int
   , jul int, aug int, sep int, oct int, nov int, dec int);

Result:

 id | extra | jan | feb | mar | apr | may | jun | jul | aug | sep | oct | nov | dec
----+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
  1 | abc   |  25 |  20 |     |     |     |     |     |     |     |     |     |
  2 | def   |  10 |   5 |     |     |     |     |     |     |     |     |     |
  3 | ghi   |     |     |  15 |     |     |     |     |     |     |     |     |

Installing the tablefunc module requires some overhead and some learning, but the resulting queries are much faster and shorter and more versatile.

Sunday, November 20, 2022
 
al404it
 
3

I agree that using rsync would be a better solution, but there is an easy way to skip a directory in bash:

for file in "$HOME/"*
do
    [[ $file = $HOME/Backup ]] && continue
    cp -r "$file" "$HOME/Backup/"
done
Friday, October 21, 2022
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :