Archive

Archive for the ‘PHP’ Category

Howto: Create custom thumbnails from Flutter image fields

September 24, 2009 8 comments

If you’ve worked with Flutter / Fresh pages (wordpress plugin) before, you’ll know that you can use the supplied get_image() function to insert the relevant image field into your template. The problem however is that these images are generated automatically during the upload process and thus conform to the whatever dimensions are set for that field.

There are instances however where you’d need to display the same image but at different sizes, for example a widget vs ‘more info’ display. This is when the following function becomes quite handy.

The auto thumbnail function

You can insert this function straight into your functions.php, a usage example will follow below.

function jt_get_thumb($fieldName, $groupIndex=1, $fieldIndex=1, $readyForEIP=true, $params, $returnimg=true){
        // use flutter's default function to get our field value (non thumbnailed image).
	$file = get($fieldName, $groupIndex, $fieldIndex, $readyForEIP);
        // if the field's empty, we can't continue, return an empty string
        if (empty($file)){
            return "";
        }
        // we want the base filename
	$file = str_replace(FLUTTER_FILES_URI, "", $file);
	
        // get the current image dimensions
	$jtSize = getimagesize(FLUTTER_FILES_PATH.$file);
	
        // break our params up into a useable array.
        $params = explode("&",$params);
	$i = 0;
	foreach($params as $p){
		$tmp = explode("=", $p);
		$jtParams[$tmp[0]] = $tmp[1]; 
		$i++;
	}
        
       // no point in continuing if the width or height params aren't set, or the original image's dimensions are smaller than the supplied params
	if ((!isset($jtParams["w"]) || !isset($jtParams["h"])) || ($jtSize[0] <= $jtParams["w"] && $jtSize[1] <= $jtParams["h"])){
		return ($returnimg) ? "<img src='". FLUTTER_FILES_URI.$file ."' />" : FLUTTER_FILES_URI.$file;
	}
	//generate thumb using flutter's default method (phpThumb class)	
	include_once(FLUTTER_PATH . "/thirdparty/phpthumb/phpthumb.class.php");
	$phpThumb = new phpThumb();
	$phpThumb->setSourceFilename(FLUTTER_FILES_PATH.$file);
	$md5_params = md5($file . $params);
	$create_md5_filename = 'jt_th_'.$md5_params."_".$file;
	$output_filename = FLUTTER_FILES_PATH.$create_md5_filename;
	$final_filename = FLUTTER_FILES_URI.$create_md5_filename;

// experimental, uncomment to test : check if the thumbnail has already been created and use that instead	
//	if (file_exists($output_filename)){
//		$attr_params = "style='width:".$jtParams['w']."px;  height:".$jtParams['h']."px;'";
//		return ($returnimg) ? "<img src='$final_filename' $attr_params />" : $final_filename;
//	}
	
	
	foreach($jtParams as $key => $val){
		$phpThumb->setParameter($key, $val);
	}
	
	if ($phpThumb->GenerateThumbnail()) {
		if ($phpThumb->RenderToFile($output_filename)) {
			$file = $final_filename;
		}
		$attr_params = "";
	} else {
                // if the thumbnail generator fails for some reason, rather return the original image with a forced style attribute
		$file = FLUTTER_FILES_URI.$file;
		$attr_params = "style='width:".$jtParams['w']."px;  height:".$jtParams['h']."px;'";
		
	}
	
	return ($returnimg) ? "<img src='$file' $attr_params />" : $file;

}

Pretty straightforward, the function accepts the standard flutter get() parameters with 2 extras added on to the end. The last two parameters are as follows :

  • $params (string): a standard query string containing the various parameters for the thumbnail generator. Example – “w=400&h=200&zc=1”. The available parameters conform to flutter’s default image thumbnail parameters, see flutter’s documentation for a full reference.
  • $returnimg (bool, default true): set to true to return a formatted img tag, whereas false will return just the full URI based path to the image.

