Creating an Image Gallery - moving along
(Page 3 of 6 )
our next script (images.php) we'll almost have to write backwards. we already know what we want, so the idea is to write one thing at a time, and then organize that information as we move along. in other words, the last function we write may very well end up being the first function to appear in the script. so long as we focus on one task at a time, the script will come together wonderfully.
since we're only working with the file system as our database, our first task should be to traverse the directory specified within our configuration file and gather its contents into an associative array. our first line of code will include the config file so we have access to the path (along with the other values we'll need later).
<?php require_once('config.php'); ?> |
now we need our tree. rather than writing one chunk of code at a time and then attempting to explain it, i'm just going to heavily comment my code and use simple variable names so you can follow along with what each step is doing. most of this should be pretty straight forward and obvious, but i'll attempt to explain things in more depth where i feel is necessary. here is our directory function:
<?php // get directory tree function directory($dir) { $mydir = opendir($dir); while(false !== ($file = readdir($mydir))) { if($file != "." && $file != "..") { // we use $dir here rather than $mydir, because opendir() only returns a // resource, we need the actual path in order to get the expected result. // if our file is a directory, we need to search that directory if(is_dir($dir.$file)) { // beware of safe mode when using chdir // it may generate warnings so we'll supress them with @ @chdir('.'); // by having the function call itself, it is now recursive // all sub directories will have an index of their own name $tree[$file] = directory($dir.$file.'/'); @chdir('..'); } else { $size = getimagesize($dir.$file); // if our file matches the valid MIME types, we add it to // the array with a numeric index if(in_array($size['mime'], unserialize(TYPE))) $tree[] = $file; } } } closedir($mydir); return $tree; } $tree = directory(PATH); ?> |
we now have an array of our entire directory structure. we can easily tell the difference between which elements are directories and which elements are images by looking at the index. the next step is to figure out how to determine what is being requested. we can accomplish this by passing a variable along in the uri which we'll call $nav, the value of $nav will change dynamically depending on what the user has clicked.
rather than traversing our entire array with every request, it would be much nicer if we could immediately jump directly to what the user wants. in this case, the value of $nav should be a list of elements which point to a particular location of our array. for example, let's assume we have the following tree structure:
Array ( [category one] => Array ( [0] => img1.jpg [1] => img2.jpg [2] => img3.jpg ) [category two] => Array ( [sub category] => Array ( [0] => img4.jpg [1] => img5.jpg ) ) ) |
if we want to display img4.jpg, then the value of $nav should be ['category two']['sub category'][0]. if we want to display the gallery for the first category, then the value of $nav should be ['category one']. this way, we can take the value of $nav and call it within our $tree in order to get exactly what we want. when we create the hyperlinks for navigating the gallery later, they will give a new value to nav with each click that will look something like this: images.php?nav=>category+two>sub+category>0 - this way we'll always know exactly where we are. one thing to note is that i tend to use the ternary "?" operator a lot, if you're unfamiliar with this operator here is a quick visual explanation:
<?php $value = (some expression) ? 'value if true' : 'value if false'; ?> |
back to the project:
<?php // let's grab our navigation $nav = isset($_REQUEST['nav']) ? urldecode($_REQUEST['nav']) : null; if(isset($nav)) { // we need to get rid of our location separators $crunch = explode('>', trim($nav)); foreach($crunch as $value) { // IMPORTANT! validate for teh h4x0rz if(!ctype_alnum(str_replace(' ', '', $value))) $invalid = true; } if($invalid) $nav = null; } ?> |
since we're working with $_REQUEST values, it is important to make sure that the data we're receiving is the actual type of data that we expect. you can't guarantee that a user won't try and modify the path themself, but you can guarantee that only alpha-numeric characters will be accepted; we'll validate further as we go along. now that we have our path, we need to append it to our $tree array and direct our script where to go next.
<?php // navigating the appropriate categories function categories($navtree) { // if we have a path to work with.. if(isset($GLOBALS['nav'])) { // we need to attach the path to our array // start counting from 1, as 0 will be empty for($i=1; $i<count($GLOBALS['crunch']); $i++) $navtree = &$navtree[$GLOBALS['crunch'][$i]];
// we'll study this function in a minute display_body($navtree); } else display_body($navtree); } categories($tree); ?> |
we know that if $nav is set, then so is $crunch, so we'll save ourselves the trouble of repeating code here by calling it globally. notice how we attach the path to our array by using a reference, trying to accomplish this with $array.$elements will not work as expected. i have to give a big thanks to zombie for helping me with that one.
the next thing to consider is our display_body function, which will be the longest of all of our functions. this function needs to determin if we're requesting a single image, a listing of categories, or a gallery of thumbnails along with which page of the gallery we're looking at. but before we get into that, we need to setup the appropriate variables for our multiple pages of galleries. we can accomplish this by adding an additional variable to our uri path called $pg in much the same way we did with $nav. the value of $pg will either be a number, indicating what page we're on, or it will have the value "all" which will suggest that the user wants to view all of the images in a given gallery at the same time.
<?php // decide what page of a paticular gallery we're viewing $pg = isset($_REQUEST['pg']) && (ctype_digit($_REQUEST['pg']) || $_REQUEST['pg'] == 'all') ? $_REQUEST['pg'] : 0; $viewall = is_numeric($pg) ? null : true; $pg = intval($pg) <= 0 ? 1 : $pg; ?> |
here we made sure that $pg will always have a numeric value, in order to avoid mathmatic problems in the following code. if the value of page is "all" we'll just define the variable $viewall as true and then convert $pg back to a number.
<?php // images to display per page $section = ROWS * COLS; // last possible ending point for images, depending which page we're on $end = $section * $pg; // starting point for images, depending which page we're on $start = $end - $section; // previous page $prev = $pg - 1; // next page $next = $pg + 1; ?> |
now we can draw previous and next links. the variable passed, $total, will be the amount of images that we're working with.
<?php // navigation (yes, cleverly named after the movie) function navigator($total) { // make a previous link $p = '<a href="'.$_SERVER['PHP_SELF'] .'?nav='.rawurlencode($GLOBALS['nav']) .'&pg='.$GLOBALS['prev'].'"><<</a>'; // make a next link $n = '<a href="'.$_SERVER['PHP_SELF'] .'?nav='.rawurlencode($GLOBALS['nav']) .'&pg='.$GLOBALS['next'].'">>></a>'; // make a view all link $a = '<a href="'.$_SERVER['PHP_SELF'] .'?nav='.rawurlencode($GLOBALS['nav']) .'&pg=all">view all</a>'; // the navigation print('<tr><td align="center" colspan="' . COLS . '">'); if(($GLOBALS['pg'] == 1 && $GLOBALS['end'] >= $total) || isset($GLOBALS['viewall'])) print('<< : view all : >>'); elseif($GLOBALS['pg'] == 1) print('<< : '.$a.' : '.$n); elseif($GLOBALS['end'] < $total) print($p.' : '.$a.' : '.$n); else print($p.' : '.$a.' : >>'); print('</td></tr>'); } ?> |
the navigator function just does some math to determine which links are appropriate to display depending on how many pages of images we have. we put this in a function in the case that we want our navigational links to appear in more than one place, such as above & below the galleries. now onto the juicy stuff:
<?php // display category links and images function display_body($navtree) { if(isset($GLOBALS['nav'])) { // we'll need the path as a directory for displaying images for($i=1; $i<count($GLOBALS['crunch']); $i++) $uri .= !is_numeric($GLOBALS['crunch'][$i]) ? $GLOBALS['crunch'][$i].'/' : ''; }
// if we have an array, then we are either requesting // category links or a gallery of images if(is_array($navtree)) { // here we combine all the keys of our array into a string // if it does not have a numeric value then we must have // categories to display if(!is_numeric(implode('', array_keys($navtree)))) { print('<tr><td>'); foreach($navtree as $key=>$value) { if(!is_int($key)) { // if our category value is an array, then // we create a link back to the images script and // give $nav the appropriate value if(is_array($value)) print('<a href="'.$_SERVER['PHP_SELF'] .'?nav='.rawurlencode($GLOBALS['nav'] .'>'.$key).'">'.$key.'</a><br />'); else // empty category, no need to link it print($key.'<br />'); } } print('</td></tr>'); } else { // if we're not working with categories, then we're // working with images, so let's decide how many // rows to display (depending if $viewall is true/false) $total = count($navtree); if(isset($GLOBALS['viewall'])) { // math, gotta love it $check = $total / COLS; $rows = is_int($check) ? $check : floor($check) + 1; } else $rows = ROWS;
// display our navigation navigator($total); // start printing the table contents for($i=0; $i<$rows; $i++) { print('<tr>'); for($n=0; $n<COLS; $n++) { $index = $GLOBALS['start']++; // we either want the filename of the image, or // an empty space to fill the rest of the table $image = $index < $total ? $navtree[$index] : '&nbsp;'; print('<td align="center" width="'.THMBWIDTH.'" height="'.THMBHEIGHT.'">'); if($image != '&nbsp;') { // this link provides the exact path to the // image we want to display print('<a href="'.$_SERVER['PHP_SELF'] .'?nav='.rawurlencode($GLOBALS['nav'] .'>'.$index).'">');
// the img tag is making the call to our next // script, imgsrc.php, which is being called // with the full directory path to the image print('<img src="imgsrc.php?src='.PATH.rawurlencode($uri).$image.'" border="0" />'); print('</a>'); } else // if no image, print a space print($image); print('</td>'); } print('</tr>'); } // display another navigation navigator($total); } } elseif(file_exists(PATH.$uri.$navtree)) { // if $navtree is not an array, then we must be calling an // image directly, first we check if the file exists just // to make sure. now we get the appropriate path for // displaying the image $root = explode('/', dirname(PATH.'.')); print('<tr><td><img src="'.end($root).'/'.$uri.$navtree.'"></td></tr>'); } else // no array + no file = invalid print('<tr><td>invalid request</td></tr>'); } ?> |
if you study the code well enough, the whole script should start to make sense, especially where we create our links and assign $nav the appropriate value to follow along with where we are surfing. as you've also probably noticed throughout this section, we always encode the data before being sent, and decode the data when it is being requested. we have used a lot of PHP's built in functions, if you want to know more about any of them just head on over to www.php.net/the_function_name_you_want_to_know_about
the last script we need to work on is imgsrc.php, which will draw our thumbnails based on the path that it is sent via the variable $src as we just saw in our display_body function. before we move onto this script, i'd like to review what we've covered so far and show you how the images.php code should be organized as a whole.
Next: review and organize >>
More Miscellaneous Articles
More By notepad
|
| · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | · | | | | |
|