Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/recipe-fiddle.git
<?php
/**
 * This file handles dynamically caching and resizing thumbnails. This
 * file is called when the web server encouters a 404 error on a thumbnail
 * image. The image is created and returned, which will prevent subsequent
 * 404s on that image. The file names must be formatted as:
 *
 * <pre>
 *   filename-#x.ext
 *   filename-x#.ext
 *   filename-#x#.ext
 * </pre>
 *
 * The examples specify: (1) width; (2) height; (3) width and height. The
 * aspect ratio is maintained.
 *
 * For security purposes, only a specific set of dimensions are allowed.
 */

include 'WideImage/WideImage.php';

// Limit the number of possible cached images (i.e., do not allow arbitrary
// dimensions to create cached files).
$ACCEPT_DIMENSIONS = array( 150, 512, 1024, 2048, 3192, 4096 );

// The "images" suffix comes from the URL.
$IMAGES_DIRECTORY = "/home/www-data";

$DIVIDER = "x";

/**
 * Given an integer value, this will find its nearest value within an
 * array of values.
 *
 * @param $needle The value whose nearest value is sought.
 * @param $haystack The valid values.
 *
 * @return The value in $haystack that is nearest to $needle.
 * @see http://stackoverflow.com/a/5464961/59087
 */
function nearest( $needle, $haystack ) {
  $near = null;

  foreach( $haystack as $hay ) {
    if( $near == null || abs( $needle - $near ) > abs( $hay - $needle ) ) {
      $near = $hay;
    }
  }

  return $near;
}

/**
 * Returns the minimum dimension of $dim_a or $dim_b such that the resulting
 * value is nearest to the acceptable array of dimensions.
 *
 * @param $dim_a The first dimension for comparison.
 * @param $dim_b The second dimension for comparison.
 *
 * @return The smaller of dim_a and dim_b evaluated to the nearest value
 * in the ACCEPT_DIMENSIONS array.
 */
function minimum( $dim_a, $dim_b ) {
  global $ACCEPT_DIMENSIONS;
  return nearest( min( $dim_a, $dim_b ), $ACCEPT_DIMENSIONS );
}

// Separate the query string into its path parts (filename, directory, etc.).
$path = pathinfo( $_SERVER["QUERY_STRING"] );

// Get the filename without the extension.
$filename = $path["filename"];

// Get the path to the image (appended to $IMAGES_DIRECTORY).
$dirname = $path["dirname"];

// Get the image extension, which will be used when saving the scaled file.
$extension = $path["extension"];

// Locate the position of what should be the last hyphen in the filename.
// This doesn't use strrpos because "filename-200x----.png" would yield
// incorrect results.
$index = strpos( $filename, "-" );

// When one hyphen is present, it indicates dimensions might be specified...
if( $index > 1 && substr_count( $filename, "-", $index ) === 1  ) {
  // Grab the filename without the specified dimensions.
  $original_filename = substr( $filename, 0, $index );
  $dimensions = substr( $filename, $index + 1 );

  // If the x isn't present, ensure it will be parsed and used in the
  // output filename.
  if( strpos( $dimensions, $DIVIDER ) === false ) {
    $dimensions .= $DIVIDER;
    $filename .= $DIVIDER;
  }

  // Split a maximum of 2 values: the width and height. The "x" is always
  // present.
  list( $width, $height ) = explode( $DIVIDER, $dimensions, 2 );

  // Ensure no funny stuff has happened with the numbers.
  $width = intval( $width );
  $height = intval( $height );

  // Determine the full path to the original file.
  $source = "$IMAGES_DIRECTORY$dirname/$original_filename.$extension";

  // Open the original image to scale to the given dimensions.
  $image = WideImage::load( $source );

  // Scaling cannot exceed original dimensions; the resize API requires
  // "null" values to maintain aspect ratios.
  // @see http://wideimage.sourceforge.net/
  $width = empty( $width ) ? null : minimum( $width, $image->getWidth() );
  $height = empty( $height ) ? null : minimum( $height, $image->getHeight() );

  // If the current URI differs from the sanitized URI then update the image.
  $requested = "$IMAGES_DIRECTORY$_SERVER[REQUEST_URI]";
  $sanitized = "$IMAGES_DIRECTORY$dirname/$original_filename-$width$DIVIDER$height.$extension";

  // Set to false when resizing and saving is not required.
  $update = true;

  // If the requested filename and the filename with acceptable dimensions
  // differ, then create the file with acceptable dimensions, provided it
  // does not already exist.
  if( $requested !== $sanitized ) {
    if( file_exists( $sanitized ) ) {
      // Load up the existing image for sending to the browser.
      $image = WideImage::load( $sanitized );

      // No need to resize or save.
      $update = false;
    }
  }

  if( $update ) {
    $image = $image->resize( $width, $height );
  }

  $image->output( $extension );

  if( $update ) {
    // Save the file (so that the web server can find it next time). This
    // could fail silently, so prefer sending the image before saving it.
    $image->saveToFile( $sanitized );
  }
}