home forums resources search newsjoinmembers: 6448
Hiveminds Network PHP Flash Java Ruby Windows Linux
Hiveminds | Fri, 2006-07-14 13:19  tags:

PHP developer

Building a CMS system looks like a big task to most. Especially when the CMS has to be a custom job. The Zend PHP Framework will make things a lot easier to code and to maintain.

I was using Drupal and want to just set a feeds output on to the front page of this website. Out of curiosity today I just looked at a Zend example on how to parse a RSS feed. Here are three examples of code which one looks easiest to deal with? while the Zend example is rather simple it still looks nicer than what I managed to do with Drupal. Though the Drupal code looks clean you can still see what a big difference using OOP makes. this and the fact that I never have to worry about my call to the Zend code changing even when the internals change. While in Drupal one point versio change might mean that my code would have to be re-written.

Zend Framwork example:

<?php

$path 
'D:\wamp\www\ZendFramework-0.1.3\library';
set_include_path(get_include_path() . PATH_SEPARATOR $path);
/**
 * Consume an RSS feed and display all of the titles and
 * associated links within.
 */
require_once 'Zend/Feed.php';
$feed Zend_Feed::import('http://news.google.com/?output=rss');
foreach (
$feed->items as $item) {
    echo 
"<p>" $item->title() . "<br />";
    echo 
"<a href=".$item->link().">"  $item->title() ."</a></p>";
}
?>

Zend Framework core code:

<?php

/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to version 1.0 of the Zend Framework
 * license, that is bundled with this package in the file LICENSE, and
 * is available through the world-wide-web at the following URL:
 * http://www.zend.com/license/framework/1_0.txt. If you did not receive
 * a copy of the Zend Framework license and are unable to obtain it
 * through the world-wide-web, please send a note to license@zend.com
 * so we can mail you a copy immediately.
 *
 * @package    Zend_Feed
 * @copyright  Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
 */
/**
 * Zend_Feed_Exception
 */
require_once 'Zend/Feed/Exception.php';
/**
 * Zend_Feed_Atom
 */
require_once 'Zend/Feed/Atom.php';
/**
 * Zend_Feed_Rss
 */
require_once 'Zend/Feed/Rss.php';
/**
 * Zend_Http_Client
 */
require_once 'Zend/Http/Client.php';
/**
 * Base Zend_Feed class, containing constants and the Zend_Http_Client instance
 * accessor.
 *
 * @package    Zend_Feed
 * @copyright  Copyright (c) 2005-2006 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://www.zend.com/license/framework/1_0.txt Zend Framework License version 1.0
 */
