Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/*
4
 * This file is part of the symfony package.
5
 * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
 
11
/**
12
 * Cache class to cache the HTML results for actions and templates.
13
 *
14
 * This class uses a sfCache instance implementation to store cache.
15
 *
16
 * To disable all caching, you can set the [sf_cache] constant to false.
17
 *
18
 * @package    symfony
19
 * @subpackage view
20
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
21
 * @version    SVN: $Id: sfViewCacheManager.class.php 32699 2011-07-01 06:58:02Z fabien $
22
 */
23
class sfViewCacheManager
24
{
25
  protected
26
    $cache       = null,
27
    $cacheConfig = array(),
28
    $context     = null,
29
    $dispatcher  = null,
30
    $controller  = null,
31
    $routing     = null,
32
    $request     = null,
33
    $loaded      = array();
34
 
35
  /**
36
   * Class constructor.
37
   *
38
   * @see initialize()
39
   */
40
  public function __construct($context, sfCache $cache, $options = array())
41
  {
42
    $this->initialize($context, $cache, $options);
43
  }
44
 
45
  /**
46
   * Initializes the cache manager.
47
   *
48
   * @param sfContext $context  Current application context
49
   * @param sfCache   $cache    An sfCache instance
50
   */
51
  public function initialize($context, sfCache $cache, $options = array())
52
  {
53
    $this->context    = $context;
54
    $this->dispatcher = $context->getEventDispatcher();
55
    $this->controller = $context->getController();
56
    $this->request    = $context->getRequest();
57
    $this->options    = array_merge(array(
58
        'cache_key_use_vary_headers' => true,
59
        'cache_key_use_host_name'    => true,
60
      ), $options);
61
 
62
    if (sfConfig::get('sf_web_debug'))
63
    {
64
      $this->dispatcher->connect('view.cache.filter_content', array($this, 'decorateContentWithDebug'));
65
    }
66
 
67
    // empty configuration
68
    $this->cacheConfig = array();
69
 
70
    // cache instance
71
    $this->cache = $cache;
72
 
73
    // routing instance
74
    $this->routing = $context->getRouting();
75
  }
76
 
77
  /**
78
   * Retrieves the current cache context.
79
   *
80
   * @return sfContext The sfContext instance
81
   */
82
  public function getContext()
83
  {
84
    return $this->context;
85
  }
86
 
87
  /**
88
   * Retrieves the current cache object.
89
   *
90
   * @return sfCache The current cache object
91
   */
92
  public function getCache()
93
  {
94
    return $this->cache;
95
  }
96
 
97
  /**
98
   * Generates a unique cache key for an internal URI.
99
   * This cache key can be used by any of the cache engines as a unique identifier to a cached resource
100
   *
101
   * Basically, the cache key generated for the following internal URI:
102
   *   module/action?key1=value1&key2=value2
103
   * Looks like:
104
   *   /localhost/all/module/action/key1/value1/key2/value2
105
   *
106
   * @param  string $internalUri       The internal unified resource identifier
107
   *                                   Accepts rules formatted like 'module/action?key1=value1&key2=value2'
108
   *                                   Does not accept rules starting with a route name, except for '@sf_cache_partial'
109
   * @param  string $hostName          The host name
110
   *                                   Optional - defaults to the current host name bu default
111
   * @param  string $vary              The vary headers, separated by |, or "all" for all vary headers
112
   *                                   Defaults to 'all'
113
   * @param  string $contextualPrefix  The contextual prefix for contextual partials.
114
   *                                   Defaults to 'currentModule/currentAction/currentPAram1/currentvalue1'
115
   *                                   Used only by the sfViewCacheManager::remove() method
116
   *
117
   * @return string The cache key
118
   *                If some of the parameters contained wildcards (* or **), the generated key will also have wildcards
119
   */
120
  public function generateCacheKey($internalUri, $hostName = '', $vary = '', $contextualPrefix = '')
121
  {
122
    if ($callable = sfConfig::get('sf_cache_namespace_callable'))
123
    {
124
      if (!is_callable($callable))
125
      {
126
        throw new sfException(sprintf('"%s" cannot be called as a function.', var_export($callable, true)));
127
      }
128
 
129
      return call_user_func($callable, $internalUri, $hostName, $vary, $contextualPrefix, $this);
130
    }
131
 
132
    if (strpos($internalUri, '@') === 0 && strpos($internalUri, '@sf_cache_partial') === false)
133
    {
134
      throw new sfException('A cache key cannot be generated for an internal URI using the @rule syntax');
135
    }
136
 
137
    $cacheKey = '';
138
 
139
    if ($this->isContextual($internalUri))
140
    {
141
      // Contextual partial
142
      if (!$contextualPrefix)
143
      {
144
        list($route_name, $params) = $this->controller->convertUrlStringToParameters($this->routing->getCurrentInternalUri());
145
 
146
        // if there is no module/action, it means that we have a 404 and the user is trying to cache it
147
        if (!isset($params['module']) || !isset($params['action']))
148
        {
149
          $params['module'] = sfConfig::get('sf_error_404_module');
150
          $params['action'] = sfConfig::get('sf_error_404_action');
151
        }
152
        $cacheKey = $this->convertParametersToKey($params);
153
      }
154
      else
155
      {
156
        $cacheKey = $contextualPrefix;
157
      }
158
      list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
159
      $cacheKey .= sprintf('/%s/%s/%s', $params['module'], $params['action'], isset($params['sf_cache_key']) ? $params['sf_cache_key'] : '');
160
    }
161
    else
162
    {
163
      // Regular action or non-contextual partial
164
      list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
165
      if ($route_name == 'sf_cache_partial')
166
      {
167
        $cacheKey = 'sf_cache_partial/';
168
      }
169
 
170
      $cacheKey .= $this->convertParametersToKey($params);
171
    }
172
 
173
    // add vary headers
174
    if ($varyPart = $this->getCacheKeyVaryHeaderPart($internalUri, $vary))
175
    {
176
      $cacheKey = '/'.$varyPart.'/'.ltrim($cacheKey, '/');
177
    }
178
 
179
    // add hostname
180
    if ($hostNamePart = $this->getCacheKeyHostNamePart($hostName))
181
    {
182
      $cacheKey = '/'.$hostNamePart.'/'.ltrim($cacheKey, '/');
183
    }
184
 
185
    // normalize to a leading slash
186
    if (0 !== strpos($cacheKey, '/'))
187
    {
188
      $cacheKey = '/'.$cacheKey;
189
    }
190
 
191
    // distinguish multiple slashes
192
    while (false !== strpos($cacheKey, '//'))
193
    {
194
      $cacheKey = str_replace('//', '/'.substr(sha1($cacheKey), 0, 7).'/', $cacheKey);
195
    }
196
 
197
    // prevent directory traversal
198
    $cacheKey = strtr($cacheKey, array(
199
      '/.'  => '/_.',
200
      '/_'  => '/__',
201
      '\\.' => '\\_.',
202
      '\\_' => '\\__',
203
    ));
204
 
205
    return $cacheKey;
206
  }
207
 
208
  /**
209
   * Gets the vary header part of view cache key.
210
   *
211
   * @param  string $vary
212
   * @return string
213
   */
214
  protected function getCacheKeyVaryHeaderPart($internalUri, $vary = '')
215
  {
216
    if (!$this->options['cache_key_use_vary_headers'])
217
    {
218
      return '';
219
    }
220
 
221
    // prefix with vary headers
222
    if (!$vary)
223
    {
224
      $varyHeaders = $this->getVary($internalUri);
225
 
226
      if (!$varyHeaders)
227
      {
228
        return 'all';
229
      }
230
 
231
      sort($varyHeaders);
232
      $request = $this->context->getRequest();
233
      $varys = array();
234
 
235
      foreach ($varyHeaders as $header)
236
      {
237
        $varys[] = $header . '-' . preg_replace('/\W+/', '_', $request->getHttpHeader($header));
238
      }
239
      $vary = implode($varys, '-');
240
    }
241
 
242
    return $vary;
243
  }
244
 
245
  /**
246
   * Gets the hostname part of view cache key.
247
   *
248
   * @param string $hostName
249
   * @return void
250
   */
251
  protected function getCacheKeyHostNamePart($hostName = '')
252
  {
253
    if (!$this->options['cache_key_use_host_name'])
254
    {
255
      return '';
256
    }
257
 
258
    if (!$hostName)
259
    {
260
      $hostName = $this->context->getRequest()->getHost();
261
    }
262
 
263
    $hostName = preg_replace('/[^a-z0-9\*]/i', '_', $hostName);
264
    $hostName = preg_replace('/_+/', '_', $hostName);
265
 
266
    return strtolower($hostName);
267
  }
268
 
269
  /**
270
   * Transforms an associative array of parameters from an URI into a unique key
271
   *
272
   * @param  array $params  Associative array of parameters from the URI (including, at least, module and action)
273
   *
274
   * @return string Unique key
275
   */
276
  protected function convertParametersToKey($params)
277
  {
278
    if(!isset($params['module']) || !isset($params['action']))
279
    {
280
      throw new sfException('A cache key must contain both a module and an action parameter');
281
    }
282
    $module = $params['module'];
283
    unset($params['module']);
284
    $action = $params['action'];
285
    unset($params['action']);
286
    ksort($params);
287
    $cacheKey = sprintf('%s/%s', $module, $action);
288
    foreach ($params as $key => $value)
289
    {
290
      $cacheKey .= sprintf('/%s/%s', $key, $value);
291
    }
292
 
293
    return $cacheKey;
294
  }
295
 
296
  /**
297
   * Adds a cache to the manager.
298
   *
299
   * @param string $moduleName  Module name
300
   * @param string $actionName  Action name
301
   * @param array  $options     Options for the cache
302
   */
303
  public function addCache($moduleName, $actionName, $options = array())
304
  {
305
    // normalize vary headers
306
    if (isset($options['vary']))
307
    {
308
      foreach ($options['vary'] as $key => $name)
309
      {
310
        $options['vary'][$key] = strtr(strtolower($name), '_', '-');
311
      }
312
    }
313
 
314
    $options['lifeTime'] = isset($options['lifeTime']) ? $options['lifeTime'] : 0;
315
    if (!isset($this->cacheConfig[$moduleName]))
316
    {
317
      $this->cacheConfig[$moduleName] = array();
318
    }
319
    $this->cacheConfig[$moduleName][$actionName] = array(
320
      'withLayout'     => isset($options['withLayout']) ? $options['withLayout'] : false,
321
      'lifeTime'       => $options['lifeTime'],
322
      'clientLifeTime' => isset($options['clientLifeTime']) ? $options['clientLifeTime'] : $options['lifeTime'],
323
      'contextual'     => isset($options['contextual']) ? $options['contextual'] : false,
324
      'vary'           => isset($options['vary']) ? $options['vary'] : array(),
325
    );
326
  }
327
 
328
  /**
329
   * Registers configuration options for the cache.
330
   *
331
   * @param string $moduleName  Module name
332
   */
333
  public function registerConfiguration($moduleName)
334
  {
335
    if (!isset($this->loaded[$moduleName]))
336
    {
337
      require($this->context->getConfigCache()->checkConfig('modules/'.$moduleName.'/config/cache.yml'));
338
      $this->loaded[$moduleName] = true;
339
    }
340
  }
341
 
342
  /**
343
   * Retrieves the layout from the cache option list.
344
   *
345
   * @param  string $internalUri  Internal uniform resource identifier
346
   *
347
   * @return bool true, if have layout otherwise false
348
   */
349
  public function withLayout($internalUri)
350
  {
351
    return $this->getCacheConfig($internalUri, 'withLayout', false);
352
  }
353
 
354
  /**
355
   * Retrieves lifetime from the cache option list.
356
   *
357
   * @param  string $internalUri  Internal uniform resource identifier
358
   *
359
   * @return int LifeTime
360
   */
361
  public function getLifeTime($internalUri)
362
  {
363
    return $this->getCacheConfig($internalUri, 'lifeTime', 0);
364
  }
365
 
366
  /**
367
   * Retrieves client lifetime from the cache option list
368
   *
369
   * @param  string $internalUri  Internal uniform resource identifier
370
   *
371
   * @return int Client lifetime
372
   */
373
  public function getClientLifeTime($internalUri)
374
  {
375
    return $this->getCacheConfig($internalUri, 'clientLifeTime', 0);
376
  }
377
 
378
  /**
379
   * Retrieves contextual option from the cache option list.
380
   *
381
   * @param  string $internalUri  Internal uniform resource identifier
382
   *
383
   * @return boolean true, if is contextual otherwise false
384
   */
385
  public function isContextual($internalUri)
386
  {
387
    return $this->getCacheConfig($internalUri, 'contextual', false);
388
  }
389
 
390
  /**
391
   * Retrieves vary option from the cache option list.
392
   *
393
   * @param  string $internalUri  Internal uniform resource identifier
394
   *
395
   * @return array Vary options for the cache
396
   */
397
  public function getVary($internalUri)
398
  {
399
    return $this->getCacheConfig($internalUri, 'vary', array());
400
  }
401
 
402
  /**
403
   * Gets a config option from the cache.
404
   *
405
   * @param string $internalUri   Internal uniform resource identifier
406
   * @param string $key           Option name
407
   * @param string $defaultValue  Default value of the option
408
   *
409
   * @return mixed Value of the option
410
   */
411
  protected function getCacheConfig($internalUri, $key, $defaultValue = null)
412
  {
413
    list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
414
 
415
    if (!isset($params['module']))
416
    {
417
        return $defaultValue;
418
    }
419
 
420
    $this->registerConfiguration($params['module']);
421
 
422
    $value = $defaultValue;
423
    if (isset($this->cacheConfig[$params['module']][$params['action']][$key]))
424
    {
425
      $value = $this->cacheConfig[$params['module']][$params['action']][$key];
426
    }
427
    else if (isset($this->cacheConfig[$params['module']]['DEFAULT'][$key]))
428
    {
429
      $value = $this->cacheConfig[$params['module']]['DEFAULT'][$key];
430
    }
431
 
432
    return $value;
433
  }
434
 
435
  /**
436
   * Returns true if the current content is cacheable.
437
   *
438
   * Possible break in backward compatibility: If the sf_lazy_cache_key
439
   * setting is turned on in settings.yml, this method is not used when
440
   * initially checking a partial's cacheability.
441
   *
442
   * @see sfPartialView, isActionCacheable()
443
   *
444
   * @param  string $internalUri  Internal uniform resource identifier
445
   *
446
   * @return bool true, if the content is cacheable otherwise false
447
   */
448
  public function isCacheable($internalUri)
449
  {
450
    if ($this->request instanceof sfWebRequest && !$this->request->isMethod(sfRequest::GET))
451
    {
452
      return false;
453
    }
454
 
455
    list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
456
 
457
    if (!isset($params['module']))
458
    {
459
        return false;
460
    }
461
 
462
    $this->registerConfiguration($params['module']);
463
 
464
    if (isset($this->cacheConfig[$params['module']][$params['action']]))
465
    {
466
      return ($this->cacheConfig[$params['module']][$params['action']]['lifeTime'] > 0);
467
    }
468
    else if (isset($this->cacheConfig[$params['module']]['DEFAULT']))
469
    {
470
      return ($this->cacheConfig[$params['module']]['DEFAULT']['lifeTime'] > 0);
471
    }
472
 
473
    return false;
474
  }
475
 
476
  /**
477
   * Returns true if the action is cacheable.
478
   *
479
   * @param  string $moduleName A module name
480
   * @param  string $actionName An action or partial template name
481
   *
482
   * @return boolean True if the action is cacheable
483
   *
484
   * @see isCacheable()
485
   */
486
  public function isActionCacheable($moduleName, $actionName)
487
  {
488
    if ($this->request instanceof sfWebRequest && !$this->request->isMethod(sfRequest::GET))
489
    {
490
      return false;
491
    }
492
 
493
    $this->registerConfiguration($moduleName);
494
 
495
    if (isset($this->cacheConfig[$moduleName][$actionName]))
496
    {
497
      return $this->cacheConfig[$moduleName][$actionName]['lifeTime'] > 0;
498
    }
499
    else if (isset($this->cacheConfig[$moduleName]['DEFAULT']))
500
    {
501
      return $this->cacheConfig[$moduleName]['DEFAULT']['lifeTime'] > 0;
502
    }
503
 
504
    return false;
505
  }
506
 
507
  /**
508
   * Retrieves content in the cache.
509
   *
510
   * @param  string $internalUri  Internal uniform resource identifier
511
   *
512
   * @return string The content in the cache
513
   */
514
  public function get($internalUri)
515
  {
516
    // no cache or no cache set for this action
517
    if (!$this->isCacheable($internalUri) || $this->ignore())
518
    {
519
      return null;
520
    }
521
 
522
    $retval = $this->cache->get($this->generateCacheKey($internalUri));
523
 
524
    if (sfConfig::get('sf_logging_enabled'))
525
    {
526
      $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Cache for "%s" %s', $internalUri, $retval !== null ? 'exists' : 'does not exist'))));
527
    }
528
 
529
    return $retval;
530
  }
531
 
532
  /**
533
   * Returns true if there is a cache.
534
   *
535
   * @param  string $internalUri  Internal uniform resource identifier
536
   *
537
   * @return bool true, if there is a cache otherwise false
538
   */
539
  public function has($internalUri)
540
  {
541
    if (!$this->isCacheable($internalUri) || $this->ignore())
542
    {
543
      return null;
544
    }
545
 
546
    return $this->cache->has($this->generateCacheKey($internalUri));
547
  }
548
 
549
  /**
550
   * Ignores the cache functionality.
551
   *
552
   * @return bool true, if the cache is ignore otherwise false
553
   */
554
  protected function ignore()
555
  {
556
    // ignore cache parameter? (only available in debug mode)
557
    if (sfConfig::get('sf_debug') && $this->context->getRequest()->getAttribute('sf_ignore_cache'))
558
    {
559
      if (sfConfig::get('sf_logging_enabled'))
560
      {
561
        $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Discard cache')));
562
      }
563
 
564
      return true;
565
    }
566
 
567
    return false;
568
  }
569
 
570
  /**
571
   * Sets the cache content.
572
   *
573
   * @param  string $data         Data to put in the cache
574
   * @param  string $internalUri  Internal uniform resource identifier
575
   *
576
   * @return boolean true, if the data get set successfully otherwise false
577
   */
578
  public function set($data, $internalUri)
579
  {
580
    if (!$this->isCacheable($internalUri))
581
    {
582
      return false;
583
    }
584
 
585
    try
586
    {
587
      $ret = $this->cache->set($this->generateCacheKey($internalUri), $data, $this->getLifeTime($internalUri));
588
    }
589
    catch (Exception $e)
590
    {
591
      return false;
592
    }
593
 
594
    if (sfConfig::get('sf_logging_enabled'))
595
    {
596
      $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Save cache for "%s"', $internalUri))));
597
    }
598
 
599
    return true;
600
  }
601
 
602
  /**
603
   * Removes the content in the cache.
604
   *
605
   * @param  string $internalUri       Internal uniform resource identifier
606
   * @param  string $hostName          The host name
607
   * @param  string $vary              The vary headers, separated by |, or "all" for all vary headers
608
   * @param  string $contextualPrefix  The removal prefix for contextual partials. Defaults to '**' (all actions, all params)
609
   *
610
   * @return bool true, if the remove happened, false otherwise
611
   */
612
  public function remove($internalUri, $hostName = '', $vary = '', $contextualPrefix = '**')
613
  {
614
    if (sfConfig::get('sf_logging_enabled'))
615
    {
616
      $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Remove cache for "%s"', $internalUri))));
617
    }
618
 
619
    $cacheKey = $this->generateCacheKey($internalUri, $hostName, $vary, $contextualPrefix);
620
 
621
    if(strpos($cacheKey, '*'))
622
    {
623
      return $this->cache->removePattern($cacheKey);
624
    }
625
    elseif ($this->cache->has($cacheKey))
626
    {
627
      return $this->cache->remove($cacheKey);
628
    }
629
  }
630
 
631
  /**
632
   * Retrieves the last modified time.
633
   *
634
   * @param  string $internalUri  Internal uniform resource identifier
635
   *
636
   * @return int    The last modified datetime
637
   */
638
  public function getLastModified($internalUri)
639
  {
640
    if (!$this->isCacheable($internalUri))
641
    {
642
      return 0;
643
    }
644
 
645
    return $this->cache->getLastModified($this->generateCacheKey($internalUri));
646
  }
647
 
648
  /**
649
   * Retrieves the timeout.
650
   *
651
   * @param  string $internalUri  Internal uniform resource identifier
652
   *
653
   * @return int    The timeout datetime
654
   */
655
  public function getTimeout($internalUri)
656
  {
657
    if (!$this->isCacheable($internalUri))
658
    {
659
      return 0;
660
    }
661
 
662
    return $this->cache->getTimeout($this->generateCacheKey($internalUri));
663
  }
664
 
665
  /**
666
   * Starts the fragment cache.
667
   *
668
   * @param  string $name            Unique fragment name
669
   * @param  string $lifeTime        Life time for the cache
670
   * @param  string $clientLifeTime  Client life time for the cache
671
   * @param  array  $vary            Vary options for the cache
672
   *
673
   * @return bool true, if success otherwise false
674
   */
675
  public function start($name, $lifeTime, $clientLifeTime = null, $vary = array())
676
  {
677
    $internalUri = $this->routing->getCurrentInternalUri();
678
 
679
    if (!$clientLifeTime)
680
    {
681
      $clientLifeTime = $lifeTime;
682
    }
683
 
684
    // add cache config to cache manager
685
    list($route_name, $params) = $this->controller->convertUrlStringToParameters($internalUri);
686
    $this->addCache($params['module'], $params['action'], array('withLayout' => false, 'lifeTime' => $lifeTime, 'clientLifeTime' => $clientLifeTime, 'vary' => $vary));
687
 
688
    // get data from cache if available
689
    $data = $this->get($internalUri.(strpos($internalUri, '?') ? '&' : '?').'_sf_cache_key='.$name);
690
    if ($data !== null)
691
    {
692
      return $data;
693
    }
694
    else
695
    {
696
      ob_start();
697
      ob_implicit_flush(0);
698
 
699
      return null;
700
    }
701
  }
702
 
703
  /**
704
   * Stops the fragment cache.
705
   *
706
   * @param  string $name Unique fragment name
707
   *
708
   * @return bool true, if success otherwise false
709
   */
710
  public function stop($name)
711
  {
712
    $data = ob_get_clean();
713
 
714
    // save content to cache
715
    $internalUri = $this->routing->getCurrentInternalUri();
716
    try
717
    {
718
      $this->set($data, $internalUri.(strpos($internalUri, '?') ? '&' : '?').'_sf_cache_key='.$name);
719
    }
720
    catch (Exception $e)
721
    {
722
    }
723
 
724
    return $data;
725
  }
726
 
727
  /**
728
   * Computes the cache key based on the passed parameters.
729
   *
730
   * @param array $parameters  An array of parameters
731
   */
732
  public function computeCacheKey(array $parameters)
733
  {
734
    if (isset($parameters['sf_cache_key']))
735
    {
736
      return $parameters['sf_cache_key'];
737
    }
738
 
739
    if (sfConfig::get('sf_logging_enabled'))
740
    {
741
      $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Generate cache key')));
742
    }
743
    ksort($parameters);
744
 
745
    return md5(serialize($parameters));
746
  }
747
 
748
  /**
749
   * Checks that the supplied parameters include a cache key.
750
   *
751
   * If no 'sf_cache_key' parameter is present one is added to the array as
752
   * it is passed by reference.
753
   *
754
   * @param  array  $parameters An array of parameters
755
   *
756
   * @return string The cache key
757
   */
758
  public function checkCacheKey(array & $parameters)
759
  {
760
    $parameters['sf_cache_key'] = $this->computeCacheKey($parameters);
761
 
762
    return $parameters['sf_cache_key'];
763
  }
764
 
765
  /**
766
   * Computes a partial internal URI.
767
   *
768
   * @param  string $module    The module name
769
   * @param  string $action    The action name
770
   * @param  string $cacheKey  The cache key
771
   *
772
   * @return string The internal URI
773
   */
774
  public function getPartialUri($module, $action, $cacheKey)
775
  {
776
    return sprintf('@sf_cache_partial?module=%s&action=%s&sf_cache_key=%s', $module, $action, $cacheKey);
777
  }
778
 
779
  /**
780
   * Returns whether a partial template is in the cache.
781
   *
782
   * @param  string $module    The module name
783
   * @param  string $action    The action name
784
   * @param  string $cacheKey  The cache key
785
   *
786
   * @return bool true if a partial is in the cache, false otherwise
787
   */
788
  public function hasPartialCache($module, $action, $cacheKey)
789
  {
790
    return $this->has($this->getPartialUri($module, $action, $cacheKey));
791
  }
792
 
793
  /**
794
   * Gets a partial template from the cache.
795
   *
796
   * @param  string $module    The module name
797
   * @param  string $action    The action name
798
   * @param  string $cacheKey  The cache key
799
   *
800
   * @return string The cache content
801
   */
802
  public function getPartialCache($module, $action, $cacheKey)
803
  {
804
    $uri = $this->getPartialUri($module, $action, $cacheKey);
805
 
806
    if (!$this->isCacheable($uri))
807
    {
808
      return null;
809
    }
810
 
811
    // retrieve content from cache
812
    $cache = $this->get($uri);
813
 
814
    if (null === $cache)
815
    {
816
      return null;
817
    }
818
 
819
    $cache = unserialize($cache);
820
    $content = $cache['content'];
821
    $this->context->getResponse()->merge($cache['response']);
822
 
823
    if (sfConfig::get('sf_web_debug'))
824
    {
825
      $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => false)), $content)->getReturnValue();
