Issues (910)

framework/behaviors/CacheableWidgetBehavior.php (2 issues)

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\behaviors;
9
10
use yii\base\Behavior;
11
use yii\base\InvalidConfigException;
12
use yii\base\Widget;
13
use yii\base\WidgetEvent;
14
use yii\caching\CacheInterface;
15
use yii\caching\Dependency;
16
use yii\di\Instance;
17
18
/**
19
 * Cacheable widget behavior automatically caches widget contents according to duration and dependencies specified.
20
 *
21
 * The behavior may be used without any configuration if an application has `cache` component configured.
22
 * By default the widget will be cached for one minute.
23
 *
24
 * The following example will cache the posts widget for an indefinite duration until any post is modified.
25
 *
26
 * ```php
27
 * use yii\behaviors\CacheableWidgetBehavior;
28
 *
29
 * public function behaviors()
30
 * {
31
 *     return [
32
 *         [
33
 *             'class' => CacheableWidgetBehavior::class,
34
 *             'cacheDuration' => 0,
35
 *             'cacheDependency' => [
36
 *                 'class' => 'yii\caching\DbDependency',
37
 *                 'sql' => 'SELECT MAX(updated_at) FROM posts',
38
 *             ],
39
 *         ],
40
 *     ];
41
 * }
42
 * ```
43
 *
44
 * @author Nikolay Oleynikov <[email protected]>
45
 * @since 2.0.14
46
 */
47
class CacheableWidgetBehavior extends Behavior
48
{
49
    /**
50
     * @var CacheInterface|string|array a cache object or a cache component ID
51
     * or a configuration array for creating a cache object.
52
     * Defaults to the `cache` application component.
53
     */
54
    public $cache = 'cache';
55
    /**
56
     * @var int cache duration in seconds.
57
     * Set to `0` to indicate that the cached data will never expire.
58
     * Defaults to 60 seconds or 1 minute.
59
     */
60
    public $cacheDuration = 60;
61
    /**
62
     * @var Dependency|array|null a cache dependency or a configuration array
63
     * for creating a cache dependency or `null` meaning no cache dependency.
64
     *
65
     * For example,
66
     *
67
     * ```php
68
     * [
69
     *     'class' => 'yii\caching\DbDependency',
70
     *     'sql' => 'SELECT MAX(updated_at) FROM posts',
71
     * ]
72
     * ```
73
     *
74
     * would make the widget cache depend on the last modified time of all posts.
75
     * If any post has its modification time changed, the cached content would be invalidated.
76
     */
77
    public $cacheDependency;
78
    /**
79
     * @var string[]|string an array of strings or a single string which would cause
80
     * the variation of the content being cached (e.g. an application language, a GET parameter).
81
     *
82
     * The following variation setting will cause the content to be cached in different versions
83
     * according to the current application language:
84
     *
85
     * ```php
86
     * [
87
     *     Yii::$app->language,
88
     * ]
89
     * ```
90
     */
91
    public $cacheKeyVariations = [];
92
    /**
93
     * @var bool whether to enable caching or not. Allows to turn the widget caching
94
     * on and off according to specific conditions.
95
     * The following configuration will disable caching when a special GET parameter is passed:
96
     *
97
     * ```php
98
     * empty(Yii::$app->request->get('disable-caching'))
99
     * ```
100
     */
101
    public $cacheEnabled = true;
102
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 3
    public function attach($owner)
108
    {
109 3
        parent::attach($owner);
110
111 3
        $this->initializeEventHandlers();
112
    }
113
114
    /**
115
     * Begins fragment caching. Prevents owner widget from execution
116
     * if its contents can be retrieved from the cache.
117
     *
118
     * @param WidgetEvent $event `Widget::EVENT_BEFORE_RUN` event.
119
     */
120 3
    public function beforeRun($event)
121
    {
122 3
        $cacheKey = $this->getCacheKey();
123 3
        $fragmentCacheConfiguration = $this->getFragmentCacheConfiguration();
124
125 3
        if (!$this->owner->view->beginCache($cacheKey, $fragmentCacheConfiguration)) {
0 ignored issues
show
Bug Best Practice introduced by
The property view does not exist on yii\base\Component. Since you implemented __get, consider adding a @property annotation.
Loading history...
126 2
            $event->isValid = false;
127
        }
128
    }
129
130
    /**
131
     * Outputs widget contents and ends fragment caching.
132
     *
133
     * @param WidgetEvent $event `Widget::EVENT_AFTER_RUN` event.
134
     */
135 3
    public function afterRun($event)
136
    {
137 3
        echo $event->result;
138 3
        $event->result = null;
139
140 3
        $this->owner->view->endCache();
141
    }
142
143
    /**
144
     * Initializes widget event handlers.
145
     */
146 3
    private function initializeEventHandlers()
147
    {
148 3
        $this->owner->on(Widget::EVENT_BEFORE_RUN, [$this, 'beforeRun']);
0 ignored issues
show
The method on() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

148
        $this->owner->/** @scrutinizer ignore-call */ 
149
                      on(Widget::EVENT_BEFORE_RUN, [$this, 'beforeRun']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
149 3
        $this->owner->on(Widget::EVENT_AFTER_RUN, [$this, 'afterRun']);
150
    }
151
152
    /**
153
     * Returns the cache instance.
154
     *
155
     * @return CacheInterface cache instance.
156
     * @throws InvalidConfigException if cache instance instantiation fails.
157
     */
158 3
    private function getCacheInstance()
159
    {
160 3
        $cacheInterface = 'yii\caching\CacheInterface';
161 3
        return Instance::ensure($this->cache, $cacheInterface);
162
    }
163
164
    /**
165
     * Returns the widget cache key.
166
     *
167
     * @return string[] an array of strings representing the cache key.
168
     */
169 3
    private function getCacheKey()
170
    {
171
        // `$cacheKeyVariations` may be a `string` and needs to be cast to an `array`.
172 3
        $cacheKey = array_merge(
173 3
            (array)get_class($this->owner),
174 3
            (array)$this->cacheKeyVariations
175 3
        );
176
177 3
        return $cacheKey;
178
    }
179
180
    /**
181
     * Returns a fragment cache widget configuration array.
182
     *
183
     * @return array a fragment cache widget configuration array.
184
     */
185 3
    private function getFragmentCacheConfiguration()
186
    {
187 3
        $cache = $this->getCacheInstance();
188 3
        $fragmentCacheConfiguration = [
189 3
            'cache' => $cache,
190 3
            'duration' => $this->cacheDuration,
191 3
            'dependency' => $this->cacheDependency,
192 3
            'enabled' => $this->cacheEnabled,
193 3
        ];
194
195 3
        return $fragmentCacheConfiguration;
196
    }
197
}
198