class Zend_Feed
{
    
/**
     * HTTP client object to use for retrieving feeds
     *
     * @var Zend_Http_Client_Abstract
     */
    
protected static $_httpClient null;
    
/**
     * @var array
     */
    
protected static $_namespaces = array(
        
'osrss' => 'http://a9.com/-/spec/opensearchrss/1.0/',
        
'atom' => 'http://www.w3.org/2005/Atom',
        
'rss' => 'http://blogs.law.harvard.edu/tech/rss',
        );
    
/**
     * Sets the HTTP client object to use for retrieving the feeds.  If none
     * is set, the default Zend_Http_Client will be used.
     *
     * @param Zend_Http_Client_Abstract $httpClient
     */
    
public static function setHttpClient(Zend_Http_Client_Abstract $httpClient)
    {
        
self::$_httpClient $httpClient;
    }
    
/**
     * Gets the HTTP client object.
     *
     * @return Zend_Http_Client_Abstract
     */
    
public static function getHttpClient() {
        if (!
self::$_httpClient instanceof Zend_Http_Client_Abstract)
        {
            
self::$_httpClient = new Zend_Http_Client();
        }
        return 
self::$_httpClient;
    }
    
/**
     */
    
public static function lookupNamespace($prefix)
    {
        return isset(
self::$_namespaces[$prefix]) ?
            
self::$_namespaces[$prefix] :
            
$prefix;
    }
    
/**
     */
    
public static function registerNamespace($prefix$namespaceURI)
    {
        
self::$_namespaces[$prefix] = $namespaceURI;
    }
    
/**
     * Imports a feed located at $uri.
     *
     * @param string $uri
     * @throws Zend_Feed_Exception
     * @return Zend_Feed_Abstract
     */
    
public static function import($uri)
    {
        
$client self::getHttpClient();
        
$client->setUri($uri);
        
$response $client->get();
        if (
$response->getStatus() !== 200) {
            throw new 
Zend_Feed_Exception('Feed failed to load, got response code ' $response->getStatus());
        }
        
$feed $response->getBody();
        return 
self::importString($feed);
    }
    
/**
     * Imports a feed represented by $string.
     *
     * @param string $string
     * @throws Zend_Feed_Exception
     * @return Zend_Feed_Abstract
     */
    
public static function importString($string)
    {
        
// Load the feed as an XML DOMDocument object
        
@ini_set('track_errors'1);
        
$doc = new DOMDocument();
        
$success = @$doc->loadXML($string);
        @
ini_restore('track_errors');
        if (! 
$success) {
            throw new 
Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
        }
        
// Try to find the base feed element or a single <entry> of an Atom feed
        
if ($doc->getElementsByTagName('feed')->item(0) ||
            
$doc->getElementsByTagName('entry')->item(0)) {
            
// return a newly created Zend_Feed_Atom object
            
return new Zend_Feed_Atom(null$string);
        }
        
// Try to find the base feed element of an RSS feed
        
if ($doc->getElementsByTagName('channel')->item(0)) {
            
// return a newly created Zend_Feed_Rss object
            
return new Zend_Feed_Rss(null$string);
        }
        
// $string does not appear to be a valid feed of the supported types
        
throw new Zend_Feed_Exception('Invalid or unsupported feed format');
    }
    
/**
     * Imports a feed from a file located at $filename.
     *
     * @param string $uri
     * @throws Zend_Feed_Exception
     * @return Zend_Feed_Abstract
     */
    
public static function importFile($filename)
    {
        @
ini_set('track_errors'1);
        
$feed = @file_get_contents($filename);
        @
ini_restore('track_errors');
        if (
$feed === false) {
            throw new 
Zend_Feed_Exception("File could not be loaded: $php_errormsg");
        }
        return 
self::importString($feed);
    }
    
/**
     * Attempts to find feeds at $uri referenced by <link ... /> tags. Returns an
     * array of the feeds referenced at $uri.
     *
     * @todo Allow findFeeds() to follow one, but only one, code 302.
     *
     * @param string $uri
     * @throws Zend_Feed_Exception
     * @return array
     */
    
public static function findFeeds($uri)
    {
        
// Get the HTTP response from $uri and save the contents
        
$client self::getHttpClient();
        
$client->setUri($uri);
        
$response $client->get();
        if (
$response->getStatus() !== 200) {
            throw new 
Zend_Feed_Exception("Failed to access $uri, got response code " $response->getStatus());
        }
        
$contents $response->getBody();
        
// Parse the contents for appropriate <link ... /> tags
        
@ini_set('track_errors'1);
        
$pattern '~(<link[^>]+)/?&gt;~i';
        
$result = @preg_match_all($pattern$contents$matches);
        @
ini_restore('track_errors');
        if (
$result === false) {
            throw new 
Zend_Feed_Exception("Internal error: $php_errormsg");
        }
        
// Try to fetch a feed for each link tag that appears to refer to a feed
        
$feeds = array();
        if (isset(
$matches[1]) && count($matches[1]) > 0) {
            foreach (
$matches[1] as $link) {
                
$xml = @simplexml_load_string("$link />");
                if (
$xml === false) {
                    continue;
                }
                
$attributes $xml->attributes();
                if (!isset(
$attributes['rel']) || strcasecmp($attributes['rel'], 'alternate')) {
                    continue;
                }
                if (!isset(
$attributes['type']) ||
                    !@
preg_match('~^application/(?:atom|rss)\+xml~'$attributes['type'])) {
                    continue;
                }
                if (!isset(
$attributes['href'])) {
                    continue;
                }
                try {
                    
$feed self::import($attributes['href']);
                } catch (
Exception $e) {
                    continue;
                }
                
$feeds[] = $feed;
            }
        }
        
// Return the fetched feeds
        
return $feeds;
    }
}