Example Useage

// will output full img tag
echo jt_get_thumb("news_img", 1, 1, false, "w=250&h=250&zc=1");

// build the image string up manually
echo "<img src='" . jt_get_thumb("news_img",1,1,false,"w=250&h=250&zc=1",false) . "' class='newsImgClass' />";

Naturally, this function assumes that the field being queried points to a valid image file, you could of course flesh it out with some fail-safe checks if you’re that way inclined. Please feel free to post any improvements in the comments and I’ll see about adding them to the next version for everyone to use.

Categories: PHP, Wordpress

Simplifying Flutter duplicate groups and fields

September 21, 2009 11 comments

Flutter or Fresh-pages is a really handy plugin for WordPress. With it, you can quite easily create custom write panels, fields and groups of information.

Having worked with this plugin quite extensively recently, I have encountered a few issues which have proved somewhat frustrating, dealing with duplicate groups or fields in particular. Flutter uses a counter based system in which to access duplicate groups. Simple enough when using a standard for loop and all the duplicates are in the correct order – but somewhat tricky to handle when say, deleting one of the duplicates.

I’ve since resorted to writing a function to make handling these duplicates and a number of associated problems whilst looping through them much, much simpler.

Which brings us to the following function – which you can paste straight into your theme’s functions.php file :

function jt_get_flutter_duplicates($field, $group){
	if (is_string($group)){
		$numfiles = getGroupDuplicates($group);
		$isgroup = true;
	} else {
    	$numfiles = getFieldDuplicates($field, $group);
    	$isgroup = false;
	}
    $return = false;
    if ($numfiles > 0){
        $return = array();
        $count = 1;
        $tmp = "";
        $first = "";
        $total = ($numfiles > 1) ? 100 : 1;
        while($count <= $total){
        	if ($isgroup){
        		$value = get($field, $count, 1);
        	} else {
            	$value = get($field, $group, $count);
        	}
            if (empty($value) || $value == $first || $tmp == $value){
                $count++;
                continue;
            } elseif ($count == 1){
                $first = $value;
            }

            $tmp = $value;
            $return[] = ($isgroup) ? $count : $value;
        }
    }
    return (is_array($return) && count($return) > 0) ? $return : false;
}

I’m not going to go into too much detail right now, but essentially what this function does is gets the required field or group duplicates, loops through them (up to a max of 100 iterations just to be safe) and places them into an array containing the correct index values (if a group) or an array of field values (if a field).

Group duplicates

Set the first parameter to the name of a field within the group which should contain a unique value (such as name, or caption). The second parameter can be any field within the required group.

$groupItems = jt_get_flutter_duplicates("news_image", "news_image");
if (is_array($groupItems) && count($groupItems) > 0){
    foreach($groupItems as $i) {        
	echo get_image("news_image",$i, 1, 1);
        echo "<br />" . get("news_image_caption",$i, 1, false);
    }
}

Field Duplicates

Set the first parameter to the name of the field you want to retrieve. Set the second parameter to the group index you want to retrieve the field duplicates from. For instance, you could have 1 group which can be duplicated, containing 3 fields, one of which can be duplicated. When looping through the group duplicates, you’ll want another sub-loop to handle the duplicated field contents. The function will return an array of values which you can then use in a standard foreach loop.

$groupItems = jt_get_flutter_duplicates("office_title", "office_title");
if (is_array($groupItems) && count($groupItems) > 0){
    foreach($groupItems as $i) {  
        echo get("office_title",$i,1,false);
        $contacts = jt_get_flutter_duplicates("office_contact", $i);
        if (is_array($contacts) && count($contacts) > 0) {           
           foreach($contacts as $contact){               
               echo "<br />Contact Person: " . $contact;
           }
        }
    }
}

This is of course a quick fix and by no means infallible, so I’d love to hear if you have any improvements or a simpler way of dealing with this particular issue.

