PHP Photo Gallery

This is a personal project for my portfolio and also to show family pictures to my friends and family online. I do have an account with flickr and picasa but I just love having my own personal gallery. I'm going to add functionality in the future to allow the gallery to read feeds from those photo services.

You just basically create a folder and place all your pictures in there and then point the Photo Gallery config.php file to read this folder and voila! Instant photo gallery :) a drop down navigation is provided, similar to nautilus and Windows 7 explorer style. Images are preloaded using javascript and thumbnails are generated on the fly. It uses the built in GD library for generating thumbnails and can be configured to use a different script with a different library if needed.

The gallery uses PHP SPL Directory Iterators and it also uses lightview for displaying images and a slideshow. It uses ajax when switching between pages.

Click here to see a demo of my PHP Photo Gallery.

PHP Photo Gallery

config.php


<?php
define ('APP_PATH', realpath(dirname(__FILE__)) );
define ('APP_URL', "http://hbalagtas.0fees.net");
define ('THUMBNAIL_SCRIPT', "thumbnailgd.php");
define ('VIEW_SCRIPT', "view.php");
define ('THUMBNAIL_PATH', APP_PATH.'/thumbnails/');
#define ('THUMBNAIL_PATH', '/tmp/');
define ('THUMBNAIL_WIDTH', 150);
define ('THUMBNAIL_HEIGHT', 100);
define ('LOCAL_GALLERY_PATH', APP_PATH.'/');
define ('GALLERY_ROOT', 'PhotoGallery');



define('IMAGE_PER_PAGE', 8);
define('ALBUM_TITLE', "Bing's Photo Gallery");


myfunctions.php


<?php
/**
 * Functions that I use in the gallery
 *
 * @author Herbert Balagtas
 *
 */

include_once ('config.php');

/**
 * Get the files and directories from a given path, returns them as
 * an array, ust list($dir, $files) = getDirElements($path)
 * @param string $localpath
 * @return array $dir, $files
 */
function getDirElements($localpath){
	try{
		$localpath = stripslashes($localpath);
		$dirIterator = new DirectoryIterator("$localpath");
	} catch (UnexpectedValueException $u){
		header("Location: .");
	}
	foreach ($dirIterator as $node){
		if ( $node->isFile() && isImage($node->getFilename()) ) {
			$files[] = $node->getFilename();
		}
		if ( $node->isDir() && !$node->isDot()) {
			$dirs[] = $node->getFilename();
		}
	}
	@sort($dirs);
	@sort($files);
	return array($dirs, $files);
}

/**
 * checks if a filename has an image extensions
 *
 * @param string $file
 * @return boolean
 */
function isImage($file){
	if (eregi ("(.)+\\.(jp(e){0,1}g$|png$|jpeg$|gif$)", $file))
	return true;
	else
	return false;
}

/**
 * Drop down navigation from path
 *
 * @param string $dir
 * @return string
 */
function jumpnav($dir){
	$localpath = LOCAL_GALLERY_PATH;
	$patharray = explode('/',$dir);
	$jumpdir = array();
	foreach ($patharray as $pathmember){
		$localpath .= $pathmember . '/';
		$jumpdir[] = $pathmember;
		$prevpath = implode('/',$jumpdir);
		$dh = new DirectoryIterator($localpath);
		$jumpurl = '';
		$options = '';
		$dir_array = array();
		foreach($dh as $item){
			if ($item->isDir() && !$item->isDot() ){
				$dir_array[] = $item->getFilename();
			}
		}
		sort($dir_array);
		foreach($dir_array as $item){
			#if ($item->isDir() && !$item->isDot() ){
			#$jumpurl = implode('/',$jumpdir) . '/' . $item->getFilename();
			$jumpurl = implode('/',$jumpdir) . '/' . $item;
			$selected = '';
			$indicator = '';
			if (preg_match('/'.escape_string_for_regex($jumpurl).'$/', $dir) ||
			preg_match('/'.escape_string_for_regex($jumpurl).'\//', $dir)
			)
			{
				$selected = 'selected="selected"';
				$indicator = '&gt;&gt;';
			}
			$jumpurl = encode_string($jumpurl);
			#$options .= '<option value="?path='.$jumpurl.'" '. $selected .'>'.$indicator.$item->getFilename().'</option>' . "\n";
			$options .= '<option value="?path='.$jumpurl.'" '. $selected .'>'.$indicator.$item.'</option>' . "\n";
			#}
		}

		if ( $options !== "" ) {
			$dropdata .= '<form name="form'.$ctr.'" style="margin:0;padding:0;display:inline;">' . "\n";
			$dropdata .= '<select name="fieldname" id="subpath" onChange="openDir( this.form )">' . "\n";
			if ( $ctr == $subdircnt ) {
				$options = '<option value="?path='.urlencode($prevpath).'" label="" width="50">Main Folder</option>' . $options;
			}
			$dropdata .= $options;
			$dropdata .= '</select>' . "\n";
			$dropdata .= '</form>' . "\n";
		} // end if $options

	}

	$dropdata = '<div class="gallery_nav">Gallery Navigation: '. $dropdata . '</div>';
	return $dropdata;
}