?>

Drupal Core:

<?php

function aggregator_parse_feed(&$data$feed) {
  global 
$items$image$channel;
  
// Unset the global variables before we use them:
  
unset($GLOBALS['element'], $GLOBALS['item'], $GLOBALS['tag']);
  
$items = array();
  
$image = array();
  
$channel = array();
  
// parse the data:
  
$xml_parser drupal_xml_parser_create($data);
  
xml_set_element_handler($xml_parser'aggregator_element_start''aggregator_element_end');
  
xml_set_character_data_handler($xml_parser'aggregator_element_data');
  if (!
xml_parse($xml_parser$data1)) {
    
$message t('Failed to parse RSS feed %site: %error at line %line.', array('%site' => theme('placeholder'$feed['title']), '%error' => xml_error_string(xml_get_error_code($xml_parser)), '%line' => xml_get_current_line_number($xml_parser)));
    
watchdog('aggregator'$messageWATCHDOG_WARNING);
    
drupal_set_message($message'error');
    return 
0;
  }
  
xml_parser_free($xml_parser);
  
/*
  ** We reverse the array such that we store the first item last,
  ** and the last item first.  In the database, the newest item
  ** should be at the top.
  */
  
$items array_reverse($items);
  foreach (
$items as $item) {
    unset(
$title$link$author$description);
    
// Prepare the item:
    
foreach ($item as $key => $value) {
      
// TODO: Make handling of aggregated HTML more flexible/configurable.
      
$value decode_entities(trim($value));
      if (
$key != 'LINK' && $key != 'GUID') {
        
$value filter_xss($value);
      }
      
$item[$key] = $value;
    }
    
/*
    ** Resolve the item's title.  If no title is found, we use
    ** up to 40 characters of the description ending at a word
    ** boundary but not splitting potential entities.
    */
    
if ($item['TITLE']) {
      
$title $item['TITLE'];
    }
    else {
      
$title preg_replace('/^(.*)[^\w;&].*?$/'"\\1"truncate_utf8($item['DESCRIPTION'], 40));
    }
    
/*
    ** Resolve the items link.
    */
    
if ($item['LINK']) {
      
$link $item['LINK'];
    }
    elseif (
$item['GUID'] && (strncmp($item['GUID'], 'http://'7) == 0)) {
      
$link $item['GUID'];
    }
    else {
      
$link $feed['link'];
    }
    
/*
    ** Try to resolve and parse the item's publication date.  If no
    ** date is found, we use the current date instead.
    */
    
if ($item['PUBDATE']) $date $item['PUBDATE'];                        // RSS 2.0
    
else if ($item['DC:DATE']) $date $item['DC:DATE'];                   // Dublin core
    
else if ($item['DCTERMS:ISSUED']) $date $item['DCTERMS:ISSUED'];     // Dublin core
    
else if ($item['DCTERMS:CREATED']) $date $item['DCTERMS:CREATED'];   // Dublin core
    
else if ($item['DCTERMS:MODIFIED']) $date $item['DCTERMS:MODIFIED']; // Dublin core
    
else $date 'now';
    
$timestamp strtotime($date); // strtotime() returns -1 on failure
    
if ($timestamp 0) {
      
$timestamp aggregator_parse_w3cdtf($date); // also returns -1 on failure
      
if ($timestamp 0) {
        
$timestamp time(); // better than nothing
      
}
    }
    
/*
    ** Save this item.  Try to avoid duplicate entries as much as
    ** possible.  If we find a duplicate entry, we resolve it and
    ** pass along it's ID such that we can update it if needed.
    */
    
if ($link && $link != $feed['link'] && $link != $feed['url']) {
      
$entry db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND link = '%s'"$feed['fid'], $link));
    }
    else {
      
$entry db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND title = '%s'"$feed['fid'], $title));
    }
    
aggregator_save_item(array('iid' => $entry->iid'fid' => $feed['fid'], 'timestamp' => $timestamp'title' => $title'link' => $link'author' => $item['AUTHOR'], 'description' => $item['DESCRIPTION']));
  }
  
/*
  ** Remove all items that are older than flush item timer:
  */
  
$age time() - variable_get('aggregator_clear'9676800);
  
$result db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d AND timestamp < %d'$feed['fid'], $age);
  if (
db_num_rows($result)) {
    
$items = array();
    while (
$item db_fetch_object($result)) {
      
$items[] = $item->iid;
    }
    
db_query('DELETE FROM {aggregator_category_item} WHERE iid IN ('implode(', '$items) .')');
    
db_query('DELETE FROM {aggregator_item} WHERE fid = %d AND timestamp < %d'$feed['fid'], $age);
  }
  return 
1;
}
?>