Categories: PHP, Wordpress

Search Engine Friendly URLs using PHP

December 11, 2008 5 comments

A little while ago I wrote a post on Django Inspired SEF urls. I’ve since put together a working example and am now making it available for download.

There are a couple of things to note :

  1. You’ll need to edit the necessary path and url info found inside index.php and .htaccess
  2. The sample code inside index.php is for demonstration purposes only – I strongly recommend using a proper gateway/bootstrap interface typically found in MVC based systems such as Zend Framework and CakePHP (though they of course have their own SEF url implementations).
  3. Edit urls.php to play around with the regular expressions. I’ll be the first to admit that my regexp skills are not the best in the world, so if you notice something that will work better, please let me know – I’d really appreciate it
  4. I’ve included a url generator inside the class to try and make your life easier – view index.php for example usage

That should do it. You can download the script here.

Please play around with it and if you make any improvements I’d love to hear about it.

Categories: PHP Tags: , , ,

Writing dynamic classes with PHP

October 13, 2008 Leave a comment

I recently had the necessity to write a dynamic class (i.e. where methods, or properties of the class are determined during runtime) and in doing some research, stumbled on to this very helpful article :

Going dynamic with PHP

The author (Jack Herrington) goes to a lot of trouble explaining how the magic __get(), __set() and __call() methods work and how best to use them.

It does get a little bit complicated towards the end, but is a good example of a)why one should or need to use a dynamic class/object and b)how to implement it successfully within your application.

PHP wrapper class for Zend View

October 7, 2008 2 comments

I recently posted an article with an example of a Zend Session wrapper I’ve been using for some of my projects.

In that same vein I’ve created a simple wrapper for Zend View. I found this useful in instances where I didn’t need to use Zend Framework’s whole MVC implementation. Zend View is extremely helpful in creating a quick templating system for smaller projects or custom frameworks.

Once again I’ll be assuming the following:

First our view wrapper/abstraction class (view.php) :

<?php
/**
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to calisza@gmail.com so we can send you a copy immediately.
 *
 * @package    FlexiDev_View
 * @copyright  Copyright (c) 2007 - 2008,  Flexible Developments - Barry Roodt (https://calisza.wordpress.com)
 * @license    New BSD License
 */
 class View {
 	public $tmplVars;
 	public $view;

 	/**
 		Constructor
 	*/
 	public function __construct(){
		
 		$this->tmplVars = array();
 		// Instantiate our Zend View
		$this->view = new Zend_View();
		
		/**
		 * reset our basepath and scriptpaths, this is because we don't need the 'scripts', 'helpers' and 'filters' sub folders 
		 * relative to this script - of course you could change this as your needs require
		 */
		$this->view->setBasePath("./view");
		$this->view->setScriptPath("./view");
		
		// set and assign some global vars to our template
		// this is just an example - you can add more here
		$globals  = array("base_url" => $_SERVER["HTTP_HOST"]);
		$this->view->assign($globals);
 	}
 	/**
	 * Method to add to our template variable stack 
	 * @param string variable name
	 * @param mixed value
	 */ 
 	public function addTmplVar($key, $value){
 		/* If our stack doesn't already exist, create a new array */
 		if (!is_array($this->tmplVars)){
 			$this->tmplVars = array($key=>$value); 			
 		} else {
 			// Add to our variable stack
 			$this->tmplVars[$key] = $value;
 		}
 	}
 	/**
	 * Method to render the required view/template
	 * @param string template script name
	 * @param bool (optional) clear variable stack (default=true)
	 */ 
 	public function getTemplate($tmpl, $clearVars=true){
 		/* Add our variable stack to the template */
		$this->view->assign($this->tmplVars); 
		/* If required, clear our stack so that we can start with a fresh template on the next call */
		if ($clearVars)
			$this->tmplVars = "";
			
		/* Return the rendered template */
		return $this->view->render($tmpl);
	}
	
 }

