PHP Script To Monitor FTP Directory Changes

I recently had the need to be able to monitor a directory on an FTP server for changes and to then be notified of those changes. I’ve been working a lot in PHP lately so I decided to use that to implement my script.

FTP Access

FTP (file transfer protocol) access in PHP is a breeze. It’s as simple as this:

Easy, right? Of course I left out a few pieces of the puzzle, but I just wanted to show how easy it is to actually get to an FTP server, login and get a directory listing.

Comparisons

After retrieving the listing which is an array I remove out the “.” and “..” listings as they are special system directories meaning “the current directory” and “the parent directory” respectively.

Now the header for this section is Comparisons, so what exactly are we comparing to? Each time this script runs we store a serialized version of this array out to file. I read the file into an array and compare it with the one we just created from the FTP directory.

Now that we have an array of files (and directories) that we just pulled from the FTP server and an array that we saved from the last time we ran we can just compare the two.

Finally, I write the contents of the new array back out to the cache file so we can use it to compare on the next run.

Summary and Download

So that’s it! Pretty simple. On my server I pre-pended the following to the file:
#!/usr/bin/env php
and changed the file mode so that it could be executed. I then setup a cron job to run at 8 o’clock every morning like this:

> crontab -e

# This is added to crontab
0 8 * * * ~/ftpMonitor.php

Here is the complete file in a zip file. Open the file in any text editor and you will see a configuration section at the top where you can set your FTP host information and email information.

ftpMonitor.zip