My Frontpage code:

<?php

while ($category db_fetch_object($result)) {
    
$output .= "<h3>$category->title</h3>\n";
    if (
variable_get('aggregator_summary_items'3)) {
      
$list = array();
      
$items db_query_range('SELECT i.title,i.description, i.timestamp, i.link, f.title as feed_title, f.link as feed_link FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON i.iid = ci.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE ci.cid = %d ORDER BY i.timestamp DESC, i.iid DESC'$category->cid0variable_get('aggregator_summary_items'3));
      while (
$item db_fetch_object($items)) {
        
$list[] = aggregator_frontpage_item($item);
      }
      
$output .= theme('item_list'$list);
    }
    
$output .= '<div class="links">'theme('links', array(l(t('more'), 'aggregator/categories/'$category->cid))) ."</div>\n";
  }
  
$output .= '';
  return 
$output;
}
function 
aggregator_frontpage_item($item) {
  
$output '<a href="'check_url($item->link) .'">'truncate_utf8($item->title,44,1,1) .'</a>';
  
$output .= '<br /><span class="age">'t('%age old', array('%age' => format_interval(time() - $item->timestamp))) .'</span>';
  if (
$item->feed_link) {
    
$output .= '&nbsp;|&nbsp;<span class="source"><a href="'$item->feed_link .'">'$item->feed_title .'</a></span>';
  }
  return 
$output ."\n";
}

?>

Hiveminds's picture
This article brought to you by the Hiveminds Magazine - Staff. Contact us if you want to post an article or announcement anonymously
 
a Visitor posted on: Thu, 2007-07-26 20:18.

Something to note is that the zend method only works in php5. The drupal method does not take advantage of anything php5 specific like zend does.

As of drupal 7, when drupal is php5+, I expect the code to get a lot simpler.

Just something to note.

a Visitor - posted on: Fri, 2008-01-11 11:14.

If anyone is interested I just released a pre-release version of my open source zend framework based cms. The source code is available at http://code.google.com/p/digitalus-site-manager/downloads/list

Bitrix Site manager - fast to create, easy to manage CMS Comparison Matrix
Put Your Site Here Developer Links
Web Developers Content Management Systems Joomla! Silverlight Web Developers Silverlight
 

Newsletter

Get updates on Hiveminds services, articles and downloads by signing up for the newsletter.

Editor's choice

Some of the better articles, stories and tutorials found at Hiveminds.

Find more

Find more of Hiveminds articles, stories, tutorials and user comments by searching.




Picked links

Hand picked websites and articles from around the web that provide quality reading.