?>

Next for some example usage (index.php) :

<?php
require_once("View.php");
$items = array ("Name" => "Barry", "Surname" => "Roodt", "Title" => "Mr");
$view = new View();
$view->addTmplVar("myitems", $items);
echo $view->getTemplate("template.phtml");
?>

And finally our view template (template.phtml) :

<div>Hi <?php echo $this->myitems["name"]; ?></div>
<div>Here is some information for you
<?php foreach($this->myitems as $key=>$val) : ?>
    <p><strong><?php echo $key; ?>: </strong><?php echo $val; ?></p>
<?php endforeach; ?>
</div>
<div>You can also use Zend View's default helpers like so : <?php echo $this->escape($this->myitems["name"]); ?></div>

You can also view an example of this class in action here.

As always, comments, critiques and corrections welcome.

Spam proof Contact Form with Zend Framework, ReCaptcha and Akismet / TypePad Antispam

October 3, 2008 7 comments

Update : Download Source code here
Update : View Working Demo

Over the last few weeks I’ve blogged on using the Zend Framework’s Akismet Service to “spam proof” your generic, run of the mill contact form.

Of course, services like Akismet and TypePad Antispam only come into play after the form has been submitted, a “cure” so to speak.

What we really need is some form of robust “prevention”. Stop the spam bots right at the door. There’s nothing more irritating than getting bombarded by 10k submissions every hour on the hour – even if they’re being filtered out by your Akismet / TypePad service.

Which brings us to the now common place CAPTCHA component. Designed to stop automated spam bots in their tracks, its a pretty effective method. Ordinarily, it’s not too difficult to implement a CAPTCHA component to your form, but ReCaptcha and the Zend Framework have now made it child’s play.

ReCaptcha is an online service providing a free CAPTCHA API for you to use. What’s also really nifty is that while your users fill in the CAPTCHA form, they’re also helping develop OCR software used to digitize books and manuscripts.

As an example I’ve gone and written a basic contact form, zipped it up and made it available here. Inside the zip file is all the code necessary for a very basic and hopefully spam proof contact form.

The following Zend Framework components were used :

  • Zend Loader
  • Zend Config
  • Zend View
  • Zend Service ReCaptcha
  • Zend Service Akismet
  • Zend Mail
  • Zend Validate
  • Zend Filter
  • Zend Registry

I’ve also included a simple wrapper for Zend View, since I didn’t feel that it was necessary to go the whole MVC route on this one.

The code used is pretty straightforward and easy to follow with as many comments thrown in there as I could stomach, but if you have any questions please feel free to leave a comment or send a mail.

Before you begin, you’ll need the following:

To use, unzip the file (it already contains the Zend Framework v1.6), edit the config.xml file and then simply customize the view scripts (view/form.php, view/success.php, view/mail.php).

Unfortunately I haven’t had much time to test the example thoroughly as it was written in a bit of a hurry (only had about an hour or so), so if you find or even fix any bugs please let me know and I’ll sort it out as soon as I get an opportunity to do so.

Update: I’ve amended 3 bugs thanks to comments from Mark. Affected files can be downloaded separately here, or you can download the full source again here. I’ve also created a working demo here.

  1. In view/form.php line 23 : “$this->TextArea” has been changed to “$this->Textarea”. TextArea works on my local host, but not when uploaded to my demo server.
  2. In view/form.php line 25-27 : Added a new paragraph containing “$this->captcha” to output the ReCaptcha element. This was not included in the zip file’s form.php for some inexplicable reason – my apologies
  3. In library/FlexiDev/Process.php line 122 : Logic error preventing ReCaptcha from verifying correctly. $recaptcha->verify(…) returns an object and not a boolean value – amended to $result = $recaptcha->verify(…); return $result->isValid();

One last note : the code is provided under the New BSD License, so have fun.

Adding TypePad Anti Spam to your Contact Form with Zend Framework

