1 | 2 | 3 | 4 | 5

6 - 10   [22]

HTML List Slicer

This post does not relate to any other topics

Here is a neat little solution I concocted the other day to slice an HTML list. The list is stored in a cached object wrapped around additional mark-up and I wanted to display only a part of the list.

Original mark-up:


<div id="menu">
<h3>Some Links</h3>
<ul>
<li><a href="">Item 1</a></li>
<li><a href="">Item 2</a></li>
<li><a href="">Item 3</a></li>
..
<li><a href="">Item n</a></li>
</ul>
</div>

Desired output:


<div id="menu">
<h3>Some Links</h3>
<ul>
<li><a href="">Item 1</a></li>
<li><a href="">Item 2</a></li>
<li><a href="">Item 3</a></li>
</ul>
</div>

One solution is to create an array of all the list elements extract the required indexes and wrap the rest of the mark-up back around the retrieved set. This could be achieved using <a href="http://www.php.net/preg_replace_callback" title="PHP Doc for preg_replace_callback">preg_replace_callback()</a>, <a href="http://www.php.net/preg_match" title="PHP Doc for preg_match">preg_match()</a> and some array manipulations. However a condensed and far more elegant solution is a single call to <a href="http://www.php.net/preg_replace" title="PHP Doc for preg_replace">preg_replace()</a> making use of the /e modifier and a user-defined function. The /e modifier makes preg_replace treat the replacement parameter as PHP code after the appropriate references substitution is done.


class SS_Cached_Item {
  
  [snip]
  
  function getMenu($length, $offset = 0) {
    ss_fns_reset_range();  
    $str = htmlentities($this->_menu, ENT_QUOTES);      
    $str = preg_replace('#(<li>.+</li>\s+)#e',
                       "ss_fns_in_range('\\1', $offset, $length)",
                       $str);
    return unhtmlentities($str);
  }

}

function ss_fns_in_range($str, $offset = 0, $length = 10) {
  static $i = -1;  
  if ($str === NULL) { $i = -1; return false; }
  return (++$i >= $offset && $i < ($offset + $length)) ?
                                      $str : "";  
}
function ss_fns_reset_range() { ss_fns_in_range(NULL); }

The function ss_fns_in_range utilises a static variable to determine the equivalent index of the current match to the pattern. If the index is within the defined range the pattern is returned intact, otherwise it is replaced by an empty string. The supporting function ss_fns_reset_range will reset the static variable hence allowing multiple occurances of the callback in a single script execution. Note, <a href="http://www.php.net/htmlentities" title="PHP Doc for htmlentities">htmlentities()</a> is applied to the string to ensure special characters are not escaped during the replacement.


// display the first five list items
$obj->getMenu(5);    

 // display four items start at offset 2
$obj->getMenu(4, 2);

This functionality could easily be extended to generate random selections from the menu list.

Posted on Aug 13, 2004 at 05:01:50. [Comments for HTML List Slicer- 0]

Simple Acronym Handler

Also relates to Web Standards

Many code snippets for handling acronyms seem to rely on <a href="http://www.php.net/preg_replace" title="PHP Doc for preg_replace">preg_replace()</a>. The problem with this method is ensuring that double replacements do not occur (e.g. with XHTML and HTML). A much better function to use is <a href="http://www.php.net/strtr" title="PHP Doc for strtr">strtr()</a> with two arguments:


$translation_array = array(
  'XHTML' => '<acronym title="eXtensible Hypertext Markup Language">',
  'HTML'  => '<acronym title="Hypertext Markup Language">'
                           );
$str = strtr($str, $translation_array);

It will always look for the longest pattern first and will not try to replace stuff it has already worked on. It is also really quick.

I have been test running the following function on this site to automate acronym handling:


function ss_fns_parse_acronyms($copy) {
  
  static $trans = array();
  
  if (empty($trans)) {
    $dat_file = SOME_PATH . '/data/acronyms.dat';
    $acronyms = parse_ini_file($dat_file);
    foreach($acronyms as $a => $d) {
      $trans[$a] = "<acronym title=\"$d\">$a</acronym>";  
    }    
  }
  
  return strtr($copy, $trans);        
}

The acronyms can be stored in php_ini format in a file, thus new acronyms can be added without interfering with this code. There are occassional limitations and gotchas - e.g. OS inadvertantly appearing in $_POST, but it is an efficient and clean alternative to preg_replace.

Posted on Aug 13, 2004 at 04:58:50. [Comments for Simple Acronym Handler- 0]

Harmonising Link Prefetching And Logging

Also relates to Apache and Firefox and Co

As part of my ongoing redevelopment of this website and in particular my weblog itself I have been building a set of classes to provide visitor statistics. The objective is a simple lightweight solution to summarise referrers, page counts, countries and other similar information per request and without the overhead of trawling the Combined Log Format. With PEAR_DB providing the database abstraction, storing the data is a simple case of compacting all the informative variables and running an auto-execution statement. Of course on the first test drive in Firefox, doubling up of log entries reared its head. This is due to the use of the <link rel="next" href="" title=""/> element providing accessible navigation to the next entry. All the Mozilla browsers (since the days of Netscape 7.x and Mozilla 1.2) will send an HTTP request for the URI in the href attribute to improve performance, thus appending an additional, yet misleading, log entry.

