An Ounce of Performance and Prevention

The Wicked Good Gallery was a lot of fun to build. It meets the stated requirements, I like the design, and I learned some good stuff.

One issue that always concerns me is performance. The Wicked Good Gallery demo has seven images, and on each page load, it reads the image directories, tests for a thumbnail and detailed image, and if they don’t exist, it creates them from the first .jpg in the directory it finds. The issue is that in most cases, when images are posted, they won’t change. This isn’t a versioned gallery, only the last image uploaded will be offered to site visitors.

For that reason, you can assume that the only time you need to test for new files is when the list of directories changes. You can further extend it to only rebuild new directories, but then you risk the problem of only testing for content in new directories, not refreshing any existing images.

Therefore, a simple caching solution was implemented. When the page loads, the code tests for the presence of a cache file. If the file is present, it compares the directories listed in the cache file to those it reads on the server. It they match, the cached list of artwork is used, otherwise, the cache is rebuilt.

$bUpdateCache=false;
$aDirectoryCache=array();
if (is_file($sDirectoryCache))
        include $sDirectoryCache;
else
        $bUpdateCache=true;
$aDirectories=glob($sArtworkDir.'/*');
if ($aDirectoryCache!=$aDirectories)
{
        foreach ($aDirectories as $k => $v)
        {
                if (is_file($v.'/'.$sArt) && is_file($v.'/'.$sThumb))
                        $aPieces[]=$v;
                else
                        if (make_images($v,$sArt,$iArtHeight,$sThumb,$iThumbHeight))
                                $aPieces[]=$v;
        }
        $bUpdateCache=true;
}
else
        $aDirectories=$aDirectoryCache;
if ($bUpdateCache)
        file_put_contents($sDirectoryCache,'<?php $aDirectoryCache='.var_export($aDirectories,true).';'.PHP_EOL.
                '$aPieces='.var_export($aPieces,true).'; ?>');

There was about a 0.0002 second improvement in performance. Although that sounds trivial, more rigorous testing, with more images, would likely justify the implementation. There is a performance penalty if the cache is removed, but it’s very small, since the cache is built with var_exports.

The prevention added was a base http://www.w3schools.com/TAGS/tag_base.asp tag. This tag ensures all files are drawn from the correct directories, while still allowing relative references in the code.


<base href="http://wirehopper.com/wickedgoodgallery/" />
<link rel="stylesheet" type="text/css" href="css/style.css" />

In this case, the base URL is http://wirehopper.com/wickedgoodgallery/, and the style tag will draw from http://wirehopper.com/wickedgoodgallery/css/style.css, regardless of any slashes submitted in the URI, the page will display properly, and future references, derived by page navigation (clicking on images and links) will be valid.

Quick Average Request Time from Apache access_log

First, you’ll need to add the time required to deliver the request into the access_log. In this case, a custom_log is created. Note the bolded ^%D at the end, which will deliver the time required to serve the request in microseconds ( http://httpd.apache.org/docs/2.2/mod/mod_log_config.html#formats ).

CustomLog logs/custom_log “%h %l %u %t \"%r\” %>s %b \"%{Referer}i\” \"%{User-agent}i\” ^%D

Add this into your Apache .conf file, as appropriate.

Next, restart Apache with apachectl restart. You can use apachectl configtest to check your editing.

The log file entry will look like this:

127.0.0.1 - - [14/Jul/2010:15:31:19 -0400] “GET /test/php/index.php HTTP/1.1″ 200 473 “-” “Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.4) Gecko/20100624 CentOS/3.6-8.el5.centos Firefox/3.6.4″ ^4097

The ^4097 indicates the number of microseconds to serve the request.

You’ll need the num_utils RPM, see the link above.

Get it and install it, like so:

wget http://suso.suso.org/programs/num-utils/downloads/rpm/num-utils-0.5-1.noarch.rpm
rpm -i num-utils-0.5-1.noarch.rpm

Use the following commands to extract the times from the log:

grep index.php custom_log | cut -d ‘^’ -f 2

And then, you can average them, like so:

cd /etc/httpd/logs
grep index.php custom_log | cut -d ‘^’ -f 2 | average
2611.33333333333

