FavIcons, Apple Touch Icons, Site Thumbnail Images: PHP and WordPress Coding Issues

This is the third part of a three-part discussion of favicons, Apple-Touch icons, site thumbnail images, and how to tag pages (especially WordPress pages) to associate them with such graphics. The first part explained the purpose of these graphics, and the required HTML. The second part provides working code that can be dropped into a WordPress theme (specifically, into functions.php). This final, very boring part motivates that code, and provides some more general discussion which could be applied to PHP other than WordPress.

In discussing the nature of favicons, Apple Touch icons, and thumbnail graphics used by linking sites, I tried to give some indication as to how I thought the HTML ought to be handled. Even in the case of dynamically generated webpages, some of the code that I've seen out there just hard-codes the HTML for these graphics. Such code is very breakable, as when, for example, a 'blog is relocated to a different directory (perhaps of a different domain). And, in the case of dynamically generated individual posts and pages which are to have their own thumbnail images, the handling too must be dynamic.

There are further issues in how generating PHP code should be designed, some general, and some specific to WordPress.

[Up-Date (2020:05/19): I up-dated the code to-day, but have only partially up-dated the discussion around the code. So the explanations are a bit crufty.]

General PHP Issues

From the perspective of user-friendliness, perhaps the best route would involve creäting a webpage interface that would allow the user to upload graphics files, and declare these or files already present to be the favicon, Apple Touch icons, or default thumbnail graphic icons. For the aforementioned sorts of files, my code just looks for candidates from within restricted sets of names, and uses the first of each that it encounters. In the case of favicons and thumbnail images, the differences amongst allowed file names is to support different extensions, corresponding to different formats; but a single file-name could have been imposed, with no extension or with the extension ignored. In the case of Apple Touch icons, the names themselves pass further information to the client device interface.

The basic idea is just