September 24, 2008 3 comments

About 2 weeks ago, I posted a quick tutorial on how to implement Akismet.com’s anti-spam checks to your contact form using the excellent Zend Framework. I was fortunate enough to have Anil Dash from Six Apart (the creators of TypePad) drop by and post a comment asking me if I’d be interested in giving TypePad’s Antispam service a try.

I’m happy to report that TypePad Antispam is just as effective as Akismet, with the bonus of there being no restrictions on usage (that I could find in any case).

With all of that in mind – I simply copied the current Zend/Service/Akismet.php class – renamed it to Zend/Service/TypePadAntiSpam.php and did a simple search and replace. The two services are so alike in implementation, that’s all it took.

The code posted below makes the following assumptions :

Firstly the TypePad Antispam Class. Simply save this file to the following folder inside your Zend Framework installation : Zend/Service/TypePadAntiSpam.php (should be in the same folder as Akismet.php)

<?php
/**
 * Please note that this is NOT an official Zend Framework package.
 * This is essentially a copy-paste-modification of the original Zend Framework's Service/Akismet.php class to
 * work with the TypePad Anti Spam service. If you find this class useful or find an error etc, please leave a
 * comment at https://calisza.wordpress.com - all feedback is welcome.
 * 
 * All original/offical headers have been left intact. Thanks to all the devs who have made the Zend Framework
 * the wonderful product that it is.
 */

/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Service
 * @subpackage TypePadAntiSpam
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */


/**
 * @see Zend_Version
 */
require_once 'Zend/Version.php';

/**   
 * @see Zend_Service_Abstract
 */
require_once 'Zend/Service/Abstract.php';