826
    }
827
 
828
    return $content;
829
  }
830
 
831
  /**
832
   * Sets an action template in the cache.
833
   *
834
   * @param  string $module    The module name
835
   * @param  string $action    The action name
836
   * @param  string $cacheKey  The cache key
837
   * @param  string $content   The content to cache
838
   *
839
   * @return string The cached content
840
   */
841
  public function setPartialCache($module, $action, $cacheKey, $content)
842
  {
843
    $uri = $this->getPartialUri($module, $action, $cacheKey);
844
    if (!$this->isCacheable($uri))
845
    {
846
      return $content;
847
    }
848
 
849
    $saved = $this->set(serialize(array('content' => $content, 'response' => $this->context->getResponse())), $uri);
850
 
851
    if ($saved && sfConfig::get('sf_web_debug'))
852
    {
853
      $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => true)), $content)->getReturnValue();
854
    }
855
 
856
    return $content;
857
  }
858
 
859
  /**
860
   * Returns whether an action template is in the cache.
861
   *
862
   * @param  string  $uri  The internal URI
863
   *
864
   * @return bool true if an action is in the cache, false otherwise
865
   */
866
  public function hasActionCache($uri)
867
  {
868
    return $this->has($uri) && !$this->withLayout($uri);
869
  }
870
 