I hope you have enjoyed this short tutorial and may possibly find it useful. If you have any comments or suggestions for improvement please feel free to leave those in the comments section below. Thanks for stopping by!

  • Matt

    This is a great script, something I have been looking for to monitor changes on my FTP server, however I am having problems running it. I get the error “Warning: Invalid argument supplied for foreach()” on line 34. I am guessing it’s a configuration issue, but I am unable to figure it out. Bear in mind I am not a programmer I just tinker with these things. By the way thanks again for sharing this script, I hope you can help.

  • http://testing jeff

    Hi, I am having issues implementing your script. I’m running it from a Windows PHP installation remotely to a Yahoo hosted website of mine. When I echo $variables to see what we’ve got so far, I’m not getting any file directory listing using echo $files;
    Any ideas?
    Thank you
    Jeff

  • http://www.franzone.com Jonathan Franzone

    Have you tried using var_dump($files)? And I’m assuming that you were able to successfully login via the PHP code? Also, have you checked that you can use a standard FTP client (FireFTP) to login and list files on your server?

  • http://testing jeff

    Thanks Jonathan, var_dump outputs this “bool(false)”. But using ftp client or even ftp from dos prompt I can list files/dirs. Could it be the installation of the Servers PHP? do I need the ftp -enabled option set? (which I can’t since its yahoo hosted) What I really need is some code to watch for when a file gets “touched”.

  • http://www.franzone.com Jonathan Franzone

    No, it shouldn’t matter what is installed on the server as far as PHP goes. You should definitely make sure your local PHP installation has the FTP modules enabled though.

  • http://testing jeff

    OK, just found out my web hosting company does NOT support FTP functions via PHP. I’ll have to figure something else out. Thanks again.

  • http://nodaddy.com NOdaddy.com

    sweeet

    could you next provide a PHP ‘fancy’ directory listing app.. that instead of creating for local directory logs in with FTP credentials?

    akin to the evoluted script, but better
    http://www.evoluted.net/community/code/directorylisting.php

  • http://nodaddy.com NOdaddy.com

    I wish this form retained my formatting :|

  • softy

    I have tested and I get following problems: Can you throw some light as why these are showing up?

    Warning: Invalid argument supplied for foreach() in /home/test/public_html/testmonitor/ftpnitor.php on line 34

    Warning: sort() expects parameter 1 to be array, boolean given in /home/test/public_html/testmonitor/ftpnitor.php on line 59

    Warning: array_diff() [function.array-diff]: Argument #2 is not an array in /home/test/public_html/testmonitor/ftpnitor.php on line 63

    Warning: fopen(ftp_cache) [function.fopen]: failed to open stream: Is a directory in /home/test/public_html/testmonitor/ftpnitor.php on line 87

    Warning: fwrite(): supplied argument is not a valid stream resource in /home/test/public_html/testmonitor/ftpnitor.php on line 88

    Warning: fflush(): supplied argument is not a valid stream resource in /home/test/public_html/testmonitor/ftpnitor.php on line 89

    Warning: fclose(): supplied argument is not a valid stream resource in /home/test/public_html/testmonitor/ftpnitor.php on line 90

  • Cori

    Any way to make this go more than one directory deep?

  • Cori

    For example, would there be a way to use ftp_rawlist and extract just the relevant info? I don’t think ftp_nlist allows recursive, which is just stupid on PHP’s part.

  • Jonathan

    what and where do you prepend:

    #!/usr/bin/env php

    any help will be highly appreciated! Thank You!

  • http://www.franzone.com Jonathan Franzone

    First line in the file. This basically tells the shell how to execute the file. So when you execute it the shell will run the “env” command with an argument of “php” which should return the path to your php executable. It will then use that to parse/run the rest of the file. You’ll also need to make the file executable like:

    > chmod 755 file.php

  • Jonathan

    Thank You for the quick reply! Great script! Cheers!

  • http://andrewchamp.com Andrew Champ

    I love it! But I can’t get it to work outside of the root. I have it in a cron jobs folder and point it in the right direction, but it won’t work unless $remote_dir says ‘/’. No errors, but the ftp_cache file says something like a{};

    Thank you.

  • Julian

    Hi Jonathan!
    Thank you, great script! In my ftp I have many subfolders. The hosting company allow me to run only 3 cron jobs, so I can’t execute every ftpMonitor.php uploaded in all folders.
    What should I modify to can it watch at once all files in all folders?

  • TerryH

    Hi Jonathan

    Very useful script. However, I have the same problem as Andrew Champ above. The script only works for me when $remote_dir = ‘/';

    I am running windows2003 server with IIS6. The directory structure is C:/domains/domain.tld/wwwroot which is where I have ftpmonitor.php installed.
    FTP client shows directory structure as /ftploginname/wwwroot

    What should the value of $remote_dir be in this case.

    FYI – I use <?php header("Refresh: 60;"); to reload the page every 60 secs.

    Cheers

    Terry

  • TerryH

    Hi again

    I found a solution by placing the file in a sub-directory and setting the value as: $remote_dir = ‘./wwwroot’;

    Maybe not ideal, but it works.

    It works when a new file is uploaded to the route directory, but the script doesn’t appear to spot when a file is deleted or amended. Maybe this is something I need to implement myself.

    Regards

    Terry

  • TerryH

    Jonathan
    Your file will be especially useful to me. My server was recently hacked, and malicious files where placed in the route directory of my site, and malicious code was added to existing files. In the future, this will allow me to detect any files that where not uploaded by me very quickly – the email notification is great. I leave the file loaded in an instance of IE on my server desktop. The file auto-refreshes every 60 seconds.

    If it’s any use to anyone, I have adjusted the file to include the following:
    Automatic refresh
    A manual refresh button
    A list of files in my route directory showing when they were last modified
    I’ve excluded directories

    Please excuse my coding – I’m not familiar with PHP.

    <?
    /**
    * File : ftpMonitor.php
    * Monitors a remote directory via FTP and emails a list of changes if any are
    * found.
    *
    * @version June 4, 2008
    * @author Jonathan Franzone
    */

    // Configuration ///////////////////////////////////////////////////////////////
    $host = ‘localhost';
    $port = 21;
    $user = ”;
    $pass = ”;
    $remote_dir = ”;
    $cache_file = ‘ftp_cache';
    $email_notify = ”;
    $email_from = ”;

    // Main Run Program ////////////////////////////////////////////////////////////

    // Connect to FTP Host
    $conn = ftp_connect($host, $port) or die(“Could not connect to {$host}\n”);

    // Login
    if(ftp_login($conn, $user, $pass)) {

    // Retrieve File List
    $files = ftp_nlist($conn, $remote_dir);

    // Filter out . and .. listings
    $ftpFiles = array();
    ?>Date the files were modified:
    File name:Date } Time
    1)
    $extension = end($parts);
    //Adjust path according to where your FTPmonitor file is placed
    $path = “../”.$thisFile;
    //Exclude directories
    if(array_key_exists(1, $parts)){
    ?> 0)
    {
    // Email the changes
    $msg = “ftpMonitor Changes” .
    “ftpMonitor Found Changes:”;
    foreach($diff as $file)
    {
    $msg .= “{$file}”;
    }
    $msg .= “”;
    $msg .= ‘Script by Jonathan Franzone‘;
    $msg .= “”;

    $headers = “MIME-Version: 1.0\r\n”;
    $headers .= “Content-type: text/html; charset=iso-8859-1\r\n”;
    $headers .= “To: {$email_notify}\r\n”;
    $headers .= “From: {$email_from}\r\n”;
    $headers .= “X-Mailer: PHP/” . phpversion();

    mail($email_notify, “ftpMonitor Changes Found”, $msg, $headers);
    }

    // Write new file list out to cache
    $handle = fopen($cache_file, “w”);
    fwrite($handle, serialize($ftpFiles));
    fflush($handle);
    fclose($handle);
    }
    else {
    echo “Could not login to {$host}\n”;
    }

    // Close Connection
    ftp_close($conn);

    ?>

  • TerryH

    Sorry – that upload didn’t quite work out. I can’t see a way of uploading the file itself. If anyone wants a copy please contact me at th(AT)f2s(dot)com

  • TerryH

    Now that the bad upload has been removed, I better say what I’ve added to the file.

    Automatic refresh
    Manual refresh button
    Display all files (not directories) in directory being monitored, along with the date/time the file was last modified.

    That’s it

  • Pingback: Studio Hyperset | hyper/boʊl/e | Monitor and Email Server Directory Changes()

  • Craig

    Hi there,
    Sorry to post on an old page…
    Would it be possible to re-upload the ftpMonitor.zip so that I can download and have a play :)
    Thanks

  • http://www.franzone.com Jonathan Franzone

    The file download has been fixed. Enjoy!

  • http://www.bajabaratok.hu vinkozoli

    Hi Jonathan!
    Can you continue to develop for resursive (subdirectories, files) listing?
    Thanks!

  • Evren COMERT

    Hi,

    The script is working great as it offers.

    But me also would like to ask a point like many others.

    Is it possible to modify it to scan sub directories as well? Hope it is something easy not to make you busy, and will share with us. And thank you for sharing this.

     

    Best regards

  • http://millerpages.co.uk Keith Miller

    Thanks so much for a really useful script.  I allow users to upload image files only (less than 2 mb) and this saves me having to keep checking the uploads folder which is behind public_html.  Worked like a charm “straight out of the box”.  Brilliant!

  • Pingback: How to check if directory contents has changed with PHP? - PHP Solutions - Developers Q & A()