Issues (910)

framework/widgets/FragmentCache.php (1 issue)

1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\widgets;
9
10
use Yii;
11
use yii\base\DynamicContentAwareInterface;
12
use yii\base\DynamicContentAwareTrait;
13
use yii\base\Widget;
14
use yii\caching\CacheInterface;
15
use yii\caching\Dependency;
16
use yii\di\Instance;
17
18
/**
19
 * FragmentCache is used by [[\yii\base\View]] to provide caching of page fragments.
20
 *
21
 * @property-read string|false $cachedContent The cached content. False is returned if valid content is not
22
 * found in the cache.
23
 *
24
 * @author Qiang Xue <[email protected]>
25
 * @since 2.0
26
 */
27
class FragmentCache extends Widget implements DynamicContentAwareInterface
28
{
29
    use DynamicContentAwareTrait;
30
31
    /**
32
     * @var CacheInterface|array|string the cache object or the application component ID of the cache object.
33
     * After the FragmentCache object is created, if you want to change this property,
34
     * you should only assign it with a cache object.
35
     * Starting from version 2.0.2, this can also be a configuration array for creating the object.
36
     */
37
    public $cache = 'cache';
38
    /**
39
     * @var int number of seconds that the data can remain valid in cache.
40
     * Use 0 to indicate that the cached data will never expire.
41
     */
42
    public $duration = 60;
43
    /**
44
     * @var array|Dependency the dependency that the cached content depends on.
45
     * This can be either a [[Dependency]] object or a configuration array for creating the dependency object.
46
     * For example,
47
     *
48
     * ```php
49
     * [
50
     *     'class' => 'yii\caching\DbDependency',
51
     *     'sql' => 'SELECT MAX(updated_at) FROM post',
52
     * ]
53
     * ```
54
     *
55
     * would make the output cache depends on the last modified time of all posts.
56
     * If any post has its modification time changed, the cached content would be invalidated.
57
     */
58
    public $dependency;
59
    /**
60
     * @var string[]|string list of factors that would cause the variation of the content being cached.
61
     * Each factor is a string representing a variation (e.g. the language, a GET parameter).
62
     * The following variation setting will cause the content to be cached in different versions
63
     * according to the current application language:
64
     *
65
     * ```php
66
     * [
67
     *     Yii::$app->language,
68
     * ]
69
     * ```
70
     */
71
    public $variations;
72
    /**
73
     * @var bool whether to enable the fragment cache. You may use this property to turn on and off
74
     * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests).
75
     */
76
    public $enabled = true;
77
78
79
    /**
80
     * Initializes the FragmentCache object.
81
     */
82 10
    public function init()
83
    {
84 10
        parent::init();
85
86 10
        $this->cache = $this->enabled ? Instance::ensure($this->cache, 'yii\caching\CacheInterface') : null;
87
88 10
        if ($this->cache instanceof CacheInterface && $this->getCachedContent() === false) {
89 9
            $this->getView()->pushDynamicContent($this);
90 9
            ob_start();
91 9
            ob_implicit_flush(false);
92
        }
93
    }
94
95
    /**
96
     * Marks the end of content to be cached.
97
     * Content displayed before this method call and after [[init()]]
98
     * will be captured and saved in cache.
99
     * This method does nothing if valid content is already found in cache.
100
     */
101 10
    public function run()
102
    {
103 10
        if (($content = $this->getCachedContent()) !== false) {
104 7
            echo $content;
105 10
        } elseif ($this->cache instanceof CacheInterface) {
106 9
            $this->getView()->popDynamicContent();
107
108 9
            $content = ob_get_clean();
109 9
            if ($content === false || $content === '') {
110
                return;
111
            }
112 9
            if (is_array($this->dependency)) {
113
                $this->dependency = Yii::createObject($this->dependency);
114
            }
115 9
            $data = [$content, $this->getDynamicPlaceholders()];
116 9
            $this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency);
117 9
            echo $this->updateDynamicContent($content, $this->getDynamicPlaceholders());
118
        }
119
    }
120
121
    /**
122
     * @var string|bool the cached content. False if the content is not cached.
123
     */
124
    private $_content;
125
126
    /**
127
     * Returns the cached content if available.
128
     * @return string|false the cached content. False is returned if valid content is not found in the cache.
129
     */
130 10
    public function getCachedContent()
131
    {
132 10
        if ($this->_content !== null) {
133 10
            return $this->_content;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_content also could return the type boolean which is incompatible with the documented return type false|string.
Loading history...
134
        }
135
136 10
        $this->_content = false;
137
138 10
        if (!($this->cache instanceof CacheInterface)) {
139 2
            return $this->_content;
140
        }
141
142 9
        $key = $this->calculateKey();
143 9
        $data = $this->cache->get($key);
144 9
        if (!is_array($data) || count($data) !== 2) {
145 9
            return $this->_content;
146
        }
147
148 7
        list($this->_content, $placeholders) = $data;
149 7
        if (!is_array($placeholders) || count($placeholders) === 0) {
150 3
            return $this->_content;
151
        }
152
153 4
        $this->_content = $this->updateDynamicContent($this->_content, $placeholders, true);
154 4
        return $this->_content;
155
    }
156
157
    /**
158
     * Generates a unique key used for storing the content in cache.
159
     * The key generated depends on both [[id]] and [[variations]].
160
     * @return mixed a valid cache key
161
     */
162 9
    protected function calculateKey()
163
    {
164 9
        return array_merge([__CLASS__, $this->getId()], (array)$this->variations);
165
    }
166
}
167