This post courtesy of Lyrix, Inc. ( http://lyrix.com / http://mobiso.com)

Browser Based Audio Preview / Listen - IE6/7/+, FF2+ - HTTP/HTTPS

The following code segment is an extract from a Smarty template which allows you to embed Windows Media Player in a web page and use javascript to initiate playback. AUDIOFILE.WAV is a marker name, you will need to use a valid URL.

The code uses two different methods for Internet Explorer (IE) and Firefox (FF).

For IE, it uses an object tag and the object model to initiate playback with player.controls.play and stop. Note the assignment of the source URL and data, this allows you to change which audio (or video) is being played.

The FF approach uses a pop up, opened with window.open. This loads a very small page which includes the player tag. The pop up is named, so later requests will reload the same window.

The template uses $bFF as a flag to indicate whether to deliver the page content for IE or FF. $bFF is true if the user agent is Firefox, false otherwise. The template logic can be replaced with PHP.


{if !$bFF}
<object id="wmp_p" name="wmp_p" style="display:none" classid="clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6" width="0" height="0"></object>
<script type="text/javascript">/* <![CDATA[ */
var sAudioFile='AUDIOFILE.WAV';
{literal}
var player=document.getElementById('wmp_p');
function StartMeUp()
{
        if (sAudioFile!='')
        {
            player.URL=sAudioFile;
            player.data=sAudioFile;
            player.controls.play();
        }
}
function ShutMeDown()
{
    player.controls.stop();
}
{/literal}
/* ]]> */
</script>
{else}
<script type="text/javascript">
/* <![CDATA[ */
var sAudioFile='AUDIOFILE.WAV';
{literal}
function StartMeUp()
{
        if (sAudioFile != '')
                window.open('player.php?'+sAudioFile,"player","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=275, height=95");
}
function ShutMeDown()
{
        if (sAudioFile != '')
                window.open('player.php?',"player","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width=275, height=95");
}
{/literal}
/* ]]> */
</script>
{/if}
<a href="javascript:StartMeUp();" title="Listen" id="Preview" name="Preview"><img src="images/cp/16x16/actions/player_play.png" alt="Listen" /></a>

player.php


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Audio Preview</title>
</head>
<body>
<div>
    <img src="images/cp/64x64/apps/arts.png" />
        /* Some code omitted for brevity */
    <!-- Some code omitted for brevity -->
    <object id="wmp_p" data="<?php echo $_SERVER['QUERY_STRING'];?>" type="application/x-ms-wmp" width="175" height="75" >
      <param name="autostart" value="true" />
      <param name="volume" value="10" />
      <param name="uiMode" value="mini" />
      <p>Error - the plugin has not loaded</p>
    </object>
</div>
</body>
</html>

Difficulties were encountered playing HTTPS audio with a self-signed certificate under FF. After research, the simplest resolution was to route audio requests through HTTP.

Resources:

http://www.w3schools.com/media/media_playerref.asp
http://port25.technet.com/pages/
http://support.mozilla.com/en-US/kb/Using+the+Windows+Media+Player+plugin+with+Firefox
For MP3s, you can use: http://docs.dojocampus.org/dojox/av/FLAudio

——————————-

This post courtesy of Lyrix, Inc. ( http://lyrix.com ) - Mobiso ( http://mobiso.com )

ErrorDocument * index.php

Using a site’s home page as an error document is a good way to prevent people and ‘bots from learning more about the site architecture. Routing all error requests to the home page, or some other page, will reduce information leaks, such as ‘file not found’, ‘access denied’, and script failures which could display error information.

It is important to monitor the server error log to see what errors are occurring. Be sure there is a robots.txt file, even if it is empty. Check the log and stats to see what files people are requesting.

Be careful not to confuse legitimate users - if you remove pages or update content frequently, you may want to use 301 redirects for those pages to alert site visitors and search engines of the change.

Wicked Good Gallery - Lightweight LAMP, Easy to Administer

The Wicked Good Gallery grew from an idea mentioned at http://w3schools.com/forum and discussions with the people at http://lilyclaireart.com.

The challenge was to build a gallery application that would allow site visitors to view and purchase artwork and be very easy to administer.

The gallery offers two viewing modes, navigation through the thumbnails at the top of the page, and a slide show. The thumbnails are limited by the width of the page, so not all thumbnails may be displayed. Arrows are used to indicate additional images earlier or later in the list. Clicking on a thumbnail displays a larger image, along with the associated HTML. The slide show hides the HTML and centers the image, then loops through the images to display all the images of the gallery. It’s as if the site visitor is walking through the gallery, looking at artwork.

Administration is through SFTP/FTP/SCP. The person running the site creates a directory, named for the image, and places the original image .jpg in it, as well as free form HTML. When the site loads the next time, it will find the new directory, check for a thumbnail and detail image, and create them if necessary. When site visitors click on the thumbnail, the HTML will be displayed with the image.

With respect to SEO, each image is represented with a dedicated URL. The artwork’s name is used as the title, and the more text in the HTML for that image, the better.

All requests into the application are routed through a single file. If the requests map to an image, the appropriate artwork is displayed, otherwise, the home page, or contact page is shown.