/**
 * Typepad Anti Spam REST service implementation
 *
 * @uses       Zend_Service_Abstract
 * @category   Zend
 * @package    Zend_Service
 * @subpackage TypePadAntiSpam
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Service_TypePadAntiSpam extends Zend_Service_Abstract
{
    /**
     * TypePadAntiSpam API key
     * @var string
     */
    protected $_apiKey;

    /**
     * Blog URL
     * @var string
     */
    protected $_blogUrl;

    /**
     * Charset used for encoding
     * @var string
     */
    protected $_charset = 'UTF-8';

    /**
     * TCP/IP port to use in requests
     * @var int
     */
    protected $_port = 80;

    /**
     * User Agent string to send in requests
     * @var string
     */
    protected $_userAgent;

    /**
     * Constructor
     *
     * @param string $apiKey TypePadAntiSpam API key
     * @param string $blog Blog URL
     * @return void
     */
    public function __construct($apiKey, $blog)
    {
        $this->setBlogUrl($blog)
             ->setApiKey($apiKey)
             ->setUserAgent('Zend Framework/' . Zend_Version::VERSION . ' | TypePadAntiSpam/1.1');
    }

    /**
     * Retrieve blog URL
     *
     * @return string
     */
    public function getBlogUrl()
    {
        return $this->_blogUrl;
    }

    /**
     * Set blog URL
     *
     * @param string $blogUrl
     * @return Zend_Service_TypePadAntiSpam
     * @throws Zend_Service_Exception if invalid URL provided
     */
    public function setBlogUrl($blogUrl)
    {
        require_once 'Zend/Uri.php';
        if (!Zend_Uri::check($blogUrl)) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid url provided for blog');
        }

        $this->_blogUrl = $blogUrl;
        return $this;
    }

    /**
     * Retrieve API key
     *
     * @return string
     */
    public function getApiKey()
    {
        return $this->_apiKey;
    }

    /**
     * Set API key
     *
     * @param string $apiKey
     * @return Zend_Service_TypePadAntiSpam
     */
    public function setApiKey($apiKey)
    {
        $this->_apiKey = $apiKey;
        return $this;
    }

    /**
     * Retrieve charset
     *
     * @return string
     */
    public function getCharset()
    {
        return $this->_charset;
    }

    /**
     * Set charset
     *
     * @param string $charset
     * @return Zend_Service_TypePadAntiSpam
     */
    public function setCharset($charset)
    {
        $this->_charset = $charset;
        return $this;
    }

    /**
     * Retrieve TCP/IP port
     *
     * @return int
     */
    public function getPort()
    {
        return $this->_port;
    }

    /**
     * Set TCP/IP port
     *
     * @param int $port
     * @return Zend_Service_TypePadAntiSpam
     * @throws Zend_Service_Exception if non-integer value provided
     */
    public function setPort($port)
    {
        if (!is_int($port)) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid port');
        }

        $this->_port = $port;
        return $this;
    }

    /**
     * Retrieve User Agent string
     *
     * @return string
     */
    public function getUserAgent()
    {
        return $this->_userAgent;
    }

    /**
     * Set User Agent
     *
     * Should be of form "Some user agent/version | TypePadAntiSpam/version"
     *
     * @param string $userAgent
     * @return Zend_Service_TypePadAntiSpam
     * @throws Zend_Service_Exception with invalid user agent string
     */
    public function setUserAgent($userAgent)
    {
        if (!is_string($userAgent)
            || !preg_match(":^[^\n/]*/[^ ]* \| TypePadAntiSpam/[0-9\.]*$:i", $userAgent))
        {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid User Agent string; must be of format "Application name/version | TypePadAntiSpam/version"');
        }

        $this->_userAgent = $userAgent;
        return $this;
    }

    /**
     * Post a request
     *
     * @param string $host
     * @param string $path
     * @param array  $params
     * @return mixed
     */
    protected function _post($host, $path, array $params)
    {
        $uri    = 'http://' . $host . ':' . $this->getPort() . $path;
        $client = self::getHttpClient();
        $client->setUri($uri);
        $client->setConfig(array(
            'useragent'    => $this->getUserAgent(),
        ));

        $client->setHeaders(array(
            'Host'         => $host,
            'Content-Type' => 'application/x-www-form-urlencoded; charset=' . $this->getCharset()
        ));
        $client->setParameterPost($params);

        $client->setMethod(Zend_Http_Client::POST);
        return $client->request();
    }

    /**
     * Verify an API key

     *
     * @param string $key Optional; API key to verify
     * @param string $blog Optional; blog URL against which to verify key
     * @return boolean
     */
    public function verifyKey($key = null, $blog = null)
    {
        if (null === $key) {
            $key = $this->getApiKey();
        }

        if (null === $blog) {
            $blog = $this->getBlogUrl();
        }

        $response = $this->_post('api.antispam.typepad.com', '/1.1/verify-key', array(
            'key'  => $key,
            'blog' => $blog
        ));

        return ('valid' == $response->getBody());
    }

    /**
     * Perform an API call
     *
     * @param string $path
     * @param array $params
     * @return Zend_Http_Response
     * @throws Zend_Service_Exception if missing user_ip or user_agent fields
     */
    protected function _makeApiCall($path, $params)
    {
        if (empty($params['user_ip']) || empty($params['user_agent'])) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Missing required TypePadAntiSpam fields (user_ip and user_agent are required)');
        }

        if (!isset($params['blog'])) {
            $params['blog'] = $this->getBlogUrl();
        }

        return $this->_post($this->getApiKey() . '.api.antispam.typepad.com', $path, $params);
    }

    /**
     * Check a comment for spam
     *
     * Checks a comment to see if it is spam. $params should be an associative
     * array with one or more of the following keys (unless noted, all keys are
     * optional):
     * - blog: URL of the blog. If not provided, uses value returned by {@link getBlogUrl()}
     * - user_ip (required): IP address of comment submitter
     * - user_agent (required): User Agent used by comment submitter
     * - referrer: contents of HTTP_REFERER header
     * - permalink: location of the entry to which the comment was submitted
     * - comment_type: typically, one of 'blank', 'comment', 'trackback', or 'pingback', but may be any value
     * - comment_author: name submitted with the content
     * - comment_author_email: email submitted with the content
     * - comment_author_url: URL submitted with the content
     * - comment_content: actual content
     *
     * Additionally, TypePadAntiSpam suggests returning the key/value pairs in the
     * $_SERVER array, and these may be included in the $params.
     *
     * This method implements the TypePadAntiSpam comment-check REST method.
     *
     * @param array $params
     * @return boolean
     * @throws Zend_Service_Exception with invalid API key
     */
    public function isSpam($params)
    {
        $response = $this->_makeApiCall('/1.1/comment-check', $params);

        $return = trim($response->getBody());

        if ('invalid' == $return) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid API key');
        }

        if ('true' == $return) {
            return true;
        }

        return false;
    }

    /**
     * Submit spam
     *
     * Takes the same arguments as {@link isSpam()}.
     *
     * Submits known spam content to TypePadAntiSpam to help train it.
     *
     * This method implements TypePadAntiSpam's submit-spam REST method.
     *
     * @param array $params
     * @return void
     * @throws Zend_Service_Exception with invalid API key
     */
    public function submitSpam($params)
    {
        $response = $this->_makeApiCall('/1.1/submit-spam', $params);
        $value    = trim($response->getBody());
        if ('invalid' == $value) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid API key');
        }
    }

    /**
     * Submit ham
     *
     * Takes the same arguments as {@link isSpam()}.
     *
     * Submits a comment that has been falsely categorized as spam by TypePadAntiSpam
     * as a false positive, telling TypePadAntiSpam's filters not to filter such
     * comments as spam in the future.
     *
     * Unlike {@link submitSpam()} and {@link isSpam()}, a valid API key is
     * never necessary; as a result, this method never throws an exception
     * (unless an exception happens with the HTTP client layer).
     *
     * this method implements TypePadAntiSpam's submit-ham REST method.
     *
     * @param array $params
     * @return void
     */
    public function submitHam($params)
    {
        $response = $this->_makeApiCall('/1.1/submit-ham', $params);
    }
}