Fortunately there is a simple solution to this which can be resolved in the PHP routine. The browser sends a custom header to tell the server that this is a prefetch request:

X-Moz: prefetch

So in the PHP routine, prefetched pages can be excluded with the following conditional:


if (isset($_SERVER["HTTP_X_MOZ"]) &&
    $_SERVER["HTTP_X_MOZ"] == "prefetch") {
  // do not log the request
  return false;      
}
else {
  // log the request
}

This discovery, all thanks to Live HTTP Headers has been a great salvation to me, since the double logging had caused me many headaches in the past. Accurate logs are essential for good SEO and providing valuable and relevant future content to visitors. But with the current growth in Mozilla based browsers (Firefox in particular) I have found logs becoming more and more distorted. Neither removing relative links nor resorting to Internet Exploder are desirable solutions.

Information on Link Prefetching seems to be sparse but here are the main pages from Mozilla:

Unfortunately this solution does not resolve the general quibble I had with overstated logs and Webalizer. The last release of Webalizer actually preceded the introduction of the prefetch HTTP header. This is a hugely popular log analyzer that seems to be present across a wide range of web servers and hosting packages - makes me wonder how many people are optimistically interpreting over-stated logs? I suppose, with accessibility still on a gradual advance to the mainstream, and the use of the link element only appearing on progressive and forward thinking sites, probably not actually that many.

I have currently been test driving an excellent and very detailed alternative log file analyzer called AW Stats. Well worth a test drive.

Posted on Jul 12, 2004 at 01:33:14. [Comments for Harmonising Link Prefetching And Logging- 1]

MySQLi In PHP5

Also relates to MySQL

PHP5 continues to serve up a fine array of new features. Having already sampled and savoured the very extensible SQLite, I have just stumbled across the new MySQLi (Improved MySQL) extension, which brings an object orientated interface and lots of exciting new enhancements to the MySQL library (while still supporting the older procedural interface). Get stuck in with these Zend articles:

Posted on Jul 01, 2004 at 00:57:37. [Comments for MySQLi In PHP5- 0]

Viva PEAR::Cache_Lite

Also relates to PEAR

With the latest release of the PEAR::Cache the Cache_DB extension has been removed. I first came across Cache_DB in the PHP Cookbook last year and ran several tests to integrate it as a principle caching mechanism in the Content Management System. However, the class offered no noticeable benefit in performance, and when several database calls were being executed for a single page, it did not make sense to use a cache per query. So as an alternative I decided to test drive the PEAR::Cache_Lite package instead. As its nomenclature suggests, this is an excellent lightweight package and offers the perfect solution for filling the caching void in content retrieval. One of the package extensions is <a href="http://pear.php.net/manual/en/package.default.cache-lite.cache-lite-function.cache-lite-function.php" title="Current End User Documentation on Cache_Lite_Function">Cache_Lite_Function</a>, a caching class for functions. This allows for several database calls and the template system to be wrapped in a single method for caching:


class SS_Content {

  // [..snip..]
  
  function fetch($action, $caching = NULL)
  {
    $this->action = $action;
    
    if ($caching === NULL) {
      if (isset($this->_caching)) {
        $caching = $this->_caching;  
      }
    }
    
    if ($caching === SS_CACHING_ON) {
      return $this->retrieveCache();
    }
    else {
      return $this->retrieve();  
    }   
  }
  
  function retrieveCache() {    
    require_once "Cache/Lite/Function.php";

    $options = array(
      'cacheDir'     => CACHE_DIR,
      'lifeTime'     => CACHE_EXP
    );
    $objCache = new Cache_Lite_Function($options);
    return $objCache->call(
      $GLOBALS['_SS_Content_Obj'] . '->retrieve');           
  }
  
  function retrieve()  { 
  
    // this is where the content is built
    // eg database retrieval and content 
    // creation with a template system
  
  }
  
  // [..snip..]
}

This is only a raw example, since within its actual context, SS_Content is actually an abstract class serving specific modular extensions. But it shows how simple Cache_Lite is to use - create the object and invocate the <a href="http://pear.php.net/manual/en/package.default.cache-lite.cache-lite-function.call.php" title="End user documentation on the call method of Cache_Lite_Function">Cache_Lite_Function::call()</a> method. There is one area of caution when using Cache_Lite in an OOP environment like this. The method will look for an object in the global name space equating to the name preceding the accessor operator in the parameter string of the method. How this is achieved is dependant on the application and naming conventions. One solution is to register the variable name of the object in a global variable during instantiation of the class (as demonstrated above).

Melon Fire provides a terse but detailed introduction to Cache_Lite, Cache_Lite_Function and its counterpart Cache_Lite_Output.

Posted on Jun 20, 2004 at 03:40:35. [Comments for Viva PEAR::Cache_Lite- 0]

Breadcrumbs Trail

[ Home ] -> TW Blog -> PHP
Site Map

The Severn Solutions website achieves the following standards:

[ XHTML 1.0 ] [ CSS 2 ] [ WAI AA ] [ Bobby AA ]

Page compiled in 0.017 seconds