drupal-格式化区块内容

现在我们再看看另一个函数——这个函数从我们创建的 SimpleXML 对象中抽取数据并格式化以便显示。

我们这里看到的函数是基本的,没有利用 Drupal 的主题引擎。通常,格式化显示数据是由主题引擎(theming engine)处理的。主题(theme)是我们下一章要讨论的话题。

下面是我们的 _goodreads_block_content() 函数:

 

/**
 * Generate the contents of a block from a SimpleXML object.
 * Given a SimpleXML object and the maximum number of
 * entries to be displayed, generate some content.
 *
 * @param $doc
 * SimpleXML object containing Goodreads XML.
 * @param $num_items
 * Number of items to format for display.
 * @return
 * Formatted string.
 */
function _goodreads_block_content($doc, $num_items=3) {
  $items = $doc->channel->item;
  $count_items = count($items);
  $len = ($count_items < $num_items) ? $count_items : $num_items;
  $template = '<div class="goodreads-item">'
     .'<img src="%s"/><br/>%s<br/>by %s</div>';
  // Default image: 'no cover'
  $default_img = 'http://www.goodreads.com/images/nocover-60x80.jpg';
  $default_link = 'http://www.goodreads.com';
  $out = '';
  foreach ($items as $item) {
    $author = check_plain($item->author_name);
    $title = strip_tags($item->title);
    $link = check_url(trim($item->link));
    $img = check_url(trim($item->book_image_url));
    if (empty($author)) $author = '';
    if (empty($title)) $title = '';
    if (empty($link) !== 0) $link = $default_link;
    if (empty($img)) $img = $default_img;
    $book_link = l($title, $link);
    $out .= sprintf($template, $img, $book_link, $author);
  }
  $out .= '<br/><div class="goodreads-more">'
     . l('Goodreads.com', 'http://www.goodreads.com')
     .'</div>';
  return $out;
}

 

与上一个函数一样,这个函数实现的也不是 Drupal钩子。实际上,正如开头的下划线 (_) 字符指明的,这是一个私有函数,是用来被本模块中的其它函数调用的。

同样,函数以一个文档块开头,解释其目的、参数、和返回值。随后是函数体:

function _goodreads_block_content($doc, $num_items=3) {
  $items = $doc->channel->item;

这个函数做的第一件事就是从 XML 数据中获取一个 <item/> 元素的列表。为了理解这里的处理方式,我们来看看从 Goodreads 返回的 XML(进行了适当简化):

 

<?xml version="1.0"?>
<rss version="2.0">
  <channel>
    <title>Matthew's bookshelf: history-of-philosophy</title>
    <copyright>
      <![CDATA[
Copyright (C) 2006 Goodreads Inc. All rights reserved.]]>
    </copyright>
    <link>http://www.goodreads.com/review/list_rss/398385</link>
    <item>
      <title>
	<![CDATA[Thought's Ego in Augustine and Descartes]]>
      </title>
      <link>http://www.goodreads.com/review/show/6895959?
            utm_source=rss&amp;utm_medium=api</link>
      <book_image_url>
	<![CDATA[
          <a href="http://www.goodreads.com/images/books/96/285/<br />
" title="http://www.goodreads.com/images/books/96/285/<br />
">http://www.goodreads.com/images/books/96/285/<br />
</a>           964285-s-1179856470.jpg
        ]]>
      </book_image_url>
      <author_name><![CDATA[Gareth B. Matthews]]></author_name>
    </item>
    <item>
      <title>
	<![CDATA[Augustine: On the Trinity Books 8-15 
         (Cambridge Texts in the History of Philosophy)]]>
      </title>
      <link>http://www.goodreads.com/review/show/6895931?
         utm_source=rss&amp;utm_medium=api
      </link>
      <book_image_url>
	<![CDATA[
          <a href="http://www.goodreads.com/images/books/35/855/<br />
" title="http://www.goodreads.com/images/books/35/855/<br />
">http://www.goodreads.com/images/books/35/855/<br />
</a>            352855-s-1174007852.jpg
        ]]>
      </book_image_url>
      <author_name><![CDATA[Gareth B. Matthews]]></author_name>
    </item>
    <item>
      <title>
	<![CDATA[
          A Treatise Concerning the Principles of Human Knowledge
            (Oxford Philosophical Texts)]]>
      </title>
      <link>http://www.goodreads.com/review/show/6894329?
        utm_source=rss&amp;utm_medium=api
      </link>
      <book_image_url>
	<![CDATA[
          <a href="http://www.goodreads.com/images/books/10/138/<br />
" title="http://www.goodreads.com/images/books/10/138/<br />
">http://www.goodreads.com/images/books/10/138/<br />
</a>            1029138-s-1180349380.jpg
        ]]>
      </book_image_url>
      <author_name><![CDATA[George Berkeley]]></author_name>
    </item>
  </channel>
</rss>

 

上面的 XML 符合我们熟悉的 RSS 文档结构。 <channel/> 首先包含了一个描述书架的域的列表,然后是一些 <item/> 元素,其中每个都描述了书架上的一本书。

 

我们对<item/>元素的内容感兴趣,因此我们从抓取 item 列表开始:

$items = $doc->channel->item;

SimpleXML 的 $doc 对象含有指向子元素的属性。<rss/>元素(用$doc表示)只有一个元素: <channel/>。依次下来,<channel>有几个子元素:<title/>, <copyright/>, <link/>, 和几个 <item/> 元素。这些用 $doc->title, $doc->copyright, 等表示。

如果有几个元素的名字相同会怎么样呢?比如<item>。

它们将被存放在数组中。因此在上面的代码中,变量 $items 指向一个 <item/> 元素的数组。

接下来,我们确定要显示多少元素;指定一个基本的模板,稍后用于为我们的区块创建 HTML;并且设置几个默认值:

 

$count_items = count($items);
$len = ($count_items < $num_items) ? $count_items : $num_items;
$template = '<div class="goodreads-item">'
.'<img src="%s"/><br/>%s<br/>by %s</div>';
// Default image: 'no cover'
$default_img = 'http://www.goodreads.com/images/nocover-60x80.jpg';
$default_link = 'http://www.goodreads.com';

 

在第一行,我们确保不会使用大于 $num_items 的值。接下来,我们给$template 变量设置了一个 sprintf() 样式的模板。我们稍后用它格式化条目。最后,我们为 logo 图像($default_img) 设置了一个默认值,设置了一个指向Goodreads ($default_link) 的链接。
做好这些之后,我们准备循环处理数组元素并生成一些HTML:

 

$out = '';
foreach ($items as $item) {
  $author = check_plain($item->author_name);
  $title = strip_tags($item->title);
  $link = check_url(trim($item->link));
  $img = check_url(trim($item->book_image_url));
  if (empty($author)) $author = 'Unknown';
  if (empty($title)) $title = 'Untitled';
  if (empty($link)) $link = $default_link;
  if (empty($img)) $img = $default_img;
  $book_link = l($title, $link);
  $out .= sprintf( $template, $img, $book_link, $author);
}

 

使用一个 foreach 循环,我们遍历 $items 列表中的每个元素。每个元素看起来都像这样:

 

<item>
  <title>
    <![CDATA[Book Title]]>
  </title>
  <link>http://www.goodreads.com/something/</link>
  <book_image_url>
    <![CDATA[
      <a href="http://www.goodreads.com/images/something.jpg<br />
" title="http://www.goodreads.com/images/something.jpg<br />
">http://www.goodreads.com/images/something.jpg<br />
</a>    ]]>
  </book_image_url>
  <author_name><![CDATA[Author Name]]></author_name>
</item>

 

我们像抽取出 title, link, author name, 和一个书的图像(image)。我们从$item 对象中抽取这些信息:

 

$author = check_plain($item->author_name);
$title = strip_tags($item->title);
$link = check_url(trim($item->link));
$img = check_url(trim($item->book_image_url));

 

尽管我们信任 Goodreads, 不过我们还是想把它发送给我们的数据消消毒,以增加安全性。在上面,我们用函数check_plain() 和 strip_tags() 检查了$author 和 $title 的值。

strip_tags()函数是PHP内建的。它只是简单地读取字符串,然后去掉所有看起来像 HTML 或 XML标记的东西。这提供了一个基本的安全层,因为它将去掉可能在我们的网页上插入 script, applet, 或 ActiveX 对象的标记。但是这种检查仍然允许 HTML实体,比如 $amp; 或 &raquote;。

Drupal 包含几个字符串编码函数,提供与 strip_tags()不同的服务。在上面,我们使用 check_plain() 对 $item->author_name 进行某些转义操作。与strip_tags() 不同,check_plain() 不会去除什么东西。相反,它把 HTML标记编码成实体(类似 t()函数中的 @修饰符) 。因此,check_plain('<em>Example</em>') 将返回字符串&lt;em&gt;Example&lt;/em&gt;.

提示:check_plain()函数在 Drupal 安全性中扮演很重要的角色。它提供了一种避免跨站点脚本攻击(cross-site scripting attacks,XSS)的方式,也可以避免恶意的 HTML 侵入。

不过,使用 check_plain() 也有不利之处。如果 check_plain() 遇到一个HTML 实体,比如 &lt;,将对它进行再次编码。因此,&lt; 将变成 &amp;lt;。开头的 & 编码为 &amp;。

因此,对于 $item->link 和 $item->book_image_url 对象,我们得做两件事。首先,我们必须先对结果进行 trim() 处理,去除开头和结尾的空格字符。这很重要,我们一会儿将看到,如果 URL 以空格开头,Drupal 的 l() 函数将无法正确处理它们。

我们也用了 Drupal 的 check_url() 函数来验证 URL 的合法性。check_url()进行一系列检查试图逮住恶意的 URL。例如,它将阻止URL中使用 javascript: 协议。我们这样做是一种安全预防措施。

接下来,我们检查每个新分配的变量。我们想要确保,如果一个变量为 null 或空值,它将得到一个默认值。

 

if (empty($author)) $author = 'Unknown';
if (empty($title)) $title = 'Untitled';
if (empty($link)) $link = $default_link;
if (empty($img)) $img = $default_img;

 

在这个 foreach 循环中,我们做的最后一件事是,把此条目格式化为 HTML 去显示:

 

$book_link = l($title, $link);
$out .= sprintf( $template, $img, $book_link, $author);

 

首先,我们创建一个指向 Goodreads 站点的书评页面的链接。这是用 Drupal 的 l() 函数(单个小写L字符)实现的。l() 是另一个重要的 Drupal 函数。这个函数创建一个超链接。在上面的代码中,它接受两个参数: 书名($title),和一个 URL ($link), 然后创建一个类似这样的 HTML 标记:

<a href="http://www.goodreads.com/review/show/6894329
?utm_source=rss&amp;amp;utm_medium=api">
A Treatise Concerning the Principles of Human Knowledge
(Oxford Philosophical Texts)
</a>

这个字符串存储在 $book_link 中。然后我们调用 PHP sprintf() 函数进行HTML 格式化:

$out .= sprintf( $template, $img, $book_link, $author);

sprintf() 函数接受一个模板($template)作为第一个参数。我们是在 foreach 循环的外面定义的 $template,它是个这样的字符串:

<div class="goodreads-item"><img src="%s"/><br/>%s<br/>by %s</div>

sprintf() 将从头到尾读入这个字符串,每次遇到一个占位符,比如 %s,它就用一个参数值替代。

字符串中有三个串占位符(%s)。sprintf 将按顺序用传入的另外三个参数的值代替它们:$img, $book_link, 和 $author。

因此,sprintf() 最终返回一个类似这样的字符串:

<div class="goodreads-item">
<img src="http://www.goodreads.com/something.jpg"/>
<br/><a href="http://www.goodreads.com/somepath">
Thought&#039;s Ego in Augustine and Descartes
</a><br/>by Gareth B. Matthews</div>

然后那个字符串被加入到 $output 中。当 foreach 循环完成时,对于 $items 中的每个条目,$output 中都应该包含一个相应的 HTML 片段。

提示:PHP 的 sprintf() 和 printf() 函数功能很强大,并且可以使 PHP 代码更容易编写、维护和阅读。更多信息请参阅 PHP 文档:http://php.net/manual/en/function.sprintf.php.

 

一旦我们完成了 foreach 循环,要做的事就所剩无几了。我们需要在 $out HTML 中添加一个链回 Goodreads 的链接,然后返回整个输出:

 

  $out .= '<br/><div class="goodreads-more">'
          . l('Goodreads.com', 'http://www.goodreads.com')
          .'</div>';
  return $out;
}

 

我们的区块钩子(goodreads_block())将接受 _goodreads_block_content() 返回的格式化 HTML,并把它保存在区块内容中。Drupal将在右边栏上显示结果,正如我们前一节配置的那样:

插图 2-8

我们的 Goodreads 哲学史书架上的前三个条目,现在在 Drupal 中显示成一个区块。

还可以做很多事来改进这个模块。我们可以添加缓存支持,那么就不会每个请求都重新获取 Goodreads XML 了。我们可以创建另外的安全手段检查 XML 内容。我们可以增加一个管理界面,使我们能够设定书架的 URL 而不是把 URL 的值硬编码在模块中。我们还可以使用主题系统创建 HTML 并把外观风格作用于它,而不是把 HTML 标记硬编码在我们的代码中。

实际上,在下一章中,我们将细细审视主题系统,并且看到这种特别改进是如何做到的。

不过,为了完成我们的模块,我们还需要最后一击。我们需要添加一些帮助文本。

评论

发表新评论

此内容将保密,不会被其他人看见。
  • 允许HTML标签:<a> <img><em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd><p>
  • 自动断行和分段。

更多关於格式化选项的信息

Image CAPTCHA
验证码
|