Next, some code to implement our new class :

<?php
/*
* Basic function to check for spam
* @param items : associative array for containing form field values
* @return boolean : true if spam, false if clean
*/
function spamCheck($items){
 	require_once 'Zend/Service/TypePadAntiSpam.php'; // include the required class file - change path if necessary
 	$url = "http://url.to.my.blog.or.form"; // url associated with API key
	$api = "432dsjk890"; // TypePad Antispam API key
 	$spam = new Zend_Service_TypePadAntiSpam($api, $url ); // create new instance of our TypePadAntiSpam Service class

	if ($spam->verifyKey()){ // make sure the API key if valid before performing check
	 	$params = array(); // check the comments for the isSpam() method in Zend/Service/TypePadAntiSpam.php for more information on available parameters
	 	$params["user_ip"] = $_SERVER['REMOTE_ADDR']; // required by TypePadAntiSpam
	 	$params["user_agent"] = $_SERVER['HTTP_USER_AGENT']; // required by TypePadAntiSpam
	 	$params["referrer"] = $_SERVER[ 'HTTP_REFERER'];
	 	$params["comment_type"] = "comment";
	 	$params["comment_author"] = $items["name"];
	 	$params["comment_author_email"] = $items["email"];
	 	$params["comment_content"] = $items["comments"];

	 	return $spam->isSpam($params); // submits api call and returns true if spam, false if clean

	} else {
 		return false;
 	}
 }

// to make use of our spam check function try the following :
$items = sanitize($_POST); // sanitize is your own built-in function to sanitize user submitted data

// only mail the form contents if not spam
if (!spamCheck($items)){
	// insert code to mail form contents here
}
?>

That should do it. You should now have a robust, easy to use anti-spam solution for your contact forms.