实现服务器端路径
现在,您已经了解了纯客户端解决方案,它通过调用 Yahoo RSS 提要,遍历结果 XML 的 DOM 对象,在 JavaScript 中显示 mapplet 的 RSS 摘要。现在该开始探讨服务器端解决方案。它包括在 JavaScript 中对 Web 服务器调用 PHP 脚本,然后调用 Yahoo 服务本身,使用 XSLT 将 RSS 转换为 KML 覆盖层,mapplet 将在地图上显示。
创建服务器端 PHP 脚本
服务器端的功能首先调用一个 PHP 脚本 weather.php,该脚本用来返回一个表示当前天气的 KML 覆盖层。首先创建 weather.php,如清单 13 所示,然后把它放到可公共存取的 Web 服务器上。
现在,将注意力转向服务器端功能路径,当在 mapplet 中选中 Server-side XSLT 单选按钮并改变地图的位置时,该功???路径被触发。它调用 fetchServerXsltResults(),如清单 13 所示。
清单 13. fetchServerXsltResults()
function fetchServerXsltResults(zipcode, units) {
var kmlUrl = "http://YOUR-SERVER/weather.php?v=" + Math.round(Math.random()
* 10000000000);
var kml = new GGeoXml(kmlUrl);
map.addOverlay(kml);
}
|
现在的目标是从服务器上加载 KML 文件,并将它覆盖到地图上。所以,首先该函数创建一个 URL,用来获得服务器上的 KML 数据。Google 将 KML 文件放入缓存,所以,为了确保每次都是从服务器上加载 KML,而不是从 Google 的缓存中加载,fetchServerXsltResults() 将 0 到 10000000000 之间的随机数作为虚设的请求参数添加到 URL中。
然后,使用 URL 创建一个 GGeoXml 对象。GGeoXML 实现 GOverlay 接口,意味着可以将该对象传递到 Gmap2.addOverlay() 中。与 Google Maps API 不同,mapplets API 只允许使用标准 GOverlay 类,比如 GgeoXml,而不能使用自定义的 GOverlay 实现。为了在地图上覆盖 KML 数据,GGeoXml 加载一个 XML 文件,并用它调用 addOverlay。
实现服务器端功能
这将实现服务器端功能,可以使用 PHP 和 XSLT 将 Yahoo! Weather RSS 提要转换为在 Google Map 上覆盖的 KML 文件。继续创建 weather.php,如清单 14 所示,将其放在公共 Web 服务器上。
清单 14. weather.php
<?php
require_once('yahoo-weather-rss.php');
require_once('XsltChainController.php');
if (! isset($_REQUEST['zipcode'])) {
die ("zipcode request parameter required");
}
if (! isset($_REQUEST['units'])) {
die ("units request parameter required - either c or f");
}
$weatherRss = readYahooWeatherRss ($_REQUEST['zipcode'], $_REQUEST['units']);
$controller = new XsltChainController
(array ('yahoo-rss-to-simple-weather.xsl',
'simple-weather-to-kml.xsl'),
'CDATA_START', 'CDATA_END');
echo $controller->echoTransformedXml ($weatherRss, true, false);
?>
|
weather.php 文件生成一个 KML 文件,mapplet 将该文件覆盖到 Google Map 上。首先,要确保存在需要的请求参数,使用邮编和单位表示(c 或 f)来调用 Yahoo RSS 提要。然后,调用 readYahooWeatherRss(),传递这些参数,并收回 Yahoo Weather RSS 提要的 XML。接下来,创建 XsltChainController 类的一个实例,指定一组 XSLT 样式表文件以逐次应用到源 XML 文档中。另外,为 XsltChainController 提供两个标记,CDATA_START 和 CDATA_END。这两个标记允许样式表生成 CDATA 部分,否则,XSLT 样式表不能生成 CDATA 部分(生成 XSLT 样式表本身时将更详细地介绍这些内容)。最后,weather.php 使用 RSS 提要调用 XsltChainController 的 echoTransformedXml() ,将结果 XML 回传到标准输出。
在 PHP 中调用 Yahoo Weather RSS 提要
现在,需要一个主要的驱动脚本,创建清单 15 所示的 yahoo-weather-rss.php。
清单 15. yahoo-weather-rss.php
<?php
require_once('readUrl.php');
define ('YAHOO_RSS_FEED', 'http://weather.yahooapis.com/forecastrss');
function readYahooWeatherRss ($zipcode, $units) {
if ($zipcode == null) {
throw new Exception ("No zipcode specified");
}
if ($units == null) {
throw new Exception ("No units specified");
}
if (! (in_array($units, array('f', 'c')))) {
throw new Exception ("Invalid units specified (can be 'c' or 'f'): $units");
}
return readUrl(YAHOO_RSS_FEED . "?p=$zipcode&u=$units");
}
?>
|
yahoo-weather-rss.php 文件是非常基础的,它提供一个简单的函数 readYahooWeatherRss()。readYahooWeatherRss() 确保邮编和单位参数是正确的,然后,返回使用这些参数读取 Yahoo Weather RSS 提要的结果。为获得 Yahoo RSS 提要的结果,调用清单 16 指定的 readUrl() 函数。
清单 16. readUrl.php
<?php
function readUrl($url) {
if (ini_get('allow_url_fopen') == '1') {
return readUrlWithFopen($url);
}
else if (function_exists('curl_init')) {
return readUrlWithCurl($url);
}
else {
throw new Exception ("readUrl: neither fopen url wrapppers nor
CURL support are enabled.");
}
}
?>
|
readUrl.php 文件提供一个通用函数,使用 fopen 或 CURL 读取 URL 的内容,具体方法取决于当前的服务器配置。首选方法是使用 fopen()。可以通过 'allow_url_fopen' PHP 初始文件设置检测,如果 PHP 安装过程中未启用 fopen HTTP wrapper(检查方法是查看 ‘allow_url_fopen’ PHP ini 文件设置),则使用 CURL 读取 URL 内容。如果检测 ‘curl_init’ 函数并不存在,说明 CURL 也没有启用,则函数因为不能继续处理而抛出一个异常。
如果启用了 fopen() HTTP
wrapper,则 readUrl() 调用 readUrlWithFopen() 函数,如清单 17 所示,因此,可以用 URL 代替本地文件路径传递给 fopen(),该函数将把它作为文件读取。
清单 17. readUrlWithFopen()
function readUrlWithFopen($url) {
$contents = file_get_contents($url);
if (! $contents) {
throw new Exception ("readUrl: Couldn't read url: $url");
}
return $contents;
}
|
readUrlWithFopen() 使用 file_get_contents() 函数读取 URL 的内容。然后,确保函数从 URL 中读取某些内容,如果内容为空,则抛出异常。否则,返回 URL 的内容。
另外一个选择是清单 18 所示的 readUrlWithCurl()。
清单 18. readUrlWithCurl()
function readUrlWithCurl($url) {
$ch = curl_init();
try {
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$content = curl_exec($ch);
curl_close($ch);
return $content;
} catch (Exception $e) {
curl_close($ch);
throw new Exception ("Couldn't read url using CURL: $e");
}
}
|
readUrlWithCurl() 使用 curl_init() 创建 CURL 处理程序。然后,设置要调用的 URL,指定不返回 HTTP 头部,只返回响应的内容,并指定返回内容而不是将其直接写到标准输出中。设置选项后,调用 curl_exec() 和 curl_close() 读取流,然后关闭流,这样可以捕获任何异常,并确保关闭 CURL 流以避免资源一直处于打开状态。
在 PHP 5 中使用 XSL 模块实现 XSLT 样式表转换的方法
脚本检索到 Yahoo Weather RSS 提要的内容后,可以将其转化为更容易被使用的内容。为此,创建一个 XsltChainController 实例,如清单 19 所示。
清单 19. XsltChainController
class XsltChainController {
private $stylesheets;
private $cdataStart;
private $cdataEnd;
public function __construct ($stylesheets, $cdataStart=null, $cdataEnd=null) {
$this->stylesheets = $stylesheets;
$this->cdataStart = $cdataStart;
$this->cdataEnd = $cdataEnd;
if ($cdataStart != null && $cdataEnd == null ||
$cdataEnd != null && $cdataStart == null) {
throw new Exception ("XsltChainController: if providing cdataStart or cdataEnd,
must provide both. Only one provided.");
}
}
|
XsltChainController 将一系列 XSLT 样式表应用到源 XML 文档中。它的构造函数接收一组样式表文件路径、可选的 $cdataStart 和 $cdataEnd 参数。这些指定标记可能出现在样式表中,用来指定结果 XML 中 CDATA 部分的开始和结束位置。这样就解决了一个问题:如果样式表要通过转换生成 CDATA 部分的主体,在 XSLT 样式表的输出中就不能包含真正的 CDATA 部分(与 <![CDATA[...stuff...]] 一样)。XSLT 处理程序发现 CDATA 部分后,就会对内容进行转义,忽略 CDATA 中要生成转换内容的 XSLT 元素。解决方法是在 XSLT 样式表中 CDATA 部分的开始处和结束处插入一个标记,在 XSLT 处理完成后,用封闭的处理程序代替实际的 CDATA 开始和结束标记。
构造函数只需配置控制器。接下来,添加 echoTransformedXml() 函数完成这一任务,如清单 20 所示。
清单 20. echoTransformedXml()
public function echoTransformedXml ($xml, $stripCdata=false) {
if ($stripCdata) {
$xml = $this->stripCdata($xml);
}
$transformedDom = $this->applyStylesheets ($xml);
echo ($this->cdataStart
? $this->replaceCdataMarkers ($transformedDom->saveXML())
: $transformedDom->saveXML());
}
protected function stripCdata($xml) {
return str_ireplace (array('<![CDATA[', ']]>'),
array ('', ''),
$xml);
}
protected function replaceCdataMarkers ($xml) {
return str_ireplace (array ($this->cdataStart, $this->cdataEnd),
array ('<![CDATA[', ']]>'),
$xml);
}
|
echoTransformedXml() 接收 XML 和一个标志,XML 要作为字符串进行转换,标志指定是否去除源文档中的 CDATA 标记。这样做的原因是,源文档可能包含 CDATA 部分,而 CDATA 部分包含样式表需要访问的 XHTML。在 Yahoo RSS 提要中,<item> 元素的 <description>,即包含天气的 HTML 摘要的 CDATA 部分,包含当前天气状况的有意义的图标。如果图标仍然在 CDATA 元素中,那么样式表不能使用这个图标。所以,在应用样式表链之前,该函数首先通过调用 stripCdata() 从源文档中去掉所有的 <!CDATA[[ and ]] 标记。类似地,如果构造函数接收了 $cdataStart 和 $cdataEnd,那么 echoTransformedXml() 将在处理样式表之后,用真正的 CDATA 开始和结束标记来代替它们,允许样式表生成自己的 CDATA 部分。在两个 CDATA 处理步骤之间,调用 applyStylesheets 转换 XML,如清单 21 所示。
清单 21. XsltChainController.applyStylesheets()
protected function applyStylesheets ($xml) {
$xmlDOM = DOMDocument::loadXML ($xml);
foreach ($this->stylesheets as $stylesheet) {
$xmlDOM = $this->applyStylesheet ($xmlDOM, $stylesheet);
}
return $xmlDOM;
}
|
首先,applyStylesheets() 用 XML 字符串创建一个 DOMDocument 对象。然后,对提供的一组样式表文件路径进行遍历,用数组中每个元素调用 applyStylesheet(),传入源 XML 或 applyStylesheet() 的前一个调用的结果,直至依次应用所有样式表为止。清单 22 显示的 applyStylesheet() 对 DOMDocument 对象应用了一个 XSLT 样式表。
清单 22. XsltChainController.applyStylesheet()
protected function applyStylesheet ($xmlDOM, $xslFile) {
$processor = new XSLTProcessor();
$xslDOM = DOMDocument::load ($xslFile);
$processor->importStyleSheet($xslDOM);
return $processor->transformToDoc($xmlDOM);
}
|
applyStylesheet() 接受保存待转换的 XML 的 DOMDocument,以及要应用到 XML 的 XSLT 样式表的文件路径。使用 PHP 5 中的 XSL 模块执行转换。 applyStylesheet() 创建一个 XSLTProcessor,根据 XSLT 样式表文件路径创建 DOMDocument,将样式表的 DOMDocument 导入到 XSLTProcessor 中,然后调用它的 transformToDoc() 方法,对源 DOMDocument 进行转换,返回转换后的 DOMDocument 对象。
|