Pick random files – or not

Posted: 2010/07/31 in PHP

My wife is a pretty good photographer… and I’m not the only one to say so! You can see her stuff at RedThreadPhoto.com – a site designed by my wife, HTML/CSS’d by a friend, and tweaked to no end by me (I wish I knew more front-end ’cause there are so many tweaks…).

So I wanted a way to load the ‘galleries’ (Weddings, Seniors, etc.) in such a way that the site always felt ‘fresh’ to my client (uh, wife).  Photographers want their site to always have new pictures, even though no one really looks at these sites except other photographers… I digress.

I don’t want to hard-code new images every time she has a photo-shoot.  So I wrote a class that scans a directory for a given (or all) extension(s) [using SPL’s DirectoryIterator class per my friend Darcy‘s suggestion].  The list of files is pulled ‘randomly’ so that every time the page is hit/refreshed, a ‘unique’ series of photos is shown.  All I have to do is drop new pictures into the folder – no hard-coding.

To-date, the class presents one of two options: get a list of files that is sorted/filtered (see next paragraph below) or get that list of files with EXIF data, also, that can be used for SEO or other purposes.

The ‘options’ are:

  1. Number of Files Returned = maximum number of files to be returned
  2. Number of Latest Required = how many of the ‘youngest’ files must be used (in our case, so that the latest pictures are always used, then a random assortment of other photos in the folder)
  3. Fully Random = do the new files (if any – see previous option) go to the beginning of the list-returned or do they get mixed into the random mix of files
  4. List of Tags = if using the EXIF-based method, what EXIF tags are returned with the file

What is returned is an array of filenames or, in the EXIF version, an array of filenames that have an array of EXIF tag-data associated with the file.

For example:

$justFiles = array('file3.jpg', 'file12.jpg', 'file5.jpg');
$usingEXIF = array(
     'file3.jpg'=>('title 3', 'subject 3'),
     'file12.jpg'=>('title 12', 'subject 12'),
     'file5.jpg'=>('title 5', 'subject 5')
     );

[[ the following two files are downloadable as the class and the sample.  Feel free to use/alter but know that I take no responsibility for anything going wrong or unexpectedly… this is educational, not professional.  If it was professional, I’d hide it make money – haha! ]]

Here’s the class, all commented-up:

<?php

/*
  * This code is free software; you can use it, redistribute it, and/or modify as you wish.
  * This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY - implied or otherwise.
  * This code is distributed "as is."  All risk and cost are assumed by the user of the code and not the creator thereof.
  * If you want to give attribution to the original creator of the original code, his name is David Malouf and he is, probably available at EmailTheDavid@gmail.com
*/

class chosenFiles {

   private $locationOfFiles;
   private $goodExtensions;        // array
   private $arrayOfFiles;

   function __construct($directoryLocation='.', $onlyTheseExtensions=FALSE) {
      $this->locationOfFiles = $directoryLocation;
      $this->goodExtensions = $onlyTheseExtensions;
      $this->LoadListIntoArray();
   }

   /**
   * getListOfFiles returns a given number of files, pulling the Latest files (if desired), and fully re-randomized (if desired)
   * @param int $NumberOfFilesReturned Number of filenames to be returned
   * @param int $NumberOfLatestRequired Number of 'latest/youngest' files to return (if that's important)
   * @param bool $FullyRandom If TRUE, will randomize final, returned array - otherwise will leave 'newest' at the beginning of the returned array
   * @return array Returns an array that is the list of files requested
   */
  function getListOfFiles($NumberOfFilesReturned, $NumberOfLatestRequired=0, $FullyRandom=FALSE) {
      // Sanity check
      if (empty($this->arrayOfFiles)) { return FALSE; }

      // Sort by date
      arsort($this->arrayOfFiles);     // puts the newest files at the beginning

      // PULL-OUT $NumberOfLastest files that are the youngest/newest and put into $chosenOnes[]
      $i = 0;
      foreach ($this->arrayOfFiles as $file=>$value) {
         if ($i == $NumberOfLatestRequired) { break; }
         $chosenOnes[] = $file;
         unset ($this->arrayOfFiles[$file]);
         $i++;
         $NumberOfFilesReturned--;
      }

      // if there are files left, pull a random $NumberOfFiles files from remaining list and add to $chosednOnes[]
       if (count($this->arrayOfFiles)) {
          // make sure there are as many files in the arrayOfFiles as are being requested (or errors get thrown)
             $NumberOfFilesReturned = $NumberOfFilesReturned > count($this->arrayOfFiles) ? count($this->arrayOfFiles) : $NumberOfFilesReturned;

             if ($NumberOfFilesReturned == 0) { return $chosenOnes; }    // no more, so return what exists
             $randomKeys = array_rand($this->arrayOfFiles, $NumberOfFilesReturned);
             if ($NumberOfFilesReturned > 1) {
                  foreach ($randomKeys as $file=>$value) {
                      $chosenOnes[] = $value;
                  }
             } else {
                $chosenOnes[] = $randomKeys;
             }
       }

       // if user requests, randomize $chosenOnes[]
       if ($FullyRandom) {
          shuffle($chosenOnes);
       }

       return $chosenOnes;
   }

