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[^>]+)/?>~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, $data, 1)) {
$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', $message, WATCHDOG_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->cid, 0, variable_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 .= ' | <span class="source"><a href="'. $item->feed_link .'">'. $item->feed_title .'</a></span>';
}
return $output ."\n";
}
?>
This article brought to you by the
Hiveminds Magazine - Staff.
Contact us if you want to post an article or announcement anonymously