871
  /**
872
   * Gets an action template from the cache.
873
   *
874
   * @param  string $uri  The internal URI
875
   *
876
   * @return array  An array composed of the cached content and the view attribute holder
877
   */
878
  public function getActionCache($uri)
879
  {
880
    if (!$this->isCacheable($uri) || $this->withLayout($uri))
881
    {
882
      return null;
883
    }
884
 
885
    // retrieve content from cache
886
    $cache = $this->get($uri);
887
 
888
    if (null === $cache)
889
    {
890
      return null;
891
    }
892
 
893
    $cache = unserialize($cache);
894
    $content = $cache['content'];
895
    $cache['response']->setEventDispatcher($this->dispatcher);
896
    $this->context->getResponse()->copyProperties($cache['response']);
897
 
898
    if (sfConfig::get('sf_web_debug'))
899
    {
900
      $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => false)), $content)->getReturnValue();
901
    }
902
 
903
    return array($content, $cache['decoratorTemplate']);
904
  }
905
 
906
  /**
907
   * Sets an action template in the cache.
908
   *
909
   * @param  string $uri                The internal URI
910
   * @param  string $content            The content to cache
911
   * @param  string $decoratorTemplate  The view attribute holder to cache
912
   *
913
   * @return string The cached content
914
   */