/**
 * Escapes string to be used in a regular expression function
 * @param string $str
 * @return string
 */
function escape_string_for_regex($str)
{
	//All regex special chars (according to arkani at iol dot pt below):
	$patterns = array('/\//', '/\^/', '/\./', '/\$/', '/\|/', '/\(/', '/\)/', '/\[/', '/\]/', '/\*/', '/\+/', '/\?/', '/\{/', '/\}/', '/\,/', '/\ /');
	$replace = array('\/', '\^', '\.', '\$', '\|', '\(', '\)', '\[', '\]', '\*', '\+', '\?', '\{', '\}', '\,', '\\ ');

	return preg_replace($patterns,$replace, $str);
}

/**
 * quickly resizes and image
 */

// FROM PHP.NET
function fastimagecopyresampled (&$dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h, $quality = 2) {
	// Plug-and-Play fastimagecopyresampled function replaces much slower imagecopyresampled.
	// Just include this function and change all "imagecopyresampled" references to "fastimagecopyresampled".
	// Typically from 30 to 60 times faster when reducing high resolution images down to thumbnail size using the default quality setting.
	// Author: Tim Eckel - Date: 09/07/07 - Version: 1.1 - Project: FreeRingers.net - Freely distributable - These comments must remain.
	//
	// Optional "quality" parameter (defaults is 3). Fractional values are allowed, for example 1.5. Must be greater than zero.
	// Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect.
	// 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized.
	// 2 = Up to 95 times faster.  Images appear a little sharp, some prefer this over a quality of 3.
	// 3 = Up to 60 times faster.  Will give high quality smooth results very close to imagecopyresampled, just faster.
	// 4 = Up to 25 times faster.  Almost identical to imagecopyresampled for most images.
	// 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled.

	if (empty($src_image) || empty($dst_image) || $quality <= 0) { return false; }
	if ($quality < 5 && (($dst_w * $quality) < $src_w || ($dst_h * $quality) < $src_h)) {
		$temp = imagecreatetruecolor ($dst_w * $quality + 1, $dst_h * $quality + 1);
		imagecopyresized ($temp, $src_image, 0, 0, $src_x, $src_y, $dst_w * $quality + 1, $dst_h * $quality + 1, $src_w, $src_h);
		imagecopyresampled ($dst_image, $temp, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $dst_w * $quality, $dst_h * $quality);
		imagedestroy ($temp);
	} else imagecopyresampled ($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
	return true;
}

/**
 * Open Image
 * @param unknown_type $file
 * @return unknown_type
 */
function open_image ($file) {
	// Get extension
	$extension = strrchr($file, '.');
	$extension = strtolower($extension);
	switch($extension) {
		case '.jpg':
		case '.jpeg':
			$im = @imagecreatefromjpeg($file);
			break;
		case '.gif':
			$im = @imagecreatefromgif($file);
			break;
		case '.png':
			$im = @imagecreatefrompng($file);
			break;
			// ... etc
		default:
			$im = false;
			break;
	}
	return $im;
}

function implode_dir($array){
	foreach ($array as $a){
		$b .= $a . (substr($a, -1) == '/'?'':'/');
	}
	$b = substr($b, 0, -1);
	return $b;
}

/**
 * Encodes a string to be used as a url parameter
 * @param unknown_type $str
 */
function encode_string($str){
	$str = htmlentities($str, ENT_QUOTES);
	$str = urlencode($str);
	return $str;
}

/**
 * Decode a string passed from a url
 * @param String $str
 */
function decode_string($str){
	$str = html_entity_decode($str, ENT_QUOTES);
	$str = stripslashes(urldecode($str));
	return $str;
}


phpgallery.php


<?php
/**
 * PHP Gallery
 *
 * Uses GD to create a gallery from a directory structure
 *
 * @author Herbert Balagtas
 *
 */

error_reporting(0);
include('../cache_start.php');

#define ( 'APP_PATH', realpath ( dirname ( __FILE__ ) ) );
include_once ('config.php');
include_once ('myfunctions.php');

$time_start = microtime_float ();

// if no path is passed to the script use default path
if (! empty ( $_GET ['path'] ) && isset ( $_GET ['path'] ) && ! preg_match ( '/(^\/|\.\.\/)/', decode_string ( $_GET ['path'] ) )) {
	$path = decode_string ( $_GET ['path'] );
	$localpath = implode_dir ( array (LOCAL_GALLERY_PATH, $path ) );
} else {
	$localpath = implode_dir ( array (LOCAL_GALLERY_PATH, GALLERY_ROOT ) );
	$path = GALLERY_ROOT;
}

list ( $dirs, $files ) = getDirElements ( $localpath );

$navigation = jumpnav ( $path );
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Photo Gallery DEMO - Herbert Balagtas - Online Portfolio</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="gallery.css" />

<script type='text/javascript' src='js/prototype.js'></script>
<script type='text/javascript' src='js/scriptaculous/scriptaculous.js'></script>
<script type='text/javascript' src='js/scriptaculous/effects.js'></script>
<script type='text/javascript' src='lightview/js/lightview.js'></script>
<link rel="stylesheet" type="text/css"
	href="lightview/css/lightview.css" />

<script type="text/javascript">
<!--
   		<?php
					// Load the first page thumbnails
					if (! empty ( $files )) {
						$imgctr = 0;
						echo 'document.observe("dom:loaded", function() {' . "\n";
						echo '$$(\'#page1 img[src="file.png"]\').invoke(\'onmouseover\');' . "\n";
						echo '});' . "\n";
					}
					?>

		// Shows and hides pages
	 	function togglePage(pagectr){
		 	$$('.page').invoke('hide');
		 	//$$(pagectr).invoke('hide');
		 	//$$(pagectr).invoke('show');
		 	//alert('test');
		 	$$(pagectr + ' img[src="file.png"]').invoke('onmouseover');
	 	}

	 	// checks to see if the crawler is running
	 	function protoUpdater(){
		 	var crawler_running = 'no';
	 		new Ajax.PeriodicalUpdater('result', 'crawlerprogress.php',
	 				  {
	 				    method: 'get',
	 				    frequency: 3,
	 				    decay: 2,
	 				    onSuccess: function(transport) {
				   			if ( transport.responseText.match(/Crawler\ is\ idle/) ){
					   			// do nothing
				   			} else {
					   			crawler_running = 'yes';
				   			}

				   			if (crawler_running == 'yes' && transport.responseText.match(/Crawler\ is\ idle/) ){
					   			location.reload(true);
					   			crawler_running = 'no';
				   			}
	 					}
	 				  });
	 	}

	 	// used by the drop down navigation
		function openDir( form ) {
			var newIndex = form.fieldname.selectedIndex;
				cururl = form.fieldname.options[ newIndex ].value;
				window.location.assign( cururl );
		}

		// preloads and shows an image
		function showThumb(ImageID, ThumbUrl){
			imgDir = getImgDirectory(document.getElementById(ImageID).src);
		    if (document.getElementById(ImageID).src == imgDir + 'file.png' ){
		    	var img = document.createElement('img');
			    img.onload = function (evt) {
			        document.getElementById(ImageID).src=this.src;
			    }
			    document.getElementById(ImageID).src= imgDir + 'ajax-loader.gif';
			    img.src = ThumbUrl;
		    }
		    return false;
		}

		/*********************************************
		Name : getImgDirectory
		Parameters : Image source path
		Return : Image source Directory
		Author : Jean-Michel Garnier
		***********************************************/

		function getImgDirectory(source) {
		    return source.substring(0, source.lastIndexOf('/') + 1);
		}
//-->
</script>
</head>
<body>
<h1><?php
echo ALBUM_TITLE;
?></h1>
<a href="../">Home</a>
<hr />
<?php
echo $navigation;
?>

<br />

<?php
$gallery = '';
$totalpage = 0;
$pages = '';
$gallery .= '<div id="gallery">';
if (! empty ( $files )) {
	$imgpp = 10;
	$imgtotal = count ( $files );
	$gallery .= 'Album: ' . $path;
	$gallery .= ' :: Total files: ' . $imgtotal . "<br /> \n";
	$totalpage = ceil ( $imgtotal / IMAGE_PER_PAGE );
	$imgonpage = 0;
	$imgctr = 0;
	$pagectr = 1;
	$ctr = 0;
	$idx = 0;
	foreach ( $files as $file ) {
		if ($imgonpage == 0) {
			$gallery .= '<div class="page" id="page' . $pagectr . '" ' . ($pagectr > 1 ? 'style="display:none;"' : '') . '>' . "\n";
			$gallery .= '<a name="page' . $pagectr . '" />';
		}
		$id = crc32 ( $file );
		$gallery .= '<div class="thumbnail">' . "\n";
		$gallery .= '<a href="' . VIEW_SCRIPT . '?filename=' . encode_string ( $file ) . '&path=' . encode_string ( $path ) . '&height=800" class="lightview" rel="gallery[myset400]" title="' . $file . ' :: :: slideshow: true" id="link' . $id . '">' . "\n";
		$imgsrc = THUMBNAIL_SCRIPT . '?filename=' . encode_string ( $file ) . '&path=' . encode_string ( $path );
		$ctr ++;
		if ($ctr <= IMAGE_PER_PAGE) {
			$gallery .= '<img id="' . $id . '" src="' . $imgsrc . '" border="0" alt="'.$file.'"/>' . "\n";
			#$gallery .= '<img id="'.$id.'" src="file.png" border="0" onmouseover="showThumb(\''.$id.'\',\''.$imgsrc.'\');"/>' . "\n";
		} else {
			$gallery .= '<img id="' . $id . '" src="file.png" border="0" onmouseover="showThumb(\'' . $id . '\',\'' . $imgsrc . '\');"/>' . "\n";
		}

		$gallery .= '</a>' . "\n";
		$gallery .= '<br />' . $file;
		$gallery .= '</div>' . "\n";

		$imgonpage ++;
		$imgctr ++;
		if ($imgonpage >= IMAGE_PER_PAGE || $imgctr >= $imgtotal) {
			$gallery .= '<br clear="all" />';
			$gallery .= 'Page ' . $pagectr;
			$gallery .= '</div>' . "\n";
			$pager [] = '<a href="#page' . ($pagectr) . '" class="pageno" onclick="togglePage(\'#page' . $pagectr . '\');$(\'page' . $pagectr . '\').appear({ duration: 1.5, from: 0, to: 1 });">' . $pagectr . '</a>' . "\n";

			if (count ( $pager ) >= 20 || $imgctr >= $imgtotal) {
				$multipage [] = $pager;
				$pager = array ();
			}

			$pagectr ++;
			$imgonpage = 0;
		}
	}

} else {
	$gallery .= 'No files found in: ' . $path;
	$gallery .= '<br clear="all" />';
	if (! empty ( $dirs )) {
		foreach ( $dirs as $dir ) {
			$pathsrc = urlencode ( $path . '/' . $dir );
			$gallery .= '<a href="?path=' . $pathsrc . '" class="folder">' . $dir . '</a>';
		}
	} else {
		$gallery .= 'No sub album for this album.';
	}
}

$gallery .= '</div>';

if ($totalpage > 1) {
	#$pages .= '<br />';
	#$pages .= '<br clear="all" />' . "\n";
	#$pages .= '<hr />' . "\n";
	$pages .= '<div id="pager">' . "\n";
	foreach ( $multipage as $mp ) {
		$pages .= implode ( '', $mp );
		$pages .= '<br clear="all"/>';
	}
	$pages .= '</div>';
}
echo $pages;
echo '<br clear="all"/>';
echo $gallery;
echo '<br clear="all"/>';
echo '<br clear="all"/>';
echo $pages;
echo '<br clear="all"/>';
echo $navigation;
$time_end = microtime_float ();
$time = $time_end - $time_start;

echo '<br clear="all" />';
echo '<hr />';
echo "Script Execution time $time seconds\n";
?>
</body>
</html>

<?php include('../cache_end.php'); ?>

Personal Links

Favorite Links