Development

Changeset 7787

You must first sign up to be able to contribute.

Changeset 7787

Show
Ignore:
Timestamp:
03/09/08 17:38:15 (6 months ago)
Author:
francois
Message:

Changed the way sfViewCacheManager deals with internal cache keys:

  • Allows sfViewCacheManager::remove() to accept URIs with wildcards for any type of cache (bye, bye, sfToolkit::clearGlob())
  • It is now possible to empty partial cache selectively
  • It is now possible to empty contextual partial cache selectively
  • It is possible to empty cache for all hosts and vary headers
  • And the remove() method is totally cache engine agnostic (works with sfFileCache, sfSQLiteCache, sfMemcacheCache, etc.)
  • Not to mention the fact that it is now much easier to explain how to remove something from the cache...
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/1.1/lib/view/sfViewCacheManager.class.php

    r7724 r7787  
    5959    // cache instance 
    6060    $this->cache = $cache; 
    61  
    62     // register a named route for our partial cache (at the end) 
     61     
     62    // routing instance 
    6363    $this->routing = $context->getRouting(); 
    64     if (!$this->routing->hasRouteName('sf_cache_partial')) 
    65     { 
    66       $this->routing->connect('sf_cache_partial', '/sf_cache_partial/:module/:action/:sf_cache_key.', array(), array()); 
    67     } 
    6864  } 
    6965 
     
    9086  /** 
    9187   * Generates a unique cache key for an internal URI. 
    92    * 
    93    * @param  string The internal unified resource identifier. 
     88   * This cache key can be used by any of the cache engines as a unique identifier to a cached resource 
     89   * 
     90   * Basically, the cache key generated for the following internal URI: 
     91   *   module/action?key1=value1&key2=value2 
     92   * Looks like: 
     93   *   /localhost/all/module/action/key1/value1/key2/value2 
     94   * 
     95   * @param  string The internal unified resource identifier 
     96   *                Accepts rules formatted like 'module/action?key1=value1&key2=value2' 
     97   *                Does not accept rules starting with a route name, except for '@sf_cache_partial' 
     98   * @param  string The host name 
     99   *                Optional - defaults to the current host name bu default 
     100   * @param  string The vary headers, separated by |, or "all" for all vary headers 
     101   *                Defaults to 'all' 
     102   * @param  string The contextual prefix for contextual partials. 
     103   *                Defaults to 'currentModule/currentAction/currentPAram1/currentvalue1' 
     104   *                Used only by the sfViewCacheManager::remove() method 
    94105   * 
    95106   * @return string The cache key 
    96    */ 
    97   public function generateCacheKey($internalUri) 
     107   *                If some of the parameters contained wildcards (* or **), the generated key will also have wildcards 
     108   */ 
     109  public function generateCacheKey($internalUri, $hostName = '', $vary = '', $contextualPrefix = '') 
    98110  { 
    99111    if ($callable = sfConfig::get('sf_cache_namespace_callable')) 
     
    104116      } 
    105117 
    106       return call_user_func($callable, $internalUri); 
    107     } 
    108  
    109     // generate uri 
    110     // we want our URL with / only 
    111     $oldUrlFormat = sfConfig::get('sf_url_format'); 
    112     sfConfig::set('sf_url_format', 'PATH'); 
     118      return call_user_func($callable, $internalUri, $hostName, $vary); 
     119    } 
     120     
     121    if (strpos($internalUri, '@') === 0 && strpos($internalUri, '@sf_cache_partial') === false) 
     122    { 
     123      throw new sfException('A cache key cannot be generated for an internal URI using the @rule syntax'); 
     124    } 
     125     
     126    $cacheKey = ''; 
     127     
    113128    if ($this->isContextual($internalUri)) 
    114129    { 
     130      // Contextual partial 
     131      if(!$contextualPrefix) 
     132      { 
     133        list($route_name, $params) = $this->controller->convertUrlStringToParameters($this->routing->getCurrentInternalUri()); 
     134        $cacheKey = $this->convertParametersToKey($params); 
     135      } 
     136      else 
     137      { 
     138        $cacheKey = $contextualPrefix; 
     139      } 
    115140      list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri); 
    116       $uri = $this->controller->genUrl($this->routing->getCurrentInternalUri()).sprintf('/%s/%s/%s', $params['module'], $params['action'], $params['sf_cache_key']); 
     141      $cacheKey .= sprintf('/%s/%s/%s', $params['module'], $params['action'], $params['sf_cache_key']); 
    117142    } 
    118143    else 
    119144    { 
    120       $uri = $this->controller->genUrl($internalUri); 
    121     } 
    122     sfConfig::set('sf_url_format', $oldUrlFormat); 
    123  
     145      // Regular action or non-contextual partial 
     146      list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri); 
     147      if ($route_name == 'sf_cache_partial') 
     148      { 
     149        $cacheKey = 'sf_cache_partial/'; 
     150      } 
     151      $cacheKey .= $this->convertParametersToKey($params); 
     152    } 
     153     
    124154    // prefix with vary headers 
    125     $varyHeaders = $this->getVary($internalUri); 
    126     if ($varyHeaders) 
    127     { 
    128       sort($varyHeaders); 
     155    if (!$vary) 
     156    { 
     157      $varyHeaders = $this->getVary($internalUri); 
     158      if ($varyHeaders) 
     159      { 
     160        sort($varyHeaders); 
     161        $request = $this->context->getRequest(); 
     162        $vary = ''; 
     163 
     164        foreach ($varyHeaders as $header) 
     165        { 
     166          $vary .= $request->getHttpHeader($header).'|'; 
     167        } 
     168 
     169        $vary = $vary; 
     170      } 
     171      else 
     172      { 
     173        $vary = 'all'; 
     174      } 
     175    } 
     176 
     177    // prefix with hostname 
     178    if (!$hostName) 
     179    { 
    129180      $request = $this->context->getRequest(); 
    130       $vary = ''; 
    131  
    132       foreach ($varyHeaders as $header) 
    133       { 
    134         $vary .= $request->getHttpHeader($header).'|'; 
    135       } 
    136  
    137       $vary = $vary; 
    138     } 
    139     else 
    140     { 
    141       $vary = 'all'; 
    142     } 
    143  
    144     // prefix with hostname 
    145     $request = $this->context->getRequest(); 
    146     $hostName = $request->getHost(); 
    147     $hostName = preg_replace('/[^a-z0-9]/i', '_', $hostName); 
    148     $hostName = strtolower(preg_replace('/_+/', '_', $hostName)); 
    149  
    150     $uri = '/'.$hostName.'/'.$vary.'/'.$uri; 
     181      $hostName = $request->getHost(); 
     182      $hostName = preg_replace('/[^a-z0-9]/i', '_', $hostName); 
     183      $hostName = strtolower(preg_replace('/_+/', '_', $hostName)); 
     184    } 
     185 
     186    $cacheKey = sprintf('/%s/%s/%s', $hostName, $vary, $cacheKey); 
    151187 
    152188    // replace multiple / 
    153     $uri = preg_replace('#/+#', '/', $uri); 
    154  
    155     return $uri; 
    156   } 
    157  
     189    $cacheKey = preg_replace('#/+#', '/', $cacheKey); 
     190 
     191    return $cacheKey; 
     192  } 
     193   
     194  /** 
     195   * Transforms an associative array of parameters from an URI into a unique key 
     196   * 
     197   * @param Array   Associative array of parameters from the URI (including, at least, module and action) 
     198   * 
     199   * @return String Unique key 
     200   */ 
     201  protected function convertParametersToKey($params) 
     202  { 
     203    if(!isset($params['module']) || !isset($params['action'])) 
     204    { 
     205      throw new sfException('A cache key must contain both a module and an action parameter'); 
     206    } 
     207    $module = $params['module']; 
     208    unset($params['module']); 
     209    $action = $params['action']; 
     210    unset($params['action']); 
     211    ksort($params); 
     212    $cacheKey = sprintf('%s/%s', $module, $action); 
     213    foreach ($params as $key => $value)  
     214    { 
     215      $cacheKey .= sprintf('/%s/%s', $key, $value); 
     216    } 
     217     
     218    return $cacheKey; 
     219  } 
     220   
    158221  /** 
    159222   * Adds a cache to the manager. 
     
    407470   * 
    408471   * @param string Internal uniform resource identifier 
    409    * 
    410    * @return boolean true, if the remove happend otherwise false 
    411    */ 
    412   public function remove($internalUri) 
     472   * @param string The host name 
     473   * @param string The vary headers, separated by |, or "all" for all vary headers 
     474   * @param string The removal prefix for contextual partials. Deauls to '**' (all actions, all params) 
     475   * 
     476   * @return boolean true, if the remove happened, false otherwise 
     477   */ 
     478  public function remove($internalUri, $hostName = '', $vary = '', $contextualPrefix = '**') 
    413479  { 
    414480    if (sfConfig::get('sf_logging_enabled')) 
     
    416482      $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Remove cache for "%s"', $internalUri)))); 
    417483    } 
    418  
    419     $key = $this->generateCacheKey($internalUri); 
    420     if ($this->cache->has($key)) 
    421     { 
    422       $this->cache->remove($key); 
     484     
     485    $cacheKey = $this->generateCacheKey($internalUri, $hostName, $vary, $contextualPrefix); 
     486     
     487    if(strpos($cacheKey, '*')) 
     488    { 
     489      return $this->cache->removePattern($cacheKey); 
     490    } 
     491    elseif ($this->cache->has($cacheKey)) 
     492    { 
     493      return $this->cache->remove($cacheKey); 
    423494    } 
    424495  } 
  • branches/1.1/test/unit/view/sfViewCacheManagerTest.php

    r4957 r7787  
    1212require_once($_test_dir.'/unit/sfContextMock.class.php'); 
    1313 
    14 $t = new lime_test(11, new lime_output_color()); 
     14$t = new lime_test(33, new lime_output_color()); 
    1515 
    1616class myController extends sfWebController 
     
    6161  public function removePattern($pattern, $delimiter = ':') 
    6262  { 
     63    $pattern = '#^' . str_replace('*', '.*', $pattern) . '$#'; 
     64    foreach(self::$cache as $key => $value) 
     65    { 
     66      if(preg_match($pattern, $key)) 
     67      { 
     68        unset(self::$cache[$key]); 
     69      } 
     70    } 
     71  } 
     72 
     73  public function clean($mode = sfCache::ALL) 
     74  { 
    6375    self::$cache = array(); 
    6476  } 
    6577 
    66   public function clean($mode = sfCache::ALL) 
     78  public function getTimeout($key) 
     79  { 
     80    return time() - 60; 
     81  } 
     82 
     83  public function getLastModified($key) 
     84  { 
     85    return time() - 600; 
     86  } 
     87 
     88  static public function clear() 
    6789  { 
    6890    self::$cache = array(); 
    6991  } 
    70  
    71   public function getTimeout($key) 
    72   { 
    73     return time() - 60; 
    74   } 
    75  
    76   public function getLastModified($key) 
    77   { 
    78     return time() - 600; 
    79   } 
    80  
    81   static public function clear() 
    82   { 
    83     self::$cache = array(); 
    84   } 
    85 
    86  
    87 $context = sfContext::getInstance(array('controller' => 'myController', 'routing' => 'sfPatternRouting', 'request' => 'myRequest')); 
     92
     93 
     94class myRouting extends sfPatternRouting 
     95
     96  public function getCurrentInternalUri($with_route_name = false) 
     97  { 
     98    return 'currentModule/currentAction?currentKey=currentValue'; 
     99  } 
     100
     101 
     102$context = sfContext::getInstance(array('controller' => 'myController', 'routing' => 'myRouting', 'request' => 'myRequest')); 
    88103 
    89104$r = $context->routing; 
     
    95110$t->is($m->getCache(), $cache, '->initialize() takes a sfCache object as its second argument'); 
    96111 
     112// ->generateCacheKey() 
     113$t->diag('->generateCacheKey'); 
     114$t->is($m->generateCacheKey('mymodule/myaction'), '/localhost/all/mymodule/myaction', '->generateCacheKey() creates a simple cache key from an internal URI'); 
     115$t->is($m->generateCacheKey('mymodule/myaction', 'foo'), '/foo/all/mymodule/myaction', '->generateCacheKey() can take a hostName as second parameter'); 
     116$t->is($m->generateCacheKey('mymodule/myaction', null, 'bar'), '/localhost/bar/mymodule/myaction', '->generateCacheKey() can take a serialized set of vary headers as third parameter'); 
     117 
     118$t->is($m->generateCacheKey('mymodule/myaction?key1=value1&key2=value2'), '/localhost/all/mymodule/myaction/key1/value1/key2/value2', '->generateCacheKey() includes request parameters as key/value pairs'); 
     119$t->is($m->generateCacheKey('mymodule/myaction?akey=value1&ckey=value2&bkey=value3'), '/localhost/all/mymodule/myaction/akey/value1/bkey/value3/ckey/value2', '->generateCacheKey() reorders request parameters alphabetically'); 
     120 
     121try 
     122{ 
     123  $m->generateCacheKey('@rule?key=value'); 
     124  $t->fail('->generateCacheKey() throws an sfException when passed an internal URI with a rule'); 
     125} 
     126catch(sfException $e) 
     127{ 
     128  $t->pass('->generateCacheKey() throws an sfException when passed an internal URI with a rule'); 
     129} 
     130try 
     131{ 
     132  $m->generateCacheKey('@sf_cache_partial?module=mymodule&action=myaction'); 
     133  $t->pass('->generateCacheKey() does not throw an sfException when passed an internal URI with a @sf_cache_partial rule'); 
     134} 
     135catch(sfException $e) 
     136{ 
     137  $t->fail('->generateCacheKey() does not throw an sfException when passed an internal URI with a @sf_cache_partial rule'); 
     138} 
     139try 
     140{ 
     141  $m->generateCacheKey('@sf_cache_partial?key=value'); 
     142  $t->fail('->generateCacheKey() throws an sfException when passed an internal URI with a @sf_cache_partial rule with no module or action param'); 
     143} 
     144catch(sfException $e) 
     145{ 
     146  $t->pass('->generateCacheKey() throws an sfException when passed an internal URI with a @sf_cache_partial rule with no module or action param'); 
     147} 
     148 
     149$t->is($m->generateCacheKey('@sf_cache_partial?module=foo&action=bar&sf_cache_key=value'), '/localhost/all/sf_cache_partial/foo/bar/sf_cache_key/value', '->generateCacheKey() can deal with internal URIs to partials'); 
     150 
     151$m = get_cache_manager($context); 
     152$m->addCache('foo', 'bar', get_cache_config(true)); 
     153$t->is($m->generateCacheKey('@sf_cache_partial?module=foo&action=bar&sf_cache_key=value'), '/localhost/all/currentModule/currentAction/currentKey/currentValue/foo/bar/value', '->generateCacheKey() can deal with internal URIs to contextual partials'); 
     154 
     155$t->is($m->generateCacheKey('@sf_cache_partial?module=foo&action=bar&sf_cache_key=value', null, null, 'baz'), '/localhost/all/baz/foo/bar/value', '->generateCacheKey() can take a prefix for contextual partials as fourth parameter'); 
     156 
     157 
    97158// ->generateNamespace() 
    98159$t->diag('->generateNamespace()'); 
     
    116177$t->is($m->set('test', 'module/action'), true, '->set() returns true if the action is cacheable'); 
    117178 
     179$m = get_cache_manager($context); 
     180$m->addCache('module', 'action', get_cache_config()); 
     181$m->set('test', 'module/action'); 
     182$t->is($m->get('module/action'), 'test', '->set() stores the first parameter in a key computed from the second parameter'); 
     183 
     184$m = get_cache_manager($context); 
     185$m->addCache('module', 'action', get_cache_config()); 
     186$m->set('test', 'module/action?key1=value1'); 
     187$t->is($m->get('module/action?key1=value1'), 'test', '->set() works with URIs with parameters'); 
     188$t->is($m->get('module/action?key2=value2'), null, '->set() stores a different version for each set of parameters'); 
     189$t->is($m->get('module/action'), null, '->set() stores a different version for each set of parameters'); 
     190 
     191$m = get_cache_manager($context); 
     192$m->addCache('module', 'action', get_cache_config()); 
     193$m->set('test', '@sf_cache_partial?module=module&action=action'); 
     194$t->is($m->get('@sf_cache_partial?module=module&action=action'), 'test', '->set() accepts keys to partials'); 
     195 
     196$m = get_cache_manager($context); 
     197$m->addCache('module', 'action', get_cache_config(true)); 
     198$m->set('test', '@sf_cache_partial?module=module&action=action'); 
     199$t->is($m->get('@sf_cache_partial?module=module&action=action'), 'test', '->set() accepts keys to contextual partials'); 
     200 
    118201// ->get() 
    119202$t->diag('->get()'); 
     
    141224$t->is($m->has('module/action'), false, '->remove() removes cache content for an action'); 
    142225 
     226$m->set('test', 'module/action?key1=value1'); 
     227$m->set('test', 'module/action?key2=value2'); 
     228$m->remove('module/action?key1=value1'); 
     229$t->is($m->has('module/action?key1=value1'), false, '->remove() removes accepts an internal URI as first parameter'); 
     230$t->is($m->has('module/action?key2=value2'), true, '->remove() does not remove cache content for keys not matching the internal URI'); 
     231 
     232$m = get_cache_manager($context); 
     233$m->addCache('module', 'action', get_cache_config()); 
     234$m->set('test', 'module/action?key1=value1'); 
     235$m->set('test', 'module/action?key1=value2'); 
     236$m->set('test', 'module/action?key2=value1'); 
     237$m->remove('module/action?key1=*'); 
     238$t->is($m->has('module/action?key1=value1'), false, '->remove() accepts wildcards in URIs and then removes all keys matching the pattern'); 
     239$t->is($m->has('module/action?key1=value2'), false, '->remove() accepts wildcards in URIs and then removes all keys matching the pattern'); 
     240$t->is($m->has('module/action?key2=value1'), true, '->remove() accepts wildcards in URIs and lets keys not matching the pattern unchanged'); 
     241 
    143242function get_cache_manager($context) 
    144243{ 
     
    149248} 
    150249 
    151 function get_cache_config(
     250function get_cache_config($contextual = false
    152251{ 
    153252  return array( 
     
    155254    'lifeTime'       => 86400, 
    156255    'clientLifeTime' => 86400, 
    157     'contextual'     => false
     256    'contextual'     => $contextual
    158257    'vary'           => array(), 
    159258  );