915
  public function setActionCache($uri, $content, $decoratorTemplate)
916
  {
917
    if (!$this->isCacheable($uri) || $this->withLayout($uri))
918
    {
919
      return $content;
920
    }
921
 
922
    $saved = $this->set(serialize(array('content' => $content, 'decoratorTemplate' => $decoratorTemplate, 'response' => $this->context->getResponse())), $uri);
923
 
924
    if ($saved && sfConfig::get('sf_web_debug'))
925
    {
926
      $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => true)), $content)->getReturnValue();
927
    }
928
 
929
    return $content;
930
  }
931
 
932
  /**
933
   * Sets a page in the cache.
934
   *
935
   * @param string $uri  The internal URI
936
   */
937
  public function setPageCache($uri)
938
  {
939
    if (sfView::RENDER_CLIENT != $this->controller->getRenderMode())
940
    {
941
      return;
942
    }
943
 
944
    // save content in cache
945
    $saved = $this->set(serialize($this->context->getResponse()), $uri);
946
 
947
    if ($saved && sfConfig::get('sf_web_debug'))
948
    {
949
      $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => true)), $this->context->getResponse()->getContent())->getReturnValue();
950
 
951
      $this->context->getResponse()->setContent($content);
952
    }
953
  }
954
 
955
  /**
956
   * Gets a page from the cache.
957
   *
958
   * @param  string $uri  The internal URI
959
   *
960
   * @return string The cached page
961
   */
