Drupal 7 缓存功能入门
在Drupal中生成标记时,您通常希望将输出存储在缓存中,而不是每次都重新生成。这对于在页面请求之间不会改变的潜在昂贵的渲染任务尤其重要。Drupal7带有一个缓存系统,可以利用cache_get()和cache_set()功能。还有第三个被调用的函数drupal_static()也填补了这两个函数之间的空白。
缓存可用于各种情况,因为它内置于Drupal中,您还可以依赖Drupal的缓存管理功能。基本的经验法则是永远不要重新生成任何可以缓存和返回的东西。如果生成输出是一项耗时且昂贵的任务,这一点尤其重要。可以轻松缓存的输出的一个很好的例子是每天更新一次的新闻提要。在这种情况下,输出只需要在内容更新时重新生成,并且可以在需要再次重新生成之前保留一段时间。您还可以缓存更复杂的内容,例如搜索结果或搜索页面上的方面,生成这些内容都可能很耗时,但从页面请求到下一个页面请求不会发生太大变化。
Drupal中的静态缓存
该drupal_static()函数通过创建一个全局缓存来工作,该缓存在页面请求期间持续存在,并在整个Drupal核心中使用。如果您在请求期间多次调用同一个函数但不希望输出发生变化,这将非常有用。一个典型的例子是加载需要在页面不同区域显示的节点时,如果节点出现在页面上和相关链接块中,这种情况并不少见。显然,一旦Drupal加载了一个节点,第二次加载它几乎没有什么好处,因此该drupal_static()函数用于将数据存储在静态缓存中,并在需要时再次检索它。这是一个非常简单的drupal_static()实际操作示例。
function my_module_load() { static $my_module_data; $my_module_data = &drupal_static(__FUNCTION__); if (empty($my_module_data)) { //运行昂贵的操作并将数据添加到$my_module_data变量。 } return $my_module_data; }
上面代码中看到的__FUNCTION__是一个PHP魔术常量,代表函数名。该drupal_static()函数采用单个必需参数,即要存储的缓存项的名称。在上面的代码中,__FUNCTION__参数可以替换为'my_module_load'以保持相同的功能。如果您想更具体地使用此标签,您可以将其与参数连接起来,以便您仅在缓存中存储和检索特定项目。
function my_module_load($id) { static $my_module_data; $my_module_data = &drupal_static(__FUNCTION__ . $id); if (empty($my_module_data)) { //运行昂贵的操作并将数据添加到$my_module_data变量。 } return $my_module_data; }
另一种方法是在函数顶部添加一个块以立即返回静态缓存(如果存在),而不是包装函数内容的if语句。
function my_module_load($id) { static $my_module_data; $my_module_data = &drupal_static(__FUNCTION__ . $id); if (!empty($my_module_data)) { return $my_module_data; } //运行昂贵的操作并将数据添加到$my_module_data变量。 return $my_module_data; }
请记住,您可以在页面请求期间多次调用此函数,但您将始终收到相同的返回值。只有在后续页面请求中,如果您的数据不同,该值才会更改。
如果您需要清除静态缓存,您可以运行该drupal_static_reset()函数,将要清除的静态缓存的名称传递给它。如果您使用了__FUNCTION__魔术常量,您可以将其替换为您定义缓存的函数的名称。
drupal_static_reset('my_module_load');
更永久的缓存
静态缓存很好,但是当您允许将缓存内容保存到磁盘(或持久缓存)并在后续页面视图中读取时,可以看到速度的真正提高。这是cache_set()和cache_get()发挥作用。
该cache_set()函数是这两个函数中最复杂的,用于(顾名思义)将任何数据写入缓存。这个函数需要一些参数:
$cid:(必需)cache_set()函数中的第一个参数是缓存的名称,用于再次检索缓存。
$data:(必需)第二个参数是要缓存的数据。需要注意的是,复杂的数据类型(如数组和对象)将在存储之前自动序列化。从缓存返回时,任何序列化数据都将被反序列化,但您需要确保包含的任何对象__sleep()以及__wake()将对象恢复到原始状态的方法。
$bin:Drupal中有许多可用的缓存,称为“bins”。此函数的默认行为是将缓存写入主缓存bin,默认情况下是Drupal数据库中名为“cache”的表。这可以更改为许多不同的bin,但您可能会使用默认的'cache'或'cache_block'进行块缓存。
$expire:这告诉Drupal将缓存数据保留多长时间。
CACHE_PERMANENT:表示除非明确告知要cache_clear_all()与缓存ID一起使用,否则不应删除该项目。
CACHE_TEMPORARY:指示应在下一次常规缓存擦除时删除该项目。
Unix时间戳:表示该项目应至少保留到给定时间,之后它的行为类似于CACHE_TEMPORARY。
您通常会cache_set()像这样使用该函数。
cache_set('my_module_cache',$data,'cache',CACHE_TEMPORARY);
该cache_get()函数返回缓存的内容并接受两个参数:
$cid:(必需)这是缓存的名称,它应该与您在使用时使用的值相同cache_set()。
$bin:这是一个可选参数,允许您选择不同的缓存箱来获取缓存。同样,这应该与您用来将缓存存储在cache_set().
该cache_get()函数不仅包含您存储在缓存中的数据,它还是一个包含您存储的数据和有关该数据的其他一些信息的对象。这是缓存对象的典型示例:
stdClass Object ( [cid] => my_module_cache [data] => text [created] => 1427730490 [expire] => -1 [serialized] => 0 )
这里的重要部分是“数据”参数,您应该访问它以获取存储的缓存值。如果缓存当前已过期(expire)以及原始值是否已序列化(serialized),则其他参数会告诉您创建(创建)缓存时的缓存ID(cid)。
下面是一个使用该cache_get()函数的简单示例。
$cache = cache_get($cache_id, 'cache'); $data = $cache->data;
当然,最好将这两个功能一起使用。下面是一个简单方法的示例,其中这些函数可用于为复杂输出创建缓存资源。我在这里创建了一些简单的标记来展示它的实际效果。
function my_module_create_markup() { //从缓存后端检索。 if ($cache = cache_get('my_module_cache', 'cache')) { $markup = $cache->data; } //如果我们还没有缓存版本,则构建标记。 if (empty($markup)) { $markup = ''; //做一些事情并将其添加到标记变量中。 $markup .= date('c'); for ($i = 0; $i < 10; ++$i) { $markup .= pow($i, 10) . ' '; } } //使用cache_id缓存输出(CACHE_TEMPORARY表示可以在缓存清除时将其删除)。 cache_set('my_module_cache', $markup, 'cache', CACHE_TEMPORARY); //返回标记,无论它是谁生成的。 return $markup; }
将所有这些功能放在一起时,我通常会包含一个参数,我可以使用该参数通过单个配置设置打开或关闭缓存功能。这使开发变得更加容易,并允许您在模块处于活动状态时自动开始使用实时系统上的缓存。将缓存ID分离到一个变量中也是一种很好的做法,这允许创建基于上下文的缓存的可能性。这意味着如果您想使用用户输入来创建缓存,那么您不必对代码进行太多修改。drupal_static()除了缓存函数之外,我还倾向于使用该函数,因为这为对同一函数的多次调用提供了进一步的弹性。
这是一个功能齐全的缓存功能的示例。
function my_module_create_markup() { //初始化静态 static $markup; //检查是否应该使用缓存。 $use_cache = variable_get('my_module_use_cache', TRUE); //设置缓存ID以存储和检索缓存。 $cache_id = 'my_module_cache'; if ($use_cache) { //尝试从静态缓存中检索。 $markup = &drupal_static($cache_id); if (empty($markup)) { //从缓存后端检索。 if ($cache = cache_get($cache_id, 'cache')) { $markup = $cache->data; } } } //如果我们还没有缓存版本,则构建标记。 if (empty($markup)) { //初始化标记变量。 $markup = ''; //做一些事情并将其添加到标记变量中。 $markup .= date('c'); for ($i = 0; $i < 10; ++$i) { $markup .= pow($i, 10) . ' '; } } //缓存这个标记? if ($use_cache) { //使用cache_id缓存输出(CACHE_TEMPORARY表示可以在缓存清除时将其删除)。 cache_set($cache_id, $markup, 'cache', CACHE_TEMPORARY); } //返回标记,无论它是谁生成的。 return $markup; }
Drupal会在缓存数据过期时自动清除缓存数据,但您可以使用该cache_clear_all()功能强制清除缓存。这个函数需要三个可选参数,因为可以不带参数调用它来清除Drupal中的所有缓存。
$cid:这是您要清除的缓存的缓存ID。本质上,cache_set()调用中的第一个参数。您还可以传递一组缓存ID以清除多个缓存。如果设置为NULL,$wildcard参数将被忽略。
$bin:这是您用来存储缓存的缓存箱。如果您提供了$cid参数,则这是必需的。
$wildcard:如果为TRUE,则$cid参数必须包含字符串值,并且除了$cid指定的确切缓存ID之外,还删除以$cid开头的缓存ID。如果$wildcard为TRUE且$cid为'*',则整个缓存被清空。
当您知道某个更改会影响缓存的输出时,您会在代码的其他地方使用此函数。例如,您可能有一个部分显示某些节点,在这种情况下,您需要添加一个hook_node_presave()钩子,以便在用户保存节点时清除该部分的缓存。要清除在上述代码中创建的缓存,您将使用以下内容。
cache_clear_all('my_module_cache','cache');
显然,在Drupal中缓存可能会有点复杂,但是如果您围绕cache_set()和cache_get()函数设计代码,那么您可以创建响应式站点来存储重复数据,而无需重新生成所有内容。可以在Drupal中扩展可用的缓存箱,但强烈建议您使用cache_set()和cache_get()函数与所有缓存进行交互。即使是视图缓存(在Drupal中创建cache_views和cache_views_data作为缓存箱)仍然使用cache_get()从缓存中获取信息。
也可以改变Drupal中的缓存机制,将缓存写入不同的存储机制。这是通过使用APC、Redis和Memcache等模块来实现的,但也要求这些系统事先存在。