如何用PHP把RDF內容插入Web站點之中
發表于:2007-07-14來源:作者:點擊數:
標簽:
名譽和巨大的財富 設想一個從最熱門的門戶網站獲得最新的新聞的站點。股票價格,天氣信息,新聞故事,線式討論組,軟件發布……所有這一切都將被動態更新,每小時一次,不需要任何手工干預。我們可以想象這隨之而來的站點訪問量,源源不斷的廣告收入以及網管
名譽和巨大的財富
設想一個從最熱門的門戶網站獲得最新的
新聞的站點。股票價格,天氣信息,新聞故事,線式討論組,軟件發布……所有這一切都將被動態更新,每小時一次,不需要任何手工干預。我們可以想象這隨之而來的站點訪問量,源源不斷的廣告收入以及網管大人所受到的“阿諛奉承”。
但是現在,停止幻想,開始閱讀,因為只要你密切關注此項技術,說不定你就能成為站點的主人。 對你的要求也只是稍許的想象力,一些聰明的PHP編碼和幾個免費的RSS文件。另外,很明顯還包括這篇文章剩下的九個部分。
有內容,就聯合成辛迪加(Have Content, Will Syndicate)
我們從最基本的開始——那么RSS究竟是什么鬼東西呢?
RSS(即RDF Site Summary)是一種格式,最早由Netscape公司設計,用于分發其門戶站點My.Netscape.Com上的內容的描述信息。自1997年被提出以來,幾經沉浮——可以點擊文章末尾的鏈接,了解一下RSS悠久復雜的歷史?,F在的穩定的版本是RSS1.0,符合RDF規范。這一版本可以說即輕便又功能齊全。
RSS使得網管及時公布和分發某一特定站點的特定位置的最新最有趣的內容的描述信息變的可能。 從新聞文章列表到股票市場數據或著是天氣預報,所有這些信息都可以通過結構良好的XML文檔來發布,從而也可以被任何XML分析器進行分析,處理和翻譯。
網站上最新信息的列表是經常更新的,而RSS使得這一列表的分發成為可能,也就為Web上簡易的內容辛迪加聯合打開了大門。想了解這其中的道理,請看下面這個簡單的例子:
站點A,屬新聞站點(“內容辛迪加組織者”),能夠每小時發布一個包含最新新聞列表以及相應鏈接的RSS文檔。 而這一RSS文檔可以被其它站點獲?。ㄈ缯军cB,“內容收集者”),分析并顯示在站點B的索引頁面上。 每次站點A發布一個新的RSS文檔,站點B的索引頁面都可以自動更新,以獲取最新的新聞。
這種方案對交易中的雙方機構都有效。 既然RSS文檔中的鏈接都指向站點A上相應的文章,那么站點A將迅速體驗到訪問量的增加。 而站點B的網管可以休假一個星期,因為他有辦法自動更新其站點上的索引頁面,而這一方法僅僅是把索引頁面與站點A發布的動態內容相連接而已。
有許多受歡迎的站點向公眾提供詳細的RSS或RDF新聞,如Freshmeat(http://www.freshmeat.net)和Slashdot(http://www.slashdot.org),當然還有其它許多站點。在這篇文章當中,我將廣泛的使用Freshmeat網站的RDF文件。需要說明的一點是,這里所談到的技術也可以應用于其它任何RSS1.0或RDF文件。
交換頻道(Switching Channels)
典型的RSS文檔包含一個由描述性元數據標記出來的資源列表(URLs),請看下面的例子:
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/">
<channel rdf:about="http://www.melonfire.com/">
<title>Trog</title>
<description>Well-written technical articles and
tutorials on Web technologies</description>
<link>http://www.melonfire.com/community/columns/trog/</link>
<items>
<rdf:Seq>
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=100" />
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=71" />
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=62" />
</rdf:Seq>
</items>
</channel>
<item
rdf:about="http://www.melonfire.com/community/columns/trog/article.php?i
d=10
0">
<title>Building A PHP-Based Mail Client (part 1)</title>
<link>http://www.melonfire.com/community/columns/trog/article.php?id=100
</li
nk>
<description>Ever wondered how Web-based mail clients
work? Find out here.</description>
</item>
<item
rdf:about="http://www.melonfire.com/community/columns/trog/article.php?i
d=71">
<title>Using PHP With XML (part 1)</title>
<link>http://www.melonfire.com/community/columns/trog/article.php?id=71<
/link>
<description>Use PHP's SAX parser to parse XML data and
generate HTML pages.</description>
</item>
<item
rdf:about="http://www.melonfire.com/community/columns/trog/article.php?i
d=62">
<title>A
clearcase/" target="_blank" >ccess Granted</title>
<link>http://www.melonfire.com/community/columns/trog/article.php?id=62<
/link>
<description>Precisely control access to information
with the mySQL grant tables.</description>
</item>
你可以看到,RDF文件由幾個界限分明的部分組成。首先是文檔序碼(prolog),
<?xml version="1.0" encoding="UTF-8"?>
然后是根元素中的名稱空間聲明。
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/">
接著是<channel>部分,這部分包含了RDF所要描述的頻道的一般信息。在上面的例子中,頻道是Melonfire網站的Trog專欄,專欄內容是新的技術文章和指南,每星期更新一次。
<channel rdf:about="http://www.melonfire.com/">
<title>Trog</title>
<description>Well-written technical articles and
tutorials on Web technologies</description>
<link>http://www.melonfire.com/community/columns/trog/</link>
<items>
<rdf:Seq>
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=100" />
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=71" />
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=62" />
</rdf:Seq>
</items>
</channel>
<channel>區包含了一個<items>區塊,<items>區塊又包含了文檔中描述的所有資源的一個順序列表。該列表通過一系列的<li />元素來表示。區塊中每一個資源都在后面的<item>區塊中有更詳細的描述。
<items>
<rdf:Seq>
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=100" />
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=71" />
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=62" />
</rdf:Seq>
</items>
還可以在其中放置一個<image>區塊,這樣你就可以發布頻道標志的URL。
所以為了肉,RSS1.0文檔中的每一個<item>區塊都更詳細地描述一個單獨的資源,包括標題,URL和資源描述。
<items>
<rdf:Seq>
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=100" />
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=71" />
<li
rdf:resource="http://www.melonfire.com/community/columns/trog/article.ph
p?id
=62" />
</rdf:Seq>
</items>
在這個例子里,<item>區塊描述了Ttrog“頻道”中單獨的一篇文章,并為這篇文章提供了描述和標題,以及URL。內容收集者可以利用URL創建“向后”鏈接。
你看得到,RSS1.0文件相當地直觀明了,不管是手工,還是通過編程,都非常容易創建。上面的例子和解釋僅僅是說明性質的,通常,你可以用RSS1.0和RDF做更多的事情。你最好看一下文章末尾提供的鏈接,以獲取更多的信息。不過在這之前,我們再花幾分鐘討論一如何將RSS1.0文檔插入到你自己的Web站點之中。
既然從技術上講,RSS是結構良好的XML文檔,所以可以用標準的XML編程技術來處理它。主要有兩種技術:SAX(the Simple API for XML)和DOM(the Document Object Model)。
SAX分析器工作時遍歷整個XML文檔,在遇到不用類型的標記時調用特定的函數。比如,調用特定函數處理一個開始標記,調用另一個函數處理一個結束標記,再調用一個函數處理兩者之間的數據。分析器的職責僅僅是順序遍歷這個文檔。而它所調用的函數負責處理發現的標記。一旦一個標記被處理完畢,分析器繼續分析文檔中的下一個元素,這一過程不斷重復。
另一方面,DOM分析器工作是把整個XML文檔讀進內存當中,并將之轉換成一種分層的樹型結構。而且為訪問不同的樹結點(以及結點所附的內容)提供了API。遞歸處理方式加上API函數使得
開發者能夠區分不同類型的結點(元素,屬性,字符數據,注釋等),同時根據文檔樹的結點類型和結點深度,使得執行不同的動作成為可能。
SAX和DOM分析器幾乎支持每一種語言,包括你我的最愛——PHP。我將在這篇文章中利用PHP的SAX分析器處理RDF的例子。 當然,使用DOM分析器也同樣很容易。
讓我們看這個簡單的例子,把它記在腦海里。下面是一個我將要使用的RDF文件,這個文件直接選自http://www.freshmeat.net/ :
<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
>
<channel rdf:about="http://freshmeat.net/">
<title>freshmeat.net</title>
<link>http://freshmeat.net/</link>
<description>freshmeat.net maintains the Web's largest index of
Unixand cross-platform open source software. Thousands of applications are
meticulously cataloged in the freshmeat.net database, and links to new
code are added daily.</description>
<dc:language>en-us</dc:language>
<dc:subject>Technology</dc:subject>
<dc:publisher>freshmeat.net</dc:publisher>
<dc:creator>freshmeat.net contributors</dc:creator>
<dc:rights>Copyright (c) 1997-2002 OSDN</dc:rights>
<dc:date>2002-02-11T10:20+00:00</dc:date>
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://freshmeat.net/releases/69583/" />
<rdf:li rdf:resource="http://freshmeat.net/releases/69581/" />
<!-- and so on -->
</rdf:Seq>
</items>
<image rdf:resource="http://freshmeat.net/img/fmII-button.gif" />
<textinput rdf:resource="http://freshmeat.net/search/" />
</channel>
<image rdf:about="http://freshmeat.net/img/fmII-button.gif">
<title>freshmeat.net</title>
<url>http://freshmeat.net/img/fmII-button.gif</url>
<link>http://freshmeat.net/</link>
</image>
<item rdf:about="http://freshmeat.net/releases/69583/">
<title>sloop.splitter 0.2.1</title>
<link>http://freshmeat.net/releases/69583/</link>
<description>A real time sound effects program.</description>
<dc:date>2002-02-11T04:52-06:00</dc:date>
</item>
<item rdf:about="http://freshmeat.net/releases/69581/">
<title>apacompile 1.9.9</title>
<link>http://freshmeat.net/releases/69581/</link>
<description>A full-featured Apache compilation HOWTO.</description>
<dc:date>2002-02-11T04:52-06:00</dc:date>
</item>
<!-- and so on -->
</rdf:RDF>
下面是分析這一文檔并顯示其中數據的PHP腳本:
<?php
// XML file
$file = "fm-releases.rdf";
// set up some variables for use by the parser
$currentTag = "";
$flag = "";
// create parser
$xp = xml_parser_create();
// set element handler
xml_set_element_handler($xp, "elementBegin", "elementEnd");
xml_set_character_data_handler($xp, "characterData");
xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, TRUE);
// read XML file
if (!($fp = fopen($file, "r")))
{
die("Could not read $file");
}
// parse data
while ($xml = fread($fp, 4096))
{
if (!xml_parse($xp, $xml, feof($fp)))
{
die("XML parser error: " .
xml_error_string(xml_get_error_code($xp)));
}
}
// destroy parser
xml_parser_free($xp);
// opening tag handler
function elementBegin($parser, $name, $attributes)
{
global $currentTag, $flag;
// export the name of the current tag to the global scope
$currentTag = $name;
// if within an item block, set a flag
if ($name == "ITEM")
{
$flag = 1;
}
}
// closing tag handler
function elementEnd($parser, $name)
{
global $currentTag, $flag;
$currentTag = "";
// if exiting an item block, print a line and reset the flag
if ($name == "ITEM")
{
echo "<hr>";
$flag = 0;
}
}
// character data handler
function characterData($parser, $data)
{
global $currentTag, $flag;
// if within an item block, print item data
if (($currentTag == "TITLE" || $currentTag == "LINK" ||
$currentTag ==
"DESCRIPTION") && $flag == 1)
{
echo "$currentTag: $data <br>";
}
}
?>
看不明白? 別著急,后面將會作出解釋。
捕獲旗標
這段腳本首先要做的是設定一些全局變量:
// XML file
$file = "fm-releases.rdf";
// set up some variables for use by the parser
$currentTag = "";
$flag = "";
$currentTag變量保存是分析器當前處理的元素的名稱——你很快就會看到為什么需要它。
因為我的最終目的是顯示頻道中的每一個單獨的條目(item),并且帶有鏈結。另外還要知道分析器什么時候退出了<channel></channel>區塊,什么時候又進入了文檔的 <item></item>部分。再說我用的是SAX分析器,它按順序方式工作,沒有任何分析器API可供使用,無法知道文檔樹中的深度和位置。所以,我不得不自己發明一個機制來做這件事——這就是引入$flag變量的原因。
$flag變量將用于判斷分析器是在<channel>區塊還是在<item>區塊里面。
下一步要做的是初始化SAX分析器,并開始分析RSS文檔。
// create parser
$xp = xml_parser_create();
// set element handler
xml_set_element_handler($xp, "elementBegin", "elementEnd");
xml_set_character_data_handler($xp, "characterData");
xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, TRUE);
// read XML file
if (!($fp = fopen($file, "r")))
{
die("Could not read $file");
}
// parse data
while ($xml = fread($fp, 4096))
{
if (!xml_parse($xp, $xml, feof($fp)))
{
die("XML parser error: " .
xml_error_string(xml_get_error_code($xp)));
}
}
// destroy parser
xml_parser_free($xp);
這段代碼簡單明了,其中的注釋已經解釋的足夠清楚了。xml_parser_create()函數建立一個分析器實例,并將之賦給句柄$xp。接著再創建回調函數處理開標記和閉標記,以及二者之間的字符數據。最后,xml_parse()函數聯合多次fread()調用,讀取RDF文件并分析它。
在文檔中,每次遇到開標記,開標記處理器elementBegin()就會被調用。
// opening tag handler
function elementBegin($parser, $name, $attributes)
{
global $currentTag, $flag;
// export the name of the current tag to the global scope
$currentTag = $name;
// if within an item block, set a flag
if ($name == "ITEM")
{
$flag = 1;
}
}
這個函數以當前標記的名稱和屬性作為起參數。標記名稱被賦值給全局變量$currentTag。如果,這個開標記是<item>,那么把$flag變量置1。
同樣,如果遇到閉標記,那么閉標記處理器elementEnd()將被調用。
// closing tag handler
function elementEnd($parser, $name)
{
global $currentTag, $flag;
$currentTag = "";
// if exiting an item block, print a line and reset the flag
if ($name == "ITEM")
{
echo "<hr>";
$flag = 0;
}
}
閉標記處理函數也是以標記名稱作為其參數。如果是遇到的是一個為</item>的閉標記,變量$flag的值重置為0,并把變量$currentTag的值清空。
那么,如何處理標記之間的字符數據呢? 這才是我們的興趣所在。先向字符數據處理器characterData()打個招呼吧。
// character data handler
function characterData($parser, $data)
{
global $currentTag, $flag;
// if within an item block, print item data
if (($currentTag == "TITLE" || $currentTag == "LINK" ||
$currentTag ==
"DESCRIPTION") && $flag == 1)
{
echo "$currentTag: $data <br>";
}
}
現在你可以看一下傳給這個函數的參數,你會發現它只接收了開標記和閉標記之間的數據,而根本不知道分析器當前正在處理哪個標記。而這正事我們一開始就引入全局變量$currentTag的原因。
如果$flag變量的值為1,也就是說如果分析器當前處于<item></itme>區塊之間,那么當前被處理的元素,不管是<title>,<link>還是<description>,數據都被打印到輸出設備上(在這里,輸出設備是Web瀏覽器),并在每個元素的輸出后面加上換行符<br>。
整個RDF文檔就是以這種順序方式處理,每發現一個<item>標記就顯示一定的輸出。你可以看一下下面的運行結果:
筑巢時間(Nesting Time)
前面的例子只是用來說明問題的。如果你真想把RDF內容插入到Web站點當中,就需要把事情做的更好一些。所以把前面的腳本的作了改進,新增了一些東西,從而簡化格式化RDF數據的任務。
<html>
<head>
<basefont face="Verdana">
</head>
<body>
<table border="0" cellspacing="5" cellpadding="5">
<tr>
<td><b>New releases on freshmeat.net today:</b></td>
</tr>
<?php
// XML file
$file = "http://www.freshmeat.net/backend/fm-releases.rdf";
// set up some variables for use by the parser
$currentTag = "";
$flag = "";
$count = 0;
// this is an associative array of channel data with keys ("title",
"link",
"description")
$channel = array();
// this is an array of arrays, with each array element representing an
<item> // each outer array element is itself an associative array
// with keys ("title", "link", "description")
$items = array();
// opening tag handler
function elementBegin($parser, $name, $attributes)
{
global $currentTag, $flag;
$currentTag = $name;
// set flag if entering <channel> or <item> block
if ($name == "ITEM")
{
$flag = 1;
}
else if ($name == "CHANNEL")
{
$flag = 2;
}
}
// closing tag handler
function elementEnd($parser, $name)
{
global $currentTag, $flag, $count;
$currentTag = "";
// set flag if exiting <channel> or <item> block
if ($name == "ITEM")
{
$count++;
$flag = 0;
}
else if ($name == "CHANNEL")
{
$flag = 0;
}
}
// character data handler
function characterData($parser, $data)
{
global $currentTag, $flag, $items, $count, $channel;
$data = trim(htmlspecialchars($data));
if ($currentTag == "TITLE" || $currentTag == "LINK" ||
$currentTag ==
"DESCRIPTION")
{
// add data to $channels[] or $items[] array
if ($flag == 1)
{
$items[$count][strtolower($currentTag)] .=
$data;
}
else if ($flag == 2)
{
$channel[strtolower($currentTag)] .= $data;
}
}
}
// create parser
$xp = xml_parser_create();
// set element handler
xml_set_element_handler($xp, "elementBegin", "elementEnd");
xml_set_character_data_handler($xp, "characterData");
xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, TRUE);
xml_parser_set_option($xp, XML_OPTION_SKIP_WHITE, TRUE);
// read XML file
if (!($fp = fopen($file, "r")))
{
die("Could not read $file");
}
// parse data
while ($xml = fread($fp, 4096))
{
if (!xml_parse($xp, $xml, feof($fp)))
{
die("XML parser error: " .
xml_error_string(xml_get_error_code($xp)));
}
}
// destroy parser
xml_parser_free($xp);
// now iterate through $items[] array
// and print each item as a table row
foreach ($items as $item)
{
echo "<tr><td><a href=" . $item["link"] . ">" . $item["title"] .
"</a><br>" . $item["description"] . "</td></tr>"; }
?>
</table>
</body>
</html>
與先前的那段的主要區別在于,這段腳本創建了兩個數組,用于保存分析過程中所提取的信息。其中,$channel是聯合性數組(associative array),存放被處理的頻道的基本描述信息,而$items是一個二維數組,包含關于單獨的頻道條目(channel intems)的信息。$items數組中的每一個元素本身又是一個聯合性數組,包含title,URL和description關鍵字。$items數組中元素總數與RDF文檔中的<item>區塊總數相同。
還需注意$flag變量的變化,根據被處理的是<channel></channel>區塊還是<item></item>區塊,它現在保存兩個值。這一點很有必要,因為只有這樣,分析器才能把信息放入正確的數組里面。
一旦文檔分析完畢,事情就簡單了——遍歷$items 數組,以表格形式打印其中的每一個條目(item)。
返回到類(Back To Class)
既然你有這么大的權力,那么究竟為什么要把自己限制在僅僅是單個的RDF來源呢?就象我早先說過的一樣,大多數主要的站點都經常為他們所提供的內容做快照。其實將所有這些不同的來源插入到你的站點當中是相當簡單的。讓我們看看是如何做的。
首先,我們把前面例子中的代碼模塊化。這樣一來,你就無須為每一個單個的來源都一遍又一遍的重寫相同的代碼了。簡化的方法就是將之打包成類,再把這個類包含到我的PHP腳本當中。
類代碼如下:
<?
class RDFParser
{
//
// variables
//
// set up local variables for this class
var $currentTag = "";
var $flag = "";
var $count = 0;
// this is an associative array of channel data with keys
("title", "link", "description")
var $channel = array();
// this is an array of arrays, with each array element
representing an <item>
// each outer array element is itself an associative array
// with keys ("title", "link", "description")
var $items = array();
//
// methods
//
// set the name of the RDF file to parse
// this is usually a local file
// you may set it to a remote file if your PHP build supports
URL fopen()
function setResource($file)
{
$this->file = $file;
}
// parse the RDF file set with setResource()
// this populates the $channel and $items arrays
function parseResource()
{
// create parser
$this->xp = xml_parser_create();
// set object reference
xml_set_object($this->xp, $this);
// set handlers and parser options
xml_set_element_handler($this->xp, "elementBegin",
"elementEnd");
xml_set_character_data_handler($this->xp,
"characterData");
xml_parser_set_option($this->xp,
XML_OPTION_CASE_FOLDING, TRUE);
xml_parser_set_option($this->xp, XML_OPTION_SKIP_WHITE,
TRUE);
// read XML file
if (!($fp = fopen($this->file, "r")))
{
die("Could not read $this->file");
}
// parse data
while ($xml = fread($fp, 4096))
{
if (!xml_parse($this->xp, $xml, feof($fp)))
{
die("XML parser error: " .
xml_error_string(xml_get_error_code($this->xp)));
}
}
// destroy parser
xml_parser_free($this->xp);
}
// opening tag handler
function elementBegin($parser, $name, $attributes)
{
$this->currentTag = $name;
// set flag if entering <channel> or <item> block
if ($name == "ITEM")
{
$this->flag = 1;
}
else if ($name == "CHANNEL")
{
$this->flag = 2;
}
}
// closing tag handler
function elementEnd($parser, $name)
{
$this->currentTag = "";
// set flag if exiting <channel> or <item> block
if ($name == "ITEM")
{
$this->count++;
$this->flag = 0;
}
else if ($name == "CHANNEL")
{
$this->flag = 0;
}
}
// character data handler
function characterData($parser, $data)
{
$data = trim(htmlspecialchars($data));
if ($this->currentTag == "TITLE" || $this->currentTag ==
"LINK" || $this->currentTag == "DESCRIPTION")
{
// add data to $channels[] or $items[] array
if ($this->flag == 1)
{
$this->items[$this->count][strtolower($this->currentTag)] .= $data;
}
else if ($this->flag == 2)
{
$this->channel[strtolower($this->currentTag)] .= $data;
}
}
}
// return an associative array containing channel information
// (the $channel[] array)
function getChannelInfo()
{
return $this->channel;
}
// return an associative array of arrays containing item
information
// (the $items[] array)
function getItems()
{
return $this->items;
}
}
?>
如果你對PHP類較為熟悉的話,那么理解這段代碼是相當容易的。如果不太懂的話,那么請直接跳到文章末尾的鏈接部分,看一篇關于類工作原理的好文章。然后在回來繼續閱讀上面的代碼。
在使用這個類之前,我要特別花幾分鐘指出其中的一行代碼——即上面對xml_set_object()函數調用的那一行。
現在的問題是如何使用這個類實際生成具有多個內容來源的Web頁。
<?
include("class.RDFParser.php");
// how many items to display in each channel
$maxItems = 5;
?>
<html>
<head>
<basefont face="Verdana">
<body>
<table width="100%" border="0" cellspacing="5" cellpadding="5"> <tr>
<!-- first cell -->
<td valign=top align=left>
<font size="-1">
<?
// get and parse freshmeat.net channel
$f = new RDFParser();
$f->setResource("http://www.freshmeat.net/backend/fm-releases.rdf");
$f->parseResource();
$f_channel = $f->getChannelInfo();
$f_items = $f->getItems();
// now format and print it...
?>
The latest from <a href=<? echo $f_channel["link"]; ?>><? echo
$f_channel["title"]; ?></a> <br> <ul> <? // iterate through items array
for ($x=0; $x<$maxItems; $x++) {
if (is_array($f_items[$x]))
{
// print data
$item = $f_items[$x];
echo "<li><a href=" . $item["link"] . ">" .
$item["title"] . "</a>";
}
}
?>
</ul>
</font>
</td>
<!-- second cell -->
<td align=center width=50%>
<i>Primary page content here</i>
</td>
<!-- third cell -->
<td valign=top align=left>
<font size="-1">
<?
// get and parse slashdot.org channel
$s = new RDFParser();
$s->setResource("http://slashdot.org/slashdot.rdf");
$s->parseResource();
$s_channel = $s->getChannelInfo();
$s_items = $s->getItems();
// now format and print it...
?>
The latest from <a href=<? echo $s_channel["link"]; ?>><? echo
$s_channel["title"]; ?></a> <br> <ul> <? // iterate through items array
for ($x=0; $x<$maxItems; $x++) {
if (is_array($s_items[$x]))
{
// print data
$item = $s_items[$x];
echo "<li><a href=" . $item["link"] . ">" .
$item["title"] . "</a>";
}
}
?>
</ul>
</font>
</td>
</tr>
</table>
</body>
</head>
</html>
這段代碼相當簡單。一旦你用“new”關鍵字生成一個類的實例,
$f = new RDFParser();
那么就可以用類方法來設置要分析的RDF文件的位置,
$f->setResource("http://www.freshmeat.net/backend/fm-releases.rdf");
$f->parseResource();
并且獲取$channel和$items數組,以用于后面的處理。
<?
$f_channel = $f->getChannelInfo();
$f_items = $f->getItems();
?>
The latest from <a href=<? echo $f_channel["link"]; ?>><? echo
$f_channel["title"]; ?></a> <br> <ul> <? // iterate through items array
for ($x=0; $x<$maxItems; $x++) {
if (is_array($f_items[$x]))
{
// print data
$item = $f_items[$x];
echo "<li><a href=" . $item["link"] . ">" .
$item["title"] . "</a>";
}
}
?>
</ul>
每次你重新裝入上面的腳本,相應的RDF文件就會被從特定的位置上取來,經過分析之后,按要求的格式顯示出來。
如果你站點具有高的訪問量,你就可能覺得我們的辛苦無意義之極,尤其是當所用的RDF數據更新地沒有那么快時,情況更糟。 在這種情況下,或許探究一下在本地緩存RDF數據才是較明智的做法:要么擴展上面的例子程序,在其中加入緩存功能;要么每閣幾個小時都花很長的時間下載一個最新RDF文件的本地副本到你的Web
服務器上,然后使用這個本地副本,而不是那個“活”的(the “live” one)。
免費午餐(A Free Lunch)
上面我所寫的那個類也是很基本的,是拿來說明問題的,或許也可以用于低訪問量的站點。如果你想尋找一些更專業的東西,去網上吧,那里有許多的開放源碼的RDF分析器,他們帶有各種附加的功能(包括緩存)。 那么就讓我們看一些如何運用這些分析器的例子吧。
第一個要講的是由Stefan Saasen 為fase4網站開發的RDF分析器類,可以從http://www.fase4.com/rdf/上免費下載。這是一個功能非常齊全的RDF分析器,支持緩存和通過代理認證。下面是如何使用它的例子:
<html>
<head>
<style type="text/css">
body {font-family: Verdana; font-size: 11px;}
.fase4_rdf {font-size: 13px; font-family: Verdana} .fase4_rdf_title
{font-size: 13px; font-weight : bolder;}
</style>
</head>
<body>
<?
// include class
include("rdf.class.php");
// instantiate object
$rdf = new fase4_rdf;
// set number of items to display
$rdf->set_max_item(5);
// set RDF engine options
$rdf->use_dynamic_display(true);
$rdf->set_Options( array("image"=>"hidden", "textinput"=>"hidden") );
// parse and display data
$rdf->parse_RDF("http://www.freshmeat.net/backend/fm-releases.rdf");
$rdf->finish();
?>
</body>
</html>
另一個要介紹的是由Jason Williams開發的PHP RDF分析器,可以在http://www.nerdzine.net/php_rdf/下載。這是一個未經任何任何渲染的PHP類,實現了一些基本的方法。但是它包含的大量的屬性,可以讓你用來安排經過處理的數據,直到你滿意為止。
--------------------------------------------------------------------------------
<html>
<head>
<basefont face="Verdana">
</head>
<body link="Red" vlink="Red" alink="Red">
<?
include("rdf_class.php");
// this needs to be a local file
$f = new rdfFile("./fm-releases.rdf");
$f->parse(True);
$f->ReturnTable(True, "black", "white", "100%");
?>
</body>
</html>
關于這些類的文檔在他們各自的網站上都有介紹。
增加一點樣式(Adding A Little Style)
萬一你討厭遍歷那些PHP數組并把他們用HTML標記出來的方式,那么你也可以選擇通過使用XSLT樣式單來格式化和顯示這些數據。PHP4 .1可以通過新的XSLT API來支持Sablotron XSLT處理器,新的API可以用來合并一個XSLT樣式單和一個XML文檔(在這里,即RDF文件),從而非常容易的把XML標記轉化為瀏覽器可讀的HTML標記。
我不想在這上面講的太細,你可以看一下PHP 手冊,或者關注一下本文后面的鏈接以獲得更加詳細的信息。不過我還是會給出一個簡單的例子來說明這個問題。首先,給出樣式單文件:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rss="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/" version="1.0">
<!-- main page -->
<xsl:template match="/rdf:RDF">
<html>
<head>
<basefont face="Arial" size="2"/>
</head>
<body>
<xsl:apply-templates select="rss:channel" />
<ul>
<xsl:apply-templates select="rss:item" />
</ul>
</body>
</html>
</xsl:template>
<!-- channel -->
<xsl:template match="rss:channel">
<b>
<a>
<xsl:attribute name="href"><xsl:value-of select="rss:link"
/></xsl:attribute>
<xsl:value-of select="rss:title" />
</a>
</b>
</xsl:template>
<!-- item -->
<xsl:template match="rss:item">
<li />
<a>
<xsl:attribute name="href"><xsl:value-of select="rss:link"
/></xsl:attribute>
<xsl:value-of select="rss:title" />
</a>
<br />
<xsl:value-of select="rss:description" />
</xsl:template>
</xsl:stylesheet>
下面是PHP腳本,用來把上面的樣式單和講的“鮮肉”RDF文檔合并起來,生成一個HTML頁面:
<?php
// XML file
// this needs to be a local file
$xml = "fm-releases.rdf";
// XSLT file
$xslt = "fm.xsl";
// create a new XSLT processor
$xp = xslt_create();
// transform the XML file as per the XSLT stylesheet
// return the result to $result
$result = xslt_process($xp, $xml, $xslt);
if ($result)
{
// print it
echo $result;
}
// clean up
xslt_free($xp);
?>
我想這相當簡單,無須再加說明。兩個文檔合并在一起,產生了下面的“合成物”:
這是把RDF數據轉換成瀏覽器可讀的HTML的另一種方法,或許簡單一些(盡管并不是最理想的)。不過要注意一點,你需要運行一個外部程序來定期更新你的RDF文件的本地副本,原因在于PHP XSLT處理器訪問遠端文件可能有困難。
家庭作業(Homework)
如果你有興趣對這些文中談到的和沒談到的技術了解的更多,你可以考慮訪問下面的鏈接:
RSS 1.0 規范:http://www.purl.org/rss/1.0/
RSS 發展史:http://backend.userland.com/stories/rss091
W3C 網站關于RDF的內容:http://www.w3.org/RDF/
PHP中關于SAX和DOM編程的討論:http://www.devshed.com/Server_Side/XML/XMLwithPHP
用PHP實現XSLT轉換的討論:http://www.devshed.com/Server_Side/XML/XSLTrans
關于PHP類的討論:http://www.devshed.com/Server_Side/PHP/BackToClass
XML基礎的討論:http://www.devshed.com/Server_Side/XML/XMLBasic
XSLT基礎的討論: http://www.devshed.com/Server_Side/XML/XSLBasics
PHP手冊中關于SAX函數的內容: http://www.php.net/manual/en/ref.xml.php
PHP手冊中關于XSLT函數的內容:http://www.php.net/manual/en/ref.xslt.php
下次再見了……保重身體呀!
注意:本文中的所有例子都在
Linux/i386,Apache1.3.12,,PHP4.1.1環境下通過檢驗。
這些例子僅用于說明問題,不是為了某一個產品。Melonfire網站不對文中的源代碼提供任何授權或支持。(本文版權為Melonfire網站所有, 原文出處:http://www.devshed.com/Server_Side/PHP/PHPRDF/page1.html)
原文轉自:http://www.anti-gravitydesign.com