962
  public function getPageCache($uri)
963
  {
964
    $retval = $this->get($uri);
965
 
966
    if (null === $retval)
967
    {
968
      return false;
969
    }
970
 
971
    $cachedResponse = unserialize($retval);
972
    $cachedResponse->setEventDispatcher($this->dispatcher);
973
 
974
    if (sfView::RENDER_VAR == $this->controller->getRenderMode())
975
    {
976
      $this->controller->getActionStack()->getLastEntry()->setPresentation($cachedResponse->getContent());
977
      $this->context->getResponse()->setContent('');
978
    }
979
    else
980
    {
981
      $this->context->setResponse($cachedResponse);
982
 
983
      if (sfConfig::get('sf_web_debug'))
984
      {
985
        $content = $this->dispatcher->filter(new sfEvent($this, 'view.cache.filter_content', array('response' => $this->context->getResponse(), 'uri' => $uri, 'new' => false)), $this->context->getResponse()->getContent())->getReturnValue();
986
 
987
        $this->context->getResponse()->setContent($content);
988
      }
989
    }
990
 
991
    return true;
992
  }
993
 
994
  /**
995
   * Returns the current request's cache key.
996
   *
997
   * This cache key is calculated based on the routing factory's current URI
998
   * and any GET parameters from the current request factory.
999
   *
1000
   * @return string The cache key for the current request
1001
   */