$filespec = "";
if (file_exists("candidateNameFirst")
  $filespec = $siteURL . "candidateNameFirst";
else if (file_exists("candidateNameSecond")
  $filespec = $siteURL . "candidateNameSecond";

else if (file_exists("candidateNameLast")
  $filespec = $siteURL . "candidateNameLast";
if ($filespec)
  echo "<tagStuff" . $filespec . "moreTagStuff />\n";

for each sort of image (the favicon, the Apple touch icons, and the default thumbnail images). The site URL isn't in the code as a (sub)string literal so that the user doesn't have to mess with it at each installation. The major complication is when the user has selected a distinct image file to be used as a thumbnail graphic for a particular page. That file then has to be invoked.

Some of the tags to be generated have a type attribute, in which a MIME image type is supposed to be identified. The easiest way to infer an image type is from the file-name extension (eg jpg), but I've encountered many files of one sort with the extension of another. So my thinking was to call a function to identify the MIME type. I didn't find a standard function that did just that, but the ill-named getimagesize function actually returns an array, one element of which (since PHP version 4.3.0) is MIME type information. So I could call getimagesize within another function, which would just return that type information.

/* First Attempt */
function getimagemimetype ($filespec)
{
  $imagesizearray = getimagesize($filespec);
  return($imagesizearray['mime']);
}

Unfortunately, while I found that this worked fine for GIF, JPEG FIF, and PNG files, it returned an empty string for MS icon files. In cases where an empty string is returned, I resorted to reading the first four bytes of the file, and comparing these with the first four bytes of the MS icon file specification. (The comparison itself is somewhat kludgy, because PHP is not good about reading bytes as such nor about comparing characters in strings to integral values, and treats a byte 0x00 as an EOS).

function getimagemimetype ($filespec)
{
  $imagesizearray = getimagesize($filespec);
  $type = $imagesizearray['mime'];
  if (strlen($type) == 0)
  { /* See if it might be a .ICO */
    $identifier = fread(fopen($filespec,'r'),4);
    $bytes = "\x0\x1\x2";
    if ( ($identifier[0] == $bytes[0]) && ($identifier[1] == $bytes[0])
                               &&
        (($identifier[2] == $bytes[1]) || ($identifier[2] == $bytes[2]))
                               &&
        ($identifier[3] == $bytes[0]))
      $type = "image/vnd.microsoft.icon";
  }
  return ($type);
}

Hypothetically, I could have just punted to looking for a file-name extension of .ico exactly and only when getimagesize failed, or even never invoked it for files with that extension. Also, the results of experimentation suggest that setting the type attribute to an empty string will not prevent a MS icon from working as a favicon.

Another possibility would be to read the first few bytes of any file, rather than calling getimagesize. A GIF file begins GIF87a or GIF89a and its proper MIME type is image/gif; a JPEG FIF file begins \xFF\xD8\xFF\xE0\xdd\xddJFIF\x00 (where d is some hexadecimal digit) and its proper MIME type is image/jpeg; an ICO file begins \x00\x00\0x00\0x01 or \x00\x00\0x00\0x02 and its proper MIME type is image/vnd.microsoft.icon; a PNG file begins \x89PNG\x0D\x0A\x1A\x0A and its proper MIME type is image/png. But (as noted parenthetically above) PHP is not good about handling the comparison in cases where a zero byte is part of the header; it might be best to provide a separate function to handle the byte comparisons as such, if there are going to be many of them.

The code to identify MIME type could be put in-line everywhere it were used; but, unless it is going to be from bald acceptance of the file-name extension (in which case it might be done when the file existence were detected), there would be dubious efficiencies in doing that.

My code for the favicon conforms pretty closely to the basic structure sketched above, but the MIME type is handled by introducing a variable $favicon_type, set as part of the positive outcome of any condition.

if (file_exists("favicon.xxx"))
{
  $favicon_spec = get_bloginfo('siteurl') . "/favicon.png";
  $favicon_type = getimagemimetype("favicon.xxx");
}

(NB: The function get_bloginfo is here drawn from WordPress.) My order of preëmption was fairly aribitrary.

My code for the AppleTouch icons actually just puts the echo commands within the conditionals.

$siteurl = get_bloginfo('siteurl');
$apple_touch_icon_possible_size = array("57", "72", "76", "120", "144", "152", "180");
foreach ($apple_touch_icon_possible_size as $apple_touch_icon_size)
{
    $apple_touch_icon_sizes = $apple_touch_icon_size . "x" . $apple_touch_icon_size;
    $apple_touch_icon_name_partial = "apple-touch-icon-" . $apple_touch_icon_sizes;
    if (file_exists($apple_touch_icon_name_partial . "-precomposed.png"))
        echo " <link rel=\"apple-touch-icon-precomposed\" sizes=\"" . $apple_touch_icon_sizes . "\" href=\""
                 . $siteurl . $apple_touch_icon_name_partial . "-precomposed.png\" />\n";
    else if (file_exists($apple_touch_icon_name_partial . ".png"))
        echo " <link rel=\"apple-touch-icon\" sizes=\"" . $apple_touch_icon_sizes . "\" href=\"" . $siteurl
                 . $apple_touch_icon_name_partial . ".png\" />\n";
}

The order of preëmption conforms to what an standard client would do in the absence of tags.

My code for the thumbnail graphics is very much like that for the favicon, except that it first looks for a page-specific image.

WordPress Issues

The first question is of what, if anything, to put in functions.php and of what, if anything, to put in header.php. In order to use the featured image facility of WordPress version 2.9 and later, functions.php must have

add_theme_support('post-thumbnails');

(preferably prefaced with if (function_exists('add_theme_support'))) inserted into it. All of the remaining code here could be put into either .php file, though getimagemimetype should probably be defined in functions.php.

I chose to put everything remaining also into functions.php. That meant wrapping it into one or more functions, and then adding each such function as an action to wp_head. If, instead, that code were placed in header.php, then the code should be inserted somewhere within the head element, and only the code of getimagemimetype should be wrapped into a function. Given that the code is all being placed in functions.php, the next question is of whether to put the code for all three sorts of graphics into just one function. If one is always going to use all of the functionality, and never modify the implementation once it's effected, then one function is fine. But, for ease of maintainability, I chose to creäte three functions, one for each basic sort of graphic; that required three calls to add_action, one for each of those functions.

I've seen code for the thumbnail graphic that provides a default for singular pages (individual entries, and those pages which WordPress calls Pages) that do not have a Featured Image, but that does not provide thumbnails for non-singular pages. I don't see why the default thumbnail graphic should not be used in such cases. In any event, the code for extracting the page-specific thumbnail specification is a bit rococco.

if (is_singular())
  if (function_exists('has_post_thumbnail'))
    if (has_post_thumbnail())
    {
      $image_array = wp_get_attachment_image_src(get_post_thumbnail_id());
      $thumbnail_graphic_spec = esc_attr($image_array[0]);
    }

get_post_thumbnail_id (with no argument) returns an integer associated with the featured image for the present page. Basically by look-up, wp_get_attachment_image_src converts that integer into an array, the initial element of which is a URL for the featured image. esc_attr makes sure that characters that could cause awkwardness are removed or escaped.

Comments