    public function getListWithExif ($NumberOfFilesReturned, $NumberOfLatestRequired=0, $FullyRandom=FALSE, $ListOfTags=array('Title', 'Subject')) {
       $ListOfFiles = $this->getListOfFiles($NumberOfFilesReturned, $NumberOfLatestRequired, $FullyRandom);
       if ($ListOfFiles == FALSE) { return FALSE; }
       foreach ($ListOfFiles as $file) {
          $fullFileName = $this->locationOfFiles . "/" . $file;
          $exifInfo = exif_read_data($fullFileName);
          foreach ($ListOfTags as $Tag) {
             $relevantData[$Tag] = htmlentities(filter_var($exifInfo[$Tag]));
          }
       $returnedArray [$file] = $relevantData;
       }
       return $returnedArray;
    }

    /**
    * LoadListIntoArray puts Files and Timestamp into $this->arrayOfFiles where directory = $this->locationOfFiles
    */
    private function LoadListIntoArray() {
       // get list of files (no directories) from $this->locationOfFiles
       // and put into $this->arrayOfFiles[]
       $desiredDirectory = $this->locationOfFiles;
       $dirListing = new DirectoryIterator($desiredDirectory);
       foreach ($dirListing as $Filename) {
          if ($Filename->isDot() || $Filename->isDir()) { continue; }
          $key = $Filename->getFilename();

          // if the file is NOT in the Good Extensions list, move on
          if ($this->goodExtensions) {
                $extension = substr($key,strrpos($key,'.'));
                if (!in_array($extension, $this->goodExtensions)) { continue; }
          }

          $value = date('Ymd__H_i_s', $Filename->getMTime());
          $this->arrayOfFiles[$key] = $value;
       }
    }

}
?>

Here’s a sample use scenario:

<?php

// Variables that indication Locations in the file structure
include ('chosenFiles.php');
$directoryLocation = './SomeSampleFiles/';

// Setting up the variables that matter.
 date_default_timezone_set('America/Los_Angeles');
   // for Windows to not freak out (timestamps & Windows often need this)
 $onlyTheseExtensions = FALSE;
   // FALSE makes EVERY file chosen (even files without extensions)
   // or array('.jpg', '.jpeg') or array('.jpg', '.png') or ...
 $NumberOfFilesReturned = 31;
   // how many file names do you want?
   //  (if this is more than the # of files in $directoryLocation,
   //  it's okay!  A sanity check is included)
 $NumberOfLatestRequired = 2;
   // how many of the Newest files in the folder do you want to guarantee
   //  to be in the list (can be 0)
 $FullyRandom = FALSE;
   // fully random list or (if FALSE), Newest files at the top, and
   //  then random after that
 $ListOfTags = array('Title', 'Subject');
   // What Tags from the IMAGE metadata are returned (to-date, only works with
   //   images)
   //  $ListOfTags = FALSE;  is also an option

// the Action happens here
$SetOfFiles = new chosenFiles($directoryLocation, $onlyTheseExtensions);
$arrayOfFiles = $SetOfFiles->getListWithExif($NumberOfFilesReturned, $NumberOfLatestRequired, $FullyRandom, $ListOfTags);
 // the array of files with Title & Subject (since this is the EXIF version)

// used in HTML below
$firstTag = $ListOfTags[0];        // by doing it this way, instead of actually STATING the tags, means we can use
$secondTag = $ListOfTags[1];    //  whatever Tags are relevant rather than hardcoding 'Title' and
                                             //  'Subject' (should you ever choose different Tags in the future)

// Begin very silly un-ordered list of random images with titles taken from
//   the EXIF data and the 'alt' tag using the 'subject' from EXIF

echo '<ul>';

foreach ($arrayOfFiles as $filename => $metadata) {
?>

          <li>
             <img src="<?php echo $directoryLocation . $filename; ?>" /> alt="<?php echo $metadata[$secondTag]; ?>"
                <br/>
             <?php echo $metadata[$firstTag] , "\n"; ?>
         <li/>
            <br/> <br/> <br/>

<?php
}       // end foreach of $arrayOfFiles

echo '</ul>';
?>
/*
* This code is free software; you can use it, redistribute it, and/or modify as you wish.
* This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY – implied or otherwise.
* This code is distributed “as is.”  All risk and cost are assumed by the user of the code and not the creator thereof.
* If you want to give attribution to the original creator of the original code, his name is David Malouf and he is, probably available at EmailTheDavid@gmail.com
*/
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s