1002
  public function getCurrentCacheKey()
1003
  {
1004
    $cacheKey = $this->routing->getCurrentInternalUri();
1005
 
1006
    if ($getParameters = $this->request->getGetParameters())
1007
    {
1008
      $cacheKey .= false === strpos($cacheKey, '?') ? '?' : '&';
1009
      $cacheKey .= http_build_query($getParameters, null, '&');
1010
    }
1011
 
1012
    return $cacheKey;
1013
  }
1014
 
1015
  /**
1016
   * Listens to the 'view.cache.filter_content' event to decorate a chunk of HTML with cache information.
1017
   *
1018
   * @param sfEvent $event   A sfEvent instance
1019
   * @param string  $content The HTML content
1020
   *
1021
   * @return string The decorated HTML string
1022
   */
1023
  public function decorateContentWithDebug(sfEvent $event, $content)
1024
  {
1025
    // don't decorate if not html or if content is null
1026
    if (!$content || false === strpos($event['response']->getContentType(), 'html'))
1027
    {
1028
      return $content;
1029
    }
1030
 
1031
    $this->context->getConfiguration()->loadHelpers(array('Helper', 'Url', 'Asset', 'Tag'));
1032
 
1033
    $sf_cache_key = $this->generateCacheKey($event['uri']);
1034
    $bgColor      = $event['new'] ? '#9ff' : '#ff9';
1035
    $lastModified = $this->cache->getLastModified($sf_cache_key);
1036
    $cacheKey     = $this->cache->getOption('prefix').$sf_cache_key;
1037
    $id           = md5($event['uri']);
1038
 
1039
    return '
1040
      <div id="main_'.$id.'" class="sfWebDebugActionCache" style="border: 1px solid #f00">
1041
      <div id="sub_main_'.$id.'" class="sfWebDebugCache" style="background-color: '.$bgColor.'; border-right: 1px solid #f00; border-bottom: 1px solid #f00;">
1042
      <div style="height: 16px; padding: 2px"><a href="#" onclick="sfWebDebugToggle(\'sub_main_info_'.$id.'\'); return false;"><strong>cache information</strong></a>&nbsp;<a href="#" onclick="sfWebDebugToggle(\'sub_main_'.$id.'\'); document.getElementById(\'main_'.$id.'\').style.border = \'none\'; return false;">'.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/close.png', array('alt' => 'close')).'</a>&nbsp;</div>
1043
        <div style="padding: 2px; display: none" id="sub_main_info_'.$id.'">
1044
        [uri]&nbsp;'.htmlspecialchars($event['uri'], ENT_QUOTES, sfConfig::get('sf_charset')).'<br />
1045
        [key&nbsp;for&nbsp;cache]&nbsp;'.htmlspecialchars($cacheKey, ENT_QUOTES, sfConfig::get('sf_charset')).'<br />
1046
        [life&nbsp;time]&nbsp;'.$this->getLifeTime($event['uri']).'&nbsp;seconds<br />
1047
        [last&nbsp;modified]&nbsp;'.(time() - $lastModified).'&nbsp;seconds<br />
1048
        &nbsp;<br />&nbsp;
1049
        </div>
1050
      </div><div>
1051
      '.$content.'
1052
      </div></div>
1053
    ';
